Skip to content

Commit 0fd169a

Browse files
committed
fix(web): keep the iOS app icon readable by Xcode 26 actool
Icon Composer 2.0, which ships with Xcode 27, writes two keys: a top-level `features: ["refractivity"]` array and a per-group `refractivity` object. actool in Xcode 26 can read neither, so archiving dies with `Could not open "AppIcon.icon"`, a nil-object exception and `ARCHIVE FAILED`. The preview iOS job runs on macos-26-arm64, whose newest Xcode is 26.6, so every iOS preview release has failed since the Liquid Glass icon landed in 20dc684. A local Xcode 27 build succeeds, which is why the icon shipped without anyone seeing this. Drop both keys so the document keeps the shape Icon Composer 1.5 produces, and flip the governance assertion into a guard that fails locally if either key returns. Record the constraint and its removal condition, runner images carrying Xcode 27, in the asset ledger and the Tauri mobile spec. Verified: node scripts/testing/run-test-owner.mjs governance (123 tests) and pnpm run check:rules both pass.
1 parent 935915b commit 0fd169a

4 files changed

Lines changed: 22 additions & 13 deletions

File tree

‎.trellis/spec/web/frontend/tauri-mobile-integration.md‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,16 @@ byte for byte.
192192
tighter shoulder reads as a kink once the 28 px offset is applied to it).
193193
- `icon.json` groups are ordered top-first in Icon Composer 27.0 (`groups[0]` is the top layer),
194194
which is the reverse of the published 2.0 documentation. The `@` keeps its own group with
195-
`glass`, `specular`, `translucency`, `blur-material`, `refractivity`, and `lighting: individual`;
196-
the wordmark group stays glass-free because `glass` adds an ~8 px rim at its boundaries.
195+
`glass`, `specular`, `translucency`, `blur-material`, and `lighting: individual`; the wordmark
196+
group stays glass-free because `glass` adds an ~8 px rim at its boundaries.
197+
- Keep `icon.json` in the shape Icon Composer 1.5 can produce: no top-level `features` array and no
198+
per-group `refractivity` object. Icon Composer 2.0 (Xcode 27) writes both, and `actool` in Xcode 26
199+
dies on them while archiving: first `Could not open "AppIcon.icon"`, then a nil-object exception that
200+
fails `ARCHIVE FAILED`. The preview iOS job runs on `macos-26-arm64`, whose newest Xcode is 26.6, so
201+
either key blocks every iOS preview release, while a local Xcode 27 build succeeds and hides the
202+
problem until CI. Restore the keys only once those runner images ship Xcode 27.
203+
`tests/tauri-build-configuration.test.js` asserts their absence, so a reintroduced key fails the
204+
local check instead of an 8-minute macOS CI job.
197205
- `ictool` ignores the `glass` flag, so glass behaviour is only verifiable on a simulator or device.
198206
- Desktop `src-tauri/icons/*`, `icon.icns`, `icon.ico`, and `android-background.png` must stay untouched by
199207
an iOS-layer-only change. They do follow the shared raster sources when a geometry change regenerates them:

‎apps/web/src-tauri/icon-sources/ios-liquid-glass/AppIcon.icon/icon.json‎

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
{
2-
"features" : [
3-
"refractivity"
4-
],
52
"fill-specializations" : [
63
{
74
"value" : {
@@ -40,11 +37,6 @@
4037
],
4138
"lighting" : "individual",
4239
"name" : "At",
43-
"refractivity" : {
44-
"depth" : 0.5,
45-
"enabled" : true,
46-
"strength" : 0.8
47-
},
4840
"shadow" : {
4941
"kind" : "layer-color",
5042
"opacity" : 0.5

‎docs/governance/assets.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,14 @@ iOS 26 及以上改用 Icon Composer 文档 `apps/web/src-tauri/icon-sources/ios
8383

8484
字面层在这之后还有一步修正。`@` 自己的黑边画在**最底层**(描边层),而字面白在它上面,所以凡是字面横跨 `@` 黑边的地方(`m` 右腿压住 `@` 环顶部、`s` 压住 `@` 右下),黑边都会被白盖掉,`@` 看起来直接贴着字面。栅格化因此在字面层上再减去 `@` 外围 6 px 到 20 px 的一条环带,把黑边让回来;内圈 6 px 以内保留,因为 9 px 白衬线只有 4.5 px 露在外面。这条环带一定落在黑框内部(脚本会断言,否则报错退出),所以不会在字面上开出透底的空洞。
8585

86-
图层分组与材质:`@` 单独成组(Liquid Glass 的材质参数是组级属性),开启 `glass`,并配 `specular`、`translucency`、`blur-material`、`refractivity` 和 `lighting: individual`,阴影浅色用 `layer-color`、暗色用 `neutral`,让红色 `@` 读起来像玻璃珠(`@` 的独立黑边来自描边层,与这层玻璃互不干扰)。字面与描边所在的 `Mark` 组保持无 `glass`:带 `glass: true` 时系统会在每层边界叠加约 8 px 宽的边缘高光,白色字面上不可见,但会在描边和 `@` 边界上留下偏粉的亮带。`ictool` 导出时忽略该标志,所以分组的实际效果只能在真机或模拟器构建上验证。处理未引入第三方图像。
86+
图层分组与材质:`@` 单独成组(Liquid Glass 的材质参数是组级属性),开启 `glass`,并配 `specular`、`translucency`、`blur-material` 和 `lighting: individual`,阴影浅色用 `layer-color`、暗色用 `neutral`,让红色 `@` 读起来像玻璃珠(`@` 的独立黑边来自描边层,与这层玻璃互不干扰)。字面与描边所在的 `Mark` 组保持无 `glass`:带 `glass: true` 时系统会在每层边界叠加约 8 px 宽的边缘高光,白色字面上不可见,但会在描边和 `@` 边界上留下偏粉的亮带。`ictool` 导出时忽略该标志,所以分组的实际效果只能在真机或模拟器构建上验证。处理未引入第三方图像。
87+
88+
`icon.json` 保持在 Icon Composer 1.5 能生成的形态:不含顶层 `features` 数组,组上也不含 `refractivity` 对象。这两个键由 Icon Composer 2.0(随 Xcode 27 发布)写入,Xcode 26 的 `actool` 读不了,归档时会先报 `Could not open "AppIcon.icon"`、再抛一个 nil 对象异常并中断,而预览版 iOS 任务所用的 runner 镜像最高只有 Xcode 26.6,留着任一键都会让每次 iOS 预览发布失败。等 runner 镜像带上 Xcode 27 再恢复;`tests/tauri-build-configuration.test.js` 断言这两个键缺席,回退时会在本地检查就先暴露,不必等 CI 跑到 macOS 任务。
8789

8890
| 仓库路径 | 字节数 | SHA-256 |
8991
| ----------------------------------------------------------------------------------- | ------ | ------------------------------------------------------------------ |
9092
| `apps/web/src-tauri/icon-sources/app-icon.svg` | 50929 | `02443e9840d1b08853505b5666cc115d1cfaf3f6d753751c613d5bdb96c1226f` |
91-
| `apps/web/src-tauri/icon-sources/ios-liquid-glass/AppIcon.icon/icon.json` | 2490 | `aa9e7fc3a5cc17b27b99c78a8d3c187d741af9d4819de6027842c3bf5a419631` |
93+
| `apps/web/src-tauri/icon-sources/ios-liquid-glass/AppIcon.icon/icon.json` | 2341 | `fb8725278154307920d0e980b945f0192f1569f3a8360f4f7fd531c9260f3f9e` |
9294
| `apps/web/src-tauri/icon-sources/ios-liquid-glass/AppIcon.icon/Assets/Outline.png` | 8174 | `af3bb8109d7064878302a05345b50045842c22979c9bf267fd709541153cc4aa` |
9395
| `apps/web/src-tauri/icon-sources/ios-liquid-glass/AppIcon.icon/Assets/Wordmark.png` | 12185 | `f4e6b52023433ef141d1d1a45e43fbdc86c612522ee831924663749192059fc8` |
9496
| `apps/web/src-tauri/icon-sources/ios-liquid-glass/AppIcon.icon/Assets/At.png` | 8939 | `1e49386990e4ca42da78baad5c277ec7e11734c2685dbdd48f842f3f24ffb2a3` |

‎tests/tauri-build-configuration.test.js‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,14 @@ test("iOS Liquid Glass icon ships a layered Icon Composer document", async () =>
336336
assert.equal(at.specular, true);
337337
assert.equal(at.lighting, "individual");
338338
assert.ok(at.translucency.enabled);
339-
assert.ok(at.refractivity.enabled);
339+
// Icon Composer 2.0, which ships with Xcode 27, writes two keys that actool
340+
// in Xcode 26 cannot read: the top-level "features" array and a per-group
341+
// "refractivity" object. Archiving then fails with `Could not open
342+
// "AppIcon.icon"` and a nil-object exception, and the preview iOS job runs on
343+
// a runner image whose newest Xcode is 26.6, so either key blocks every iOS
344+
// preview release. Restore both once those images ship Xcode 27.
345+
assert.ok(!Object.hasOwn(metadata, "features"));
346+
assert.ok(!Object.hasOwn(at, "refractivity"));
340347

341348
for (const layer of layers) {
342349
assert.ok(

0 commit comments

Comments
 (0)