Skip to content

Emit SF Symbols 6 templates so the widget catalog builds on Xcode 16.4 - #338

Merged
Ryanmello07 merged 2 commits into
urnetwork:mainfrom
Ryanmello07:fix/widget-symbols-xcode16
Sep 3, 2026
Merged

Emit SF Symbols 6 templates so the widget catalog builds on Xcode 16.4#338
Ryanmello07 merged 2 commits into
urnetwork:mainfrom
Ryanmello07:fix/widget-symbols-xcode16

Conversation

@Ryanmello07

Copy link
Copy Markdown
Contributor

main has been red since 2026-09-02. This fixes the error CI actually hits. It does not turn the pipeline green on its own — see "What this does not fix" below.

The failure

apple/app/widgets/Assets.xcassets: error: Template format 7.0 is newer than
the version that this software supports (6.0)
  → ur.symbols.connector.fill, ur.symbols.connector
** BUILD FAILED **   (CompileAssetCatalogVariant, target URnetworkWidgets)

Why it started with a docs-only commit

d826f34 ("update") changed only PARITY.md and QUICKCONNECT.md, so it looks like an impossible cause. It isn't the cause. Its parent 315d6bb2 ("add widgets", 44 files) created the URnetworkWidgets target and both symbol sets, and never ran CI: the two commits were pushed together, and Actions builds only a push's head commit. d826f34 is simply the first run that compiled its parent's new catalog. app/widgets/ returns 404 at the last green commit.

The runner image (macos-15-arm64, 20260727.0256.1) and the Xcode pin (16.4, 16F6) are byte-identical between the last green run and the red ones. Nothing outside the repo changed.

The fix

gen_connector_symbols.py hardcoded Template v.7.0 and Requires Xcode 26 or greater, and emitted SF Symbols 7-only style properties. This fixes the generator and re-runs it, rather than hand-editing the SVGs — otherwise the next regeneration reintroduces the bug.

Removed, all four inert for a single static layer with no variable value and no Draw animation:

Property Why v7-only
-sfsymbols-motion-group assigns a layer to a Draw motion group
-sfsymbols-draw-reverses-motion-groups same feature
-sfsymbols-layer-tags opaque per-layer token written by v7 exports
-sfsymbols-variable-value-mode selects colour-vs-draw for variable value

The Xcode pin is left alone — its comment documents it as load-bearing for the gomobile iossimulator/arm64 slice.

Evidence

Path geometry is untouched — SHA of all d="…" data is identical before and after.

No layers were lost. Compiled both catalogs and diffed assetutil --info: identical rendition names and counts (4 Vector Glyphs + 9 Image previews per symbol), each glyph ~123 bytes smaller — that is metadata leaving, not a layer. Rasterised output is pixel-identical.

The version banner is an upper-bound gate, not a stricter schema. Verified empirically rather than assumed: on an otherwise byte-identical file, banners v1.0–v7.0 compile clean and v8.0/v9.0 fail with the same Corrupt Content message shape, the toolchain substituting its own maximum. So a file declaring 6.0 passes a toolchain whose ceiling is 6.0.

Honest limitation: this was not validated against Xcode 16.4. No 16.4 on the machine that produced it — only Xcode 26 beta, whose actool accepts 6.0 and 7.0 alike and therefore compiles the broken catalog cleanly too. Every check above is structural. CI is the first real test.

What this does not fix

The iOS device build and the simulator tests should now pass. The "Build for macOS (unsigned)" step will then fail on a separate, pre-existing problem in the same commit, which the symbol error was masking (actool aborts at the first failure):

  • app/network/Shared/Widgets/WidgetRefresh.swift:28 references ControlCenter.shared.reloadControls, and app/widgets/Control/QuickConnectControl.swift is a ControlWidget. On macOS these begin at macOS 26, and Xcode 16.4 ships the macOS 15.5 SDK. The #available(iOS 18.0, macOS 26.0, *) guard is a runtime check — it does not help when the symbol is absent from the SDK being compiled against, so this fails at compile time.
  • WidgetRefresh.swift is compiled into three targets, so it is not confined to the widget.

That needs a decision from whoever added the feature — #if canImport, an availability-guarded file, or excluding the control from the macOS leg — so it is deliberately not bundled here.

Suggested follow-up

315d6bb2 reached main with zero CI runs because Actions builds only a push's head commit. Routing changes of that size through PRs (or building every commit of a push) would have caught both problems before merge.

🤖 Generated with Claude Code

https://claude.ai/code/session_014vN7zh8WeQYCtirhcA3aWw

Ryan Mello and others added 2 commits September 2, 2026 08:48
…e 16.4

CI has been red since 315d6bb added the widgets target:

  Assets.xcassets: error: Template format 7.0 is newer than the version
  that this software supports (6.0)

at CompileAssetCatalogVariant in URnetworkWidgets. Nothing outside the repo
changed -- the runner image and the Xcode pin (16.4) are the same as the last
green run. 315d6bb simply never ran CI: it was pushed together with its child
d826f34, and Actions builds only a push's HEAD, so the docs-only commit was
the first run that compiled the new catalog.

gen_connector_symbols.py hardcodes a Template v.7.0 banner and emits SF
Symbols 7 style metadata, because it was written on Xcode 26. Fix the
generator rather than the SVGs, so the next regeneration does not reintroduce
it, and re-run it.

The banner drops to v.6.0 ("Requires Xcode 16 or greater"), and all four
-sfsymbols-* properties go, none of which a 6.0 parser knows and none of which
this symbol uses -- it is a single static layer with no variable value and no
Draw animation:

  -sfsymbols-motion-group             SF Symbols 7 Draw animation
  -sfsymbols-draw-reverses-motion-groups   SF Symbols 7 Draw animation
  -sfsymbols-layer-tags               v7 per-layer identity token
  -sfsymbols-variable-value-mode      picks color vs. draw; only a choice in v7

The three layer classes stay (with empty bodies) so each path's class
attribute still binds the monochrome / hierarchical / multicolor layers;
.defaults goes, having carried nothing but two of the properties above.

No geometry changes: every path's d= is byte-identical to the previous
export, and the compiled Assets.car keeps the same rendition names and counts
(four Vector Glyphs per symbol, the four interpolation sources), each glyph
just ~123 bytes smaller where the v7 metadata was.

Not verified by building: this machine has no Xcode 16.4, only Xcode-beta
27.0, whose actool accepts both 6.0 and 7.0 -- so a local compile proves
nothing about CI. The argument here is structural.

The CI Xcode pin is untouched, and both symbols are kept (the outline variant
is currently unreferenced but stays, per QUICKCONNECT.md).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014vN7zh8WeQYCtirhcA3aWw
CI pins Xcode 16.4, whose macOS SDK is 15.5. ControlCenter, ControlWidget,
ControlWidgetConfiguration, ControlWidgetToggle, StaticControlConfiguration
and ControlValueProvider are all annotated @available(iOS 18.0, macOS 26.0,
watchOS 26.0, *), so on that SDK the declarations do not exist at all and
"Build for macOS (unsigned)" fails with

    error: 'ControlCenter' is unavailable in macOS

The existing #available(iOS 18.0, macOS 26.0, *) guards cannot fix this:
they are runtime checks, and the compiler still needs the declaration to be
present in the SDK it compiles against. Only a compile-time platform test
can keep the macOS leg from referring to symbols its SDK lacks, so guard the
three sites with #if os(iOS). The @available guards stay exactly as they
were, because they are still the correct runtime check on iOS.

  - WidgetRefresh.reloadControl(): body wrapped, not deleted. The method is
    called from reloadAll(), which the app, the packet tunnel extension and
    the widget extension all call, and all three build for macOS. It stays a
    no-op there rather than a missing method.
  - QuickConnectControl: the whole file, since every type it names is macOS 26.
  - URnetworkWidgetBundle: the control is dropped from the macOS bundle,
    which stays valid with the three Home Screen widgets. Info.plist declares
    no control-specific keys.

The Control Center / Lock Screen / Action button toggle is iOS-only in
practice and could never have compiled for macOS against this SDK, so
nothing that works today is removed. Every line compiled for iOS is
byte-for-byte unchanged: the diff is 28 added lines, all preprocessor
directives and comments, with no existing line modified.

Audited the rest of the widget, app and tunnel sources against the SDK's
own availability annotations for other post-15.5 symbols that the first
error could have masked; the Control family above is the complete set.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014vN7zh8WeQYCtirhcA3aWw
@Ryanmello07
Ryanmello07 merged commit 43d9ab1 into urnetwork:main Sep 3, 2026
1 check passed
Ryanmello07 added a commit that referenced this pull request Sep 9, 2026
CI has been red on main since 2026-09-03. Every run fails in the same
place, before any artwork is read:

    error: Template format 7.0 is newer than the version that this
    software supports (6.0)
    app/network/Assets.xcassets:./ur.symbols.connector.fill.symbolset:
    The SVG file provided for the symbol image set is not suitable

The build dies there, so three steps never run at all: the extension size
budget, the whole macOS leg of a check named "iOS + macOS", and the unit
tests. Inside the iOS step the app target is never compiled either -- its
Swift task is scheduled and cancelled milliseconds later -- so main has
had no compile coverage of the app, and no test coverage at all, for six
days and roughly 25k lines.

This is a regression of a fix that already landed. #338 downgraded the
symbols to Template v.6.0 and added gen_connector_symbols.py, whose
docstring states the constraint: CI pins Xcode 16.4 and its actool
rejects a newer template outright. That fix generated only the widgets
catalog. A later SF Symbols 7 export landed a fresh copy of the same
symbol in app/network, which nothing here owned, so the constraint
silently did not apply to it.

Regenerate that copy from the same script. The diff is the version stamp
and the SF Symbols 7 style attributes -- motion groups and layer tags,
which the pinned toolchain cannot read anyway; not one path element
changes. Verified by generating into a temp directory and diffing: the
script reproduces the committed widgets catalog byte for byte, so its
output is exactly what has been building in CI all along.

Also record in the docstring that TWO catalogs carry these symbols and
that both are subject to the 6.0 rule, with the commands for each.
Generating only one of them is precisely how the constraint was lost.


Claude-Session: https://claude.ai/code/session_01AWV4MaF9JBcQppSX9UxATa

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant