@mmerterden/multi-agent-pipeline 20.8.0 → 20.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +48 -0
- package/docs/facts.json +3 -3
- package/index.js +1 -0
- package/install/_codex-agents.mjs +1 -1
- package/install/_common.mjs +486 -53
- package/install/_mcp-register.mjs +173 -117
- package/install/claude.mjs +281 -220
- package/install/codex.mjs +7 -7
- package/install/copilot.mjs +13 -11
- package/install/index.mjs +92 -27
- package/install/templates/claude-hooks.json +9 -9
- package/manifest.json +112 -114
- package/package.json +5 -2
- package/pipeline/commands/multi-agent/update/SKILL.md +28 -17
- package/pipeline/lib/confusables.json +79 -33
- package/pipeline/lib/extract-conventions.sh +3 -3
- package/pipeline/lib/json-file-lock.mjs +27 -7
- package/pipeline/lib/normalize-text.mjs +86 -17
- package/pipeline/lib/outbound-gate.mjs +13 -4
- package/pipeline/lib/redact.mjs +87 -13
- package/pipeline/multi-agent-refs/analysis/evidence.md +1 -1
- package/pipeline/multi-agent-refs/analysis/synthesis.md +1 -1
- package/pipeline/multi-agent-refs/component-dispatch.md +1 -1
- package/pipeline/multi-agent-refs/conventions-defaults.md +1 -1
- package/pipeline/multi-agent-refs/features/unattended-security.md +2 -2
- package/pipeline/scripts/agent-guard.py +150 -25
- package/pipeline/scripts/audit-log.sh +3 -4
- package/pipeline/scripts/autopilot-runner.mjs +14 -5
- package/pipeline/scripts/doctor.mjs +8 -2
- package/pipeline/scripts/gen-skills-index.mjs +13 -1
- package/pipeline/scripts/log-metric.sh +9 -3
- package/pipeline/scripts/migrate-prefs.mjs +18 -4
- package/pipeline/scripts/pre-commit-check.sh +119 -27
- package/pipeline/scripts/scan-agent-config.sh +9 -9
- package/pipeline/scripts/unattended_policy.py +12 -3
- package/pipeline/scripts/uninstall.mjs +88 -1
- package/pipeline/scripts/usage-identity.mjs +1 -1
- package/pipeline/scripts/usage-register.mjs +1 -1
- package/pipeline/skills/.skill-manifest.json +30 -30
- package/pipeline/skills/shared/README.md +64 -64
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +2 -2
- package/pipeline/skills/shared/external/alarmkit/evals/evals.json +2 -2
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +6 -0
- package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +3 -0
- package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +3 -0
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +3 -4
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +5 -3
- package/pipeline/skills/shared/external/authentication/SKILL.md +29 -17
- package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +3 -1
- package/pipeline/skills/shared/external/background-processing/SKILL.md +10 -8
- package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +7 -7
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +6 -3
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +43 -0
- package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +4 -2
- package/pipeline/skills/shared/external/core-data/SKILL.md +12 -2
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +31 -0
- package/pipeline/skills/shared/external/coreml/SKILL.md +1 -1
- package/pipeline/skills/shared/external/cryptokit/SKILL.md +1 -1
- package/pipeline/skills/shared/external/device-integrity/SKILL.md +11 -5
- package/pipeline/skills/shared/external/ios-coding-standard/references/rules.yml +2 -2
- package/pipeline/skills/shared/external/ios-module-structure/modules/_TEMPLATE.yml +1 -1
- package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +6 -6
- package/pipeline/skills/shared/external/localization-reuse-map/example-mapping.json +3 -2
- package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +20 -18
- package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +4 -4
- package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +12 -9
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/_shared.py +201 -0
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +64 -30
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +14 -16
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +20 -12
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +7 -7
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +26 -12
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-key-shots.py +4 -3
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +51 -24
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +19 -9
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +26 -19
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +39 -46
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +37 -63
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +4 -2
- package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +5 -4
- package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +3 -2
- package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +2 -2
- package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +1 -1
- package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +4 -4
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +15 -6
- package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +2 -1
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +8 -4
- package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +1 -1
- package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +25 -6
- package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +1 -1
- package/pipeline/skills/shared/external/skill-creator/template.md +7 -1
- package/pipeline/skills/shared/external/storekit/references/core-patterns.md +6 -1
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +3 -2
- package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +1 -1
- package/pipeline/skills/shared/external/swift-security/SKILL.md +10 -8
- package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +8 -5
- package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +13 -9
- package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +7 -6
- package/pipeline/skills/shared/external/swift-testing/SKILL.md +8 -5
- package/pipeline/skills/shared/external/swift-testing/evals/evals.json +1 -1
- package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +3 -2
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +5 -3
- package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +17 -9
- package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +4 -2
- package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +13 -6
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +3 -1
- package/pipeline/skills/shared/external/weatherkit/SKILL.md +8 -5
- package/pipeline/skills/shared/external/widgetkit/SKILL.md +15 -7
- package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +8 -6
- package/pipeline/scripts/gen-ref-toc.mjs +0 -279
- package/pipeline/scripts/make-manifest.mjs +0 -199
- package/pipeline/scripts/scorecard-snapshot.mjs +0 -178
|
@@ -18,7 +18,7 @@ and Best Practices".
|
|
|
18
18
|
- [Reading](#reading)
|
|
19
19
|
- [One Place That Builds Queries](#one-place-that-builds-queries)
|
|
20
20
|
- [OSStatus Codes](#osstatus-codes)
|
|
21
|
-
- [An Actor Around the Keychain (iOS
|
|
21
|
+
- [An Actor Around the Keychain (iOS 13+ / macOS 10.15+)](#an-actor-around-the-keychain-ios-13--macos-1015)
|
|
22
22
|
- [Performance](#performance)
|
|
23
23
|
- [macOS: Two Keychains (TN3137)](#macos-two-keychains-tn3137)
|
|
24
24
|
- [Accessibility at a Glance](#accessibility-at-a-glance)
|
|
@@ -85,8 +85,11 @@ Internet passwords are identified by account, server, protocol, authentication
|
|
|
85
85
|
type, port, path, security domain, access group and synchronizable. See
|
|
86
86
|
[keychain-item-classes.md](keychain-item-classes.md) for every class.
|
|
87
87
|
|
|
88
|
-
`SecItemUpdate` cannot change `
|
|
89
|
-
|
|
88
|
+
`SecItemUpdate` cannot change `kSecClass`: the class belongs to the query,
|
|
89
|
+
and the attributes dictionary accepts only real keychain attributes. To change
|
|
90
|
+
class, delete the old item and add a new one. A primary-key attribute such as
|
|
91
|
+
`kSecAttrAccount` can be updated; the update fails with `errSecDuplicateItem`
|
|
92
|
+
only when another item already has the resulting primary key.
|
|
90
93
|
|
|
91
94
|
## Saving: Add, Then Update on Duplicate
|
|
92
95
|
|
|
@@ -257,7 +260,7 @@ its merits:
|
|
|
257
260
|
| -128 | `errSecUserCanceled` | The user dismissed the prompt; tell the UI. |
|
|
258
261
|
| -25293 | `errSecAuthFailed` | Authentication failed or the protected item is no longer usable. |
|
|
259
262
|
| -50 | `errSecParam` | A programming error in the dictionary; fix the query. |
|
|
260
|
-
| -
|
|
263
|
+
| -25303 | `errSecNoSuchAttr` | The attribute is not supported (seen on the data protection keychain). |
|
|
261
264
|
|
|
262
265
|
```swift
|
|
263
266
|
struct KeychainError: Error, CustomStringConvertible {
|
|
@@ -272,7 +275,7 @@ struct KeychainError: Error, CustomStringConvertible {
|
|
|
272
275
|
Logs may record the shape of a query (class, service, which flags were set)
|
|
273
276
|
and the status code. They never record `kSecValueData`, tokens or key bytes.
|
|
274
277
|
|
|
275
|
-
## An Actor Around the Keychain (iOS
|
|
278
|
+
## An Actor Around the Keychain (iOS 13+ / macOS 10.15+)
|
|
276
279
|
|
|
277
280
|
Reads of biometric-protected items wait for the user and can take several
|
|
278
281
|
seconds (WWDC 2014 Session 711). Doing that on the main actor freezes the UI.
|
|
@@ -381,7 +384,8 @@ Why an actor rather than a queue:
|
|
|
381
384
|
| `Sendable` | Enforced | Annotate `@Sendable` by hand |
|
|
382
385
|
| Swift 6 | Native | Needs care to pass strict checking |
|
|
383
386
|
|
|
384
|
-
|
|
387
|
+
In completion-handler code that has not moved to Swift concurrency, a private
|
|
388
|
+
serial queue does the same job:
|
|
385
389
|
|
|
386
390
|
```swift
|
|
387
391
|
final class LegacySecretStore {
|
|
@@ -450,7 +454,7 @@ func macSafe(_ query: [CFString: Any]) -> [CFString: Any] {
|
|
|
450
454
|
|
|
451
455
|
When a macOS keychain bug appears, first confirm which backend the call went
|
|
452
456
|
to. An attribute the file-based keychain quietly drops will produce
|
|
453
|
-
`errSecNoSuchAttr` (-
|
|
457
|
+
`errSecNoSuchAttr` (-25303) on the data protection keychain.
|
|
454
458
|
|
|
455
459
|
## Accessibility at a Glance
|
|
456
460
|
|
|
@@ -477,8 +481,8 @@ is a safe default for foreground use. The actor above defaults to
|
|
|
477
481
|
2. Saves fall back to `SecItemUpdate` on -25299.
|
|
478
482
|
3. Every `SecItemCopyMatching` sets at least one `kSecReturn*` key.
|
|
479
483
|
4. The cast of the `CFTypeRef` result matches the return keys and match limit.
|
|
480
|
-
5. No SecItem call runs on `@MainActor` (actor
|
|
481
|
-
|
|
484
|
+
5. No SecItem call runs on `@MainActor` (an actor, available from iOS 13, or
|
|
485
|
+
a private serial queue in completion-handler code).
|
|
482
486
|
6. Add, query and update dictionaries are built fresh for each call.
|
|
483
487
|
7. Keys are `kSec*` constants, not string literals.
|
|
484
488
|
8. Queries are specific: service and account for generic passwords,
|
|
@@ -321,12 +321,13 @@ func pqRoundTrip() throws -> Bool {
|
|
|
321
321
|
```
|
|
322
322
|
|
|
323
323
|
Transport needs no work: in iOS 26, `URLSession` and `Network.framework`
|
|
324
|
-
negotiate quantum-secure TLS 1.3
|
|
325
|
-
|
|
326
|
-
use it. For an app's own end-to-end encryption,
|
|
327
|
-
post-quantum and classical algorithms, such as the
|
|
328
|
-
|
|
329
|
-
|
|
324
|
+
negotiate quantum-secure TLS 1.3 on their own, offering the hybrid
|
|
325
|
+
`X25519MLKEM768` key-exchange group, and CloudKit, push notifications and
|
|
326
|
+
iCloud Private Relay already use it. For an app's own end-to-end encryption,
|
|
327
|
+
prefer a hybrid of post-quantum and classical algorithms, such as the CryptoKit
|
|
328
|
+
X-Wing KEM (`XWingMLKEM768X25519`) in HPKE; an
|
|
329
|
+
`SecureEnclave.MLKEM768.PrivateKey` performs encapsulation and decapsulation
|
|
330
|
+
inside the hardware. The full CryptoKit catalog is in
|
|
330
331
|
[cryptokit-public-key.md](cryptokit-public-key.md).
|
|
331
332
|
|
|
332
333
|
| Release | Enclave-related change |
|
|
@@ -218,7 +218,7 @@ API you correct.
|
|
|
218
218
|
| API | Needs |
|
|
219
219
|
|-----|-------|
|
|
220
220
|
| Exit tests (`processExitsWith:`) | Swift 6.2, Xcode 26.0; not iOS, tvOS, watchOS |
|
|
221
|
-
| Capture lists in exit tests | Swift 6.3 compiler;
|
|
221
|
+
| Capture lists in exit tests | Compiles with the Swift 6.3.1 compiler (Xcode 26.4); earlier compilers not checked. Captured values must be `Sendable` and `Codable` |
|
|
222
222
|
| `Test.cancel(_:)`, `Issue.record(_:severity:)`, image attachments | Swift 6.3, Xcode 26.4 |
|
|
223
223
|
|
|
224
224
|
```swift
|
|
@@ -230,9 +230,12 @@ API you correct.
|
|
|
230
230
|
}
|
|
231
231
|
```
|
|
232
232
|
|
|
233
|
-
Give each captured value an explicit type (`[name = name as Type]`).
|
|
234
|
-
`[status]` fails
|
|
235
|
-
|
|
233
|
+
Give each captured value an explicit type (`[name = name as Type]`). With the
|
|
234
|
+
Swift 6.3.1 compiler a bare `[status]` fails with "Type of captured value
|
|
235
|
+
'status' is ambiguous", a captured value that is not `Codable` fails with "must
|
|
236
|
+
conform to 'Sendable' and 'Codable'", and a closure that reads `status` with no
|
|
237
|
+
capture list fails with "a C function pointer cannot be formed from a closure
|
|
238
|
+
that captures context".
|
|
236
239
|
|
|
237
240
|
A test that calls `Test.cancel(_:)` must be `throws` or `async throws`.
|
|
238
241
|
|
|
@@ -241,7 +244,7 @@ A test that calls `Test.cancel(_:)` must be `throws` or `async throws`.
|
|
|
241
244
|
| Found | Replace with |
|
|
242
245
|
|-------|--------------|
|
|
243
246
|
| `#expect(exitsWith:)` | `await #expect(processExitsWith: .failure) { ... }`. In an iOS app target, test the logic behind a smaller API that does not exit, or move the exit test to a supported host or tool target. |
|
|
244
|
-
| Exit-test closure reading outer values with no capture list | An explicit, typed capture list (`[code = code as Int32]`); Swift 6.3
|
|
247
|
+
| Exit-test closure reading outer values with no capture list | An explicit, typed capture list (`[code = code as Int32]`); verified with Swift 6.3.1; values `Sendable` and `Codable` |
|
|
245
248
|
| `Test.cancel()` in a test that is not throwing | Mark the test `async throws` and call `try Test.cancel("reason")` |
|
|
246
249
|
| A warning that should not fail the run | `Issue.record("message", severity: .warning)`: reported, test still passes; Swift 6.3, Xcode 26.4 |
|
|
247
250
|
| `Attachment(image, named:).record()` | `Attachment.record(image, named: "...", as: .png)`; accepts `UIImage`, `CGImage`, `CIImage`, `NSImage`; Swift 6.3, Xcode 26.4 |
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"assertions": [
|
|
24
24
|
"Replaces exitsWith: with await #expect(processExitsWith: .failure).",
|
|
25
25
|
"States that exit tests need Swift 6.2 and Xcode 26.0 and do not run on iOS, tvOS or watchOS, and suggests testing the non-exiting logic in the app target.",
|
|
26
|
-
"Adds an explicit capture list for badCode,
|
|
26
|
+
"Adds an explicit, typed capture list for badCode, notes it was verified with the Swift 6.3.1 compiler, and requires Sendable plus Codable values.",
|
|
27
27
|
"Makes the cancelling test async throws (or throws) and calls try Test.cancel.",
|
|
28
28
|
"Uses Issue.record with severity: .warning and names the Swift 6.3 and Xcode 26.4 gate.",
|
|
29
29
|
"Uses Attachment.record(chartImage, named:, as: .png) and names the Swift 6.3 and Xcode 26.4 gate."
|
|
@@ -52,7 +52,8 @@ and reports it as cancelled: neither passed nor failed.
|
|
|
52
52
|
## Capturing Values in Exit Tests
|
|
53
53
|
|
|
54
54
|
ST-0012 lets an exit test's closure capture values from the surrounding scope.
|
|
55
|
-
It
|
|
55
|
+
It compiles with the Swift 6.3.1 compiler in Xcode 26.4; earlier compilers were
|
|
56
|
+
not checked. Exit tests themselves arrived in Swift 6.2
|
|
56
57
|
(Xcode 26.0) through the `processExitsWith:` macros. Captured values must be
|
|
57
58
|
`Sendable` and `Codable`, because they are sent to the child process.
|
|
58
59
|
|
|
@@ -101,7 +102,7 @@ import UIKit
|
|
|
101
102
|
|---------|---------|
|
|
102
103
|
| Attachments of standard values | Swift 6.2, Xcode 26.0 |
|
|
103
104
|
| Exit tests with `processExitsWith:` | Swift 6.2, Xcode 26.0; macOS, Linux, FreeBSD, OpenBSD, Windows |
|
|
104
|
-
| Capture lists in exit tests | Swift 6.3 compiler |
|
|
105
|
+
| Capture lists in exit tests | Verified with the Swift 6.3.1 compiler (Xcode 26.4) |
|
|
105
106
|
| `Issue.record(_:severity:)`, `Test.cancel(_:)`, image attachments | Swift 6.3, Xcode 26.4 |
|
|
106
107
|
|
|
107
108
|
iOS, tvOS and watchOS have no exit tests. Put the exiting behaviour behind a
|
|
@@ -35,9 +35,11 @@ shipped without a migration plan.
|
|
|
35
35
|
|
|
36
36
|
## Defining models
|
|
37
37
|
|
|
38
|
-
`@Model` only applies to classes. It adds conformance to `PersistentModel
|
|
39
|
-
`Observable` and `
|
|
40
|
-
designated initializer.
|
|
38
|
+
`@Model` only applies to classes. It adds conformance to `PersistentModel`
|
|
39
|
+
(which refines `Observable`, `Hashable` and `Identifiable`), so a model needs
|
|
40
|
+
reference semantics and a designated initializer. Model instances are not
|
|
41
|
+
`Sendable`: to hand a model to another actor, pass its `PersistentIdentifier`
|
|
42
|
+
(which is `Sendable`) and fetch it again from that actor's context.
|
|
41
43
|
|
|
42
44
|
```swift
|
|
43
45
|
import SwiftData
|
|
@@ -188,19 +188,24 @@ struct RootTabs: View {
|
|
|
188
188
|
The custom binding sends every change through `select`, so a tab like
|
|
189
189
|
"New" can run an action instead of becoming the selected tab.
|
|
190
190
|
|
|
191
|
-
### iOS 26
|
|
191
|
+
### Tab APIs from iOS 18 and iOS 26
|
|
192
192
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
-
|
|
196
|
-
|
|
193
|
+
From iOS 18 (macOS 15):
|
|
194
|
+
|
|
195
|
+
- `Tab(role: .search)` marks the search tab; the iOS 26 tab bar shows it as a
|
|
196
|
+
search field while that tab is active.
|
|
197
197
|
- `.tabViewSidebarHeader { }` and `.tabViewSidebarFooter { }` customise the
|
|
198
198
|
sidebar on iPadOS and macOS.
|
|
199
|
-
- `.tabViewBottomAccessory { }` pins a view just above the tab bar, such as a
|
|
200
|
-
mini player.
|
|
201
199
|
- `TabSection` collects tabs under a sidebar heading; mark it with
|
|
202
200
|
`.tabPlacement(.sidebarOnly)` to keep it out of the compact bar.
|
|
203
201
|
|
|
202
|
+
From iOS 26 only:
|
|
203
|
+
|
|
204
|
+
- `.tabBarMinimizeBehavior(_:)` with `.onScrollDown`, `.onScrollUp` or `.never`.
|
|
205
|
+
iPhone only.
|
|
206
|
+
- `.tabViewBottomAccessory { }` pins a view just above the tab bar, such as a
|
|
207
|
+
mini player.
|
|
208
|
+
|
|
204
209
|
More in [references/tabview.md](references/tabview.md).
|
|
205
210
|
|
|
206
211
|
## Deep links
|
|
@@ -259,7 +264,10 @@ Details, AASA format and scheme parsing: [references/deeplinks.md](references/de
|
|
|
259
264
|
- Driving a model-backed sheet with `.sheet(isPresented:)` instead of
|
|
260
265
|
`.sheet(item:)`.
|
|
261
266
|
- Putting views in the path. The path holds small `Hashable` route values.
|
|
262
|
-
-
|
|
267
|
+
- Writing `NavigationStack(path: $router.path)` where `router` is a plain `let`
|
|
268
|
+
or comes from `@Environment`. That gives no binding and does not compile;
|
|
269
|
+
declare the property `@Bindable var router: Router`, or add
|
|
270
|
+
`@Bindable var router = router` inside `body`.
|
|
263
271
|
- Building tabs with the older `.tabItem { }` instead of `Tab(value:)` with
|
|
264
272
|
`TabView(selection:)`.
|
|
265
273
|
- Expecting `tabBarMinimizeBehavior` to do anything on iPad. It is iPhone only.
|
|
@@ -285,5 +293,5 @@ Details, AASA format and scheme parsing: [references/deeplinks.md](references/de
|
|
|
285
293
|
|
|
286
294
|
- [references/navigationstack.md](references/navigationstack.md): routers, per-tab stacks, centralised destinations, data-driven tabs
|
|
287
295
|
- [references/sheets.md](references/sheets.md): enum sheet routing, sizing, dismissal confirmation
|
|
288
|
-
- [references/tabview.md](references/tabview.md): tab architecture, custom selection binding, dynamic tabs, iOS 26 tab
|
|
296
|
+
- [references/tabview.md](references/tabview.md): tab architecture, custom selection binding, dynamic tabs, the iOS 18 and iOS 26 tab APIs
|
|
289
297
|
- [references/deeplinks.md](references/deeplinks.md): router URL handling, universal links and AASA, custom schemes, Handoff
|
|
@@ -172,5 +172,7 @@ struct DynamicTabs: View {
|
|
|
172
172
|
- A path shared across tabs, unless you really want one global history.
|
|
173
173
|
- Route identifiers that are not stable or not `Hashable`.
|
|
174
174
|
- View instances stored in the path instead of route data.
|
|
175
|
-
- A router
|
|
176
|
-
|
|
175
|
+
- A router passed as a plain `let` or read from `@Environment` and then used as
|
|
176
|
+
`$router.path`: no binding exists without `@Bindable`, so the stack view needs
|
|
177
|
+
`@Bindable var router: Router` (as `ExploreStack` has) or a local
|
|
178
|
+
`@Bindable var router = router` in `body`.
|
|
@@ -107,7 +107,7 @@ enum AppTab: Hashable, Identifiable {
|
|
|
107
107
|
}
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
## iOS
|
|
110
|
+
## Tab APIs from iOS 18 and iOS 26
|
|
111
111
|
|
|
112
112
|
```swift
|
|
113
113
|
TabView {
|
|
@@ -121,20 +121,27 @@ TabView {
|
|
|
121
121
|
.tabViewSidebarBottomBar { SettingsButton() }
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
- `Tab(role: .search)`
|
|
127
|
-
active.
|
|
124
|
+
iOS 18 (macOS 15, visionOS 2):
|
|
125
|
+
|
|
126
|
+
- `Tab(role: .search)` marks the search tab; the iOS 26 tab bar replaces the bar
|
|
127
|
+
with a search field while that tab is active.
|
|
128
128
|
- Sidebar customisation: `.tabViewSidebarHeader { }`,
|
|
129
129
|
`.tabViewSidebarFooter { }` and `.tabViewSidebarBottomBar { }`.
|
|
130
|
+
- `TabSection` and `.tabPlacement(_:)`.
|
|
131
|
+
|
|
132
|
+
iOS 26 only:
|
|
133
|
+
|
|
134
|
+
- `TabBarMinimizeBehavior`: `.automatic` (decided by context), `.onScrollDown`,
|
|
135
|
+
`.onScrollUp` and `.never`. The two scroll behaviours apply on iPhone only.
|
|
130
136
|
- `.tabViewBottomAccessory { }` shows content just above the tab bar; its
|
|
131
137
|
placement is described by `TabViewBottomAccessoryPlacement`.
|
|
132
138
|
|
|
139
|
+
The example mixes both groups, so as written it needs iOS 26.
|
|
140
|
+
|
|
133
141
|
## Pitfalls
|
|
134
142
|
|
|
135
143
|
- A view model per tab. Keep state local to views or in shared `@Observable`
|
|
136
144
|
services.
|
|
137
|
-
- `@Observable` objects nested inside each other.
|
|
138
145
|
- An `AppTab.id` that changes between launches; dynamic cases must hash on a
|
|
139
146
|
stable identifier.
|
|
140
147
|
- Special tabs such as compose that change the selection.
|
|
@@ -44,7 +44,9 @@ optional.
|
|
|
44
44
|
|
|
45
45
|
Most requests are value types. Requests that must remember earlier frames are
|
|
46
46
|
`final class` types conforming to `StatefulRequest`: `TrackObjectRequest`,
|
|
47
|
-
`TrackRectangleRequest`, `TrackOpticalFlowRequest`,
|
|
47
|
+
`TrackRectangleRequest`, `TrackOpticalFlowRequest`,
|
|
48
|
+
`TrackTranslationalImageRegistrationRequest`,
|
|
49
|
+
`TrackHomographicImageRegistrationRequest`, `DetectTrajectoriesRequest`,
|
|
48
50
|
`DetectHumanBodyPose3DRequest` and `GeneratePersonSegmentationRequest`. Keep one
|
|
49
51
|
instance alive for the whole sequence.
|
|
50
52
|
|
|
@@ -68,8 +68,9 @@ headline = "\(now.temperature.formatted()), \(now.condition.description)"
|
|
|
68
68
|
|
|
69
69
|
### Hourly
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
current hour
|
|
71
|
+
At the time of writing, Apple's documentation says the default hourly forecast
|
|
72
|
+
covers 25 consecutive hours starting with the current hour; read the count from
|
|
73
|
+
the result rather than hard-coding it. `hourlyForecast` is a `Forecast<HourWeather>`:
|
|
73
74
|
|
|
74
75
|
```swift
|
|
75
76
|
let timeline = report.hourlyForecast.map { slot in
|
|
@@ -79,7 +80,9 @@ let timeline = report.hourlyForecast.map { slot in
|
|
|
79
80
|
|
|
80
81
|
### Daily
|
|
81
82
|
|
|
82
|
-
|
|
83
|
+
At the time of writing, Apple's documentation says the default daily forecast
|
|
84
|
+
covers 10 consecutive days starting today; again, read the count from the
|
|
85
|
+
result.
|
|
83
86
|
`dailyForecast` is a `Forecast<DayWeather>`:
|
|
84
87
|
|
|
85
88
|
```swift
|
|
@@ -145,8 +148,8 @@ if let nextHour = try await forecaster.weather(for: site, including: .minute) {
|
|
|
145
148
|
| Query | Result type | Notes |
|
|
146
149
|
| --- | --- | --- |
|
|
147
150
|
| `.current` | `CurrentWeather` | |
|
|
148
|
-
| `.hourly` | `Forecast<HourWeather>` | 25 hours from now |
|
|
149
|
-
| `.daily` | `Forecast<DayWeather>` | 10 days from today |
|
|
151
|
+
| `.hourly` | `Forecast<HourWeather>` | Default span per Apple's docs: 25 hours from now |
|
|
152
|
+
| `.daily` | `Forecast<DayWeather>` | Default span per Apple's docs: 10 days from today |
|
|
150
153
|
| `.minute` | `Forecast<MinuteWeather>?` | `nil` where unsupported |
|
|
151
154
|
| `.alerts` | `[WeatherAlert]?` | `nil` where unsupported |
|
|
152
155
|
| `.availability` | `WeatherAvailability` | Which region-limited datasets exist here |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: widgetkit
|
|
3
|
-
description: "WidgetKit
|
|
3
|
+
description: "WidgetKit surfaces on iOS 26+: timeline-driven widgets for the Home Screen, the Lock Screen and StandBy, interactive widgets using Button or Toggle with AppIntent actions, widget views for a Live Activity (Dynamic Island compact, expanded, minimal), Control Center controls with ControlWidgetButton or ControlWidgetToggle, families, deep links, the refresh budget, push reloads, accented rendering, extension setup, App Groups. Use when implementing or reviewing widgets, Live Activity views or controls. Not for Live Activity lifecycle or pushes (live-activities)."
|
|
4
4
|
metadata:
|
|
5
5
|
source: multi-agent-pipeline
|
|
6
6
|
---
|
|
@@ -13,7 +13,9 @@ surface WidgetKit and ActivityKit draw on: the Home Screen, the Lock Screen,
|
|
|
13
13
|
Live Activities and the Dynamic Island, Control Center and StandBy.
|
|
14
14
|
|
|
15
15
|
Related skills: `app-intents` for intent and entity design beyond what a widget
|
|
16
|
-
needs, `live-activities` for
|
|
16
|
+
needs, `live-activities` for the Live Activity lifecycle (request, update, end),
|
|
17
|
+
push tokens and ActivityKit push contracts. This skill covers the widget views a
|
|
18
|
+
Live Activity renders.
|
|
17
19
|
|
|
18
20
|
Longer examples are in [the advanced reference](references/widgetkit-advanced.md).
|
|
19
21
|
|
|
@@ -300,14 +302,20 @@ on its side. Read `@Environment(\.widgetLocation)` to tell where you are drawn:
|
|
|
300
302
|
- Budget: roughly 40 to 70 refreshes a day, with entries 5 minutes apart or more.
|
|
301
303
|
For countdowns use `Text(timerInterval:countsDown:)` instead of one entry per tick.
|
|
302
304
|
|
|
303
|
-
## iOS
|
|
305
|
+
## iOS 18 Additions
|
|
304
306
|
|
|
305
307
|
- `WidgetAccentedRenderingMode` (`.accented`, `.accentedDesaturated`,
|
|
306
|
-
`.desaturated`, `.fullColor`)
|
|
308
|
+
`.desaturated`, `.fullColor`), applied with `.widgetAccentedRenderingMode(_:)`,
|
|
309
|
+
controls how images adapt to accented (tinted) rendering. Available from
|
|
310
|
+
iOS 18, macOS 15 and watchOS 11; on visionOS from 26.
|
|
311
|
+
- `ControlPushHandler.pushTokensDidChange(controls:)` gives a push token per
|
|
312
|
+
control; an APNs push to it reloads the control.
|
|
313
|
+
|
|
314
|
+
## iOS 26 Additions
|
|
315
|
+
|
|
307
316
|
- `WidgetPushHandler.pushTokenDidChange(_:widgets:)` gives you a token to send to
|
|
308
|
-
your server; an APNs push to it reloads the widget.
|
|
309
|
-
|
|
310
|
-
[Reloading timelines by push](references/widgetkit-advanced.md#reloading-timelines-by-push-ios-26-and-later).
|
|
317
|
+
your server; an APNs push to it reloads the widget. See
|
|
318
|
+
[Reloading timelines by push](references/widgetkit-advanced.md#reloading-timelines-by-push).
|
|
311
319
|
- `.systemSmall` widgets render in CarPlay from iOS 26. Keep them readable at a
|
|
312
320
|
glance so a driver is never pulled into detail.
|
|
313
321
|
|
|
@@ -6,7 +6,7 @@ heading says otherwise.
|
|
|
6
6
|
## Contents
|
|
7
7
|
|
|
8
8
|
- [Timeline Strategies](#timeline-strategies)
|
|
9
|
-
- [Reloading timelines by push
|
|
9
|
+
- [Reloading timelines by push](#reloading-timelines-by-push)
|
|
10
10
|
- [Deep Links and Widget URLs](#deep-links-and-widget-urls)
|
|
11
11
|
- [User-configurable widgets through App Intents](#user-configurable-widgets-through-app-intents)
|
|
12
12
|
- [Multiple Widget Support](#multiple-widget-support)
|
|
@@ -75,9 +75,10 @@ cost budget.
|
|
|
75
75
|
- A widget run from Xcode's debugger has no refresh limit, so budget problems
|
|
76
76
|
only show up outside the debugger.
|
|
77
77
|
|
|
78
|
-
## Reloading timelines by push
|
|
78
|
+
## Reloading timelines by push
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
From iOS 26, a `WidgetPushHandler` receives a token for APNs. Send it to your
|
|
81
|
+
server.
|
|
81
82
|
|
|
82
83
|
```swift
|
|
83
84
|
struct ScoreboardPushHandler: WidgetPushHandler {
|
|
@@ -99,8 +100,9 @@ func hexString(_ bytes: Data) -> String {
|
|
|
99
100
|
When the server sends an APNs push addressed to that token, the system calls
|
|
100
101
|
`getTimeline` or `timeline(for:in:)` again.
|
|
101
102
|
|
|
102
|
-
Controls have their own handler
|
|
103
|
-
`ControlInfo` values; each carries its token in an optional
|
|
103
|
+
Controls have their own handler, available from iOS 18, that receives every
|
|
104
|
+
control at once, as `ControlInfo` values; each carries its token in an optional
|
|
105
|
+
`pushInfo`:
|
|
104
106
|
|
|
105
107
|
```swift
|
|
106
108
|
struct GaragePushHandler: ControlPushHandler {
|
|
@@ -478,7 +480,7 @@ Task {
|
|
|
478
480
|
}
|
|
479
481
|
```
|
|
480
482
|
|
|
481
|
-
From iOS
|
|
483
|
+
From iOS 18 a broadcast channel works too: `pushType: .channel("ferry-route-12")`.
|
|
482
484
|
|
|
483
485
|
Update payload:
|
|
484
486
|
|