@mmerterden/multi-agent-pipeline 20.0.0 → 20.2.0

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.
Files changed (90) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +5 -5
  3. package/README.tr.md +5 -5
  4. package/SECURITY.md +3 -3
  5. package/docs/adr/0011-dormant-ci.md +10 -1
  6. package/docs/architecture.md +2 -2
  7. package/docs/ecosystem.md +5 -5
  8. package/docs/facts.json +8 -7
  9. package/install/_codex-agents.mjs +1 -1
  10. package/manifest.json +92 -64
  11. package/package.json +1 -1
  12. package/pipeline/agents/code-reviewer.md +2 -2
  13. package/pipeline/agents/dev-critic.md +5 -5
  14. package/pipeline/agents/security-auditor.md +80 -72
  15. package/pipeline/commands/figma-to-swiftui.md +1 -1
  16. package/pipeline/commands/multi-agent/SKILL.md +1 -1
  17. package/pipeline/commands/multi-agent/channels/SKILL.md +1 -1
  18. package/pipeline/commands/multi-agent/diff-explain/SKILL.md +1 -1
  19. package/pipeline/commands/multi-agent/help/SKILL.md +2 -0
  20. package/pipeline/commands/multi-agent/scan/SKILL.md +2 -2
  21. package/pipeline/commands/multi-agent/security-review/SKILL.md +52 -0
  22. package/pipeline/commands/multi-agent/sync/SKILL.md +3 -3
  23. package/pipeline/multi-agent-refs/component-dispatch.md +5 -5
  24. package/pipeline/multi-agent-refs/cross-cli-contract.md +6 -6
  25. package/pipeline/multi-agent-refs/features/security-audit.md +55 -0
  26. package/pipeline/multi-agent-refs/phases/modes.md +1 -1
  27. package/pipeline/multi-agent-refs/phases/phase-3-review.md +9 -15
  28. package/pipeline/multi-agent-refs/phases/phase-5-report.md +1 -1
  29. package/pipeline/multi-agent-refs/threat-model.md +39 -0
  30. package/pipeline/schemas/agent-state.schema.json +23 -0
  31. package/pipeline/schemas/phases.json +1 -2
  32. package/pipeline/schemas/prefs.schema.json +0 -4
  33. package/pipeline/schemas/reviewer-output.schema.json +99 -2
  34. package/pipeline/schemas/security-finding.schema.json +144 -0
  35. package/pipeline/scripts/_stack-routing.mjs +1 -0
  36. package/pipeline/scripts/gc-abandoned.sh +16 -9
  37. package/pipeline/scripts/render-work-summary.sh +7 -4
  38. package/pipeline/skills/.skill-manifest.json +47 -23
  39. package/pipeline/skills/.skills-index.json +75 -9
  40. package/pipeline/skills/shared/README.md +13 -7
  41. package/pipeline/skills/shared/core/multi-agent/SKILL.md +3 -4
  42. package/pipeline/skills/shared/core/multi-agent-scan/SKILL.md +2 -2
  43. package/pipeline/skills/shared/core/multi-agent-security-review/SKILL.md +29 -0
  44. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +3 -3
  45. package/pipeline/skills/shared/external/android-architecture/SKILL.md +71 -0
  46. package/pipeline/skills/shared/external/android-architecture/references/patterns.md +142 -0
  47. package/pipeline/skills/shared/external/android-build-quality-gates/SKILL.md +314 -0
  48. package/pipeline/skills/shared/external/android-build-quality-gates/references/patterns.md +432 -0
  49. package/pipeline/skills/shared/external/android-datastore/SKILL.md +236 -0
  50. package/pipeline/skills/shared/external/android-datastore/references/patterns.md +297 -0
  51. package/pipeline/skills/shared/external/android-design-tokens-codegen/SKILL.md +249 -0
  52. package/pipeline/skills/shared/external/android-design-tokens-codegen/references/patterns.md +270 -0
  53. package/pipeline/skills/shared/external/android-jetpack-compose-expert/SKILL.md +62 -0
  54. package/pipeline/skills/shared/external/android-mvi-viewmodel/SKILL.md +255 -0
  55. package/pipeline/skills/shared/external/android-mvi-viewmodel/references/patterns.md +257 -0
  56. package/pipeline/skills/shared/external/android-performance/SKILL.md +86 -602
  57. package/pipeline/skills/shared/external/android-performance/references/patterns.md +659 -0
  58. package/pipeline/skills/shared/external/android-security/SKILL.md +117 -430
  59. package/pipeline/skills/shared/external/android-security/references/patterns.md +690 -0
  60. package/pipeline/skills/shared/external/{android_ui_verification → android-ui-verification}/SKILL.md +1 -1
  61. package/pipeline/skills/shared/external/api-security-best-practices/SKILL.md +35 -733
  62. package/pipeline/skills/shared/external/api-security-best-practices/references/auth.md +299 -0
  63. package/pipeline/skills/shared/external/api-security-best-practices/references/input-validation.md +255 -0
  64. package/pipeline/skills/shared/external/api-security-best-practices/references/rate-limiting.md +167 -0
  65. package/pipeline/skills/shared/external/app-intents/SKILL.md +39 -174
  66. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +178 -0
  67. package/pipeline/skills/shared/external/compose-components/SKILL.md +48 -0
  68. package/pipeline/skills/shared/external/compose-components/references/patterns.md +200 -0
  69. package/pipeline/skills/shared/external/compose-navigation/SKILL.md +66 -3
  70. package/pipeline/skills/shared/external/compose-navigation/references/patterns.md +191 -0
  71. package/pipeline/skills/shared/external/compose-testing/SKILL.md +107 -397
  72. package/pipeline/skills/shared/external/compose-testing/references/patterns.md +631 -0
  73. package/pipeline/skills/shared/external/gradle-kotlin-dsl/SKILL.md +121 -449
  74. package/pipeline/skills/shared/external/gradle-kotlin-dsl/references/patterns.md +715 -0
  75. package/pipeline/skills/shared/external/kotlin-coroutines-expert/SKILL.md +143 -0
  76. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +27 -102
  77. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +42 -0
  78. package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +94 -383
  79. package/pipeline/skills/shared/external/retrofit-networking/references/patterns.md +640 -0
  80. package/pipeline/skills/shared/external/room-database/SKILL.md +101 -440
  81. package/pipeline/skills/shared/external/room-database/references/patterns.md +614 -0
  82. package/pipeline/skills/shared/external/security-review/SKILL.md +64 -0
  83. package/pipeline/skills/shared/external/security-review/references/owasp-mobile-top10-2024.md +53 -0
  84. package/pipeline/skills/shared/external/security-review/references/owasp-web-api-top10-2021.md +56 -0
  85. package/pipeline/skills/shared/external/storekit/SKILL.md +69 -343
  86. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +371 -0
  87. package/pipeline/skills/shared/external/widgetkit/SKILL.md +25 -101
  88. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +107 -0
  89. package/pipeline/skills/skills-index.md +8 -2
  90. package/pipeline/commands/security-review.md +0 -6
@@ -0,0 +1,371 @@
1
+ # StoreKit 2 Core Patterns
2
+
3
+ Full code for the core purchase, transaction listener, entitlement, and store
4
+ view flows described in the top-level StoreKit skill, plus the annotated common
5
+ mistakes. Load this when you need a complete, copyable example for a core flow;
6
+ SKILL.md keeps the review guidance and short snippets. For control styles,
7
+ offers, testing, server validation, and advanced subscription handling see
8
+ [storekit-advanced.md](storekit-advanced.md).
9
+
10
+ ## Contents
11
+
12
+ - [Purchase Flow](#purchase-flow)
13
+ - [Transaction.updates Listener](#transactionupdates-listener)
14
+ - [Entitlement Checking](#entitlement-checking)
15
+ - [SubscriptionStoreView](#subscriptionstoreview)
16
+ - [StoreView and ProductView](#storeview-and-productview)
17
+ - [Subscription Status Checking](#subscription-status-checking)
18
+ - [App Transaction](#app-transaction)
19
+ - [SwiftUI Purchase Callbacks](#swiftui-purchase-callbacks)
20
+ - [Common Mistakes](#common-mistakes)
21
+
22
+ ## Purchase Flow
23
+
24
+ Handle every `PurchaseResult`, verify before access, deliver durably, then
25
+ finish.
26
+
27
+ ```swift
28
+ @Environment(\.purchase) private var purchase
29
+
30
+ func purchaseProduct(_ product: Product) async throws {
31
+ let result = try await purchase(product, options: [
32
+ .appAccountToken(userAccountToken)
33
+ ])
34
+ switch result {
35
+ case .success(let verification):
36
+ let transaction = try checkVerified(verification)
37
+ await deliverContent(for: transaction)
38
+ await transaction.finish()
39
+ case .userCancelled:
40
+ break
41
+ case .pending:
42
+ // Ask to Buy or deferred approval: show pending UI, no unlock yet.
43
+ showPendingApprovalMessage()
44
+ @unknown default:
45
+ break
46
+ }
47
+ }
48
+
49
+ func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
50
+ switch result {
51
+ case .verified(let value): return value
52
+ case .unverified(_, let error): throw error
53
+ }
54
+ }
55
+ ```
56
+
57
+ ## Transaction.updates Listener
58
+
59
+ Start at app launch, not when a paywall appears. Keep the task retained for the
60
+ app lifetime.
61
+
62
+ ```swift
63
+ @main
64
+ struct MyApp: App {
65
+ private let transactionListener: Task<Void, Never>
66
+
67
+ init() {
68
+ transactionListener = Self.listenForTransactions()
69
+ }
70
+
71
+ var body: some Scene {
72
+ WindowGroup { ContentView() }
73
+ }
74
+
75
+ static func listenForTransactions() -> Task<Void, Never> {
76
+ Task(priority: .background) {
77
+ for await result in Transaction.updates {
78
+ guard case .verified(let transaction) = result else { continue }
79
+ await StoreManager.shared.updateEntitlements()
80
+ await transaction.finish()
81
+ }
82
+ }
83
+ }
84
+ }
85
+ ```
86
+
87
+ ## Entitlement Checking
88
+
89
+ Use `Transaction.currentEntitlements`; skip transactions with a non-nil
90
+ `revocationDate`. Track consumable fulfillment in your own app or server ledger.
91
+
92
+ ```swift
93
+ @Observable
94
+ @MainActor
95
+ class StoreManager {
96
+ static let shared = StoreManager()
97
+ var purchasedProductIDs: Set<String> = []
98
+ var isPremium: Bool { purchasedProductIDs.contains(ProductID.premium) }
99
+
100
+ func updateEntitlements() async {
101
+ var purchased = Set<String>()
102
+ for await result in Transaction.currentEntitlements {
103
+ if case .verified(let transaction) = result,
104
+ transaction.revocationDate == nil {
105
+ purchased.insert(transaction.productID)
106
+ }
107
+ }
108
+ purchasedProductIDs = purchased
109
+ }
110
+ }
111
+ ```
112
+
113
+ ### SwiftUI .currentEntitlementTask Modifier
114
+
115
+ ```swift
116
+ struct PremiumGatedView: View {
117
+ @State private var state: EntitlementTaskState<VerificationResult<Transaction>?> = .loading
118
+
119
+ var body: some View {
120
+ Group {
121
+ switch state {
122
+ case .loading: ProgressView()
123
+ case .failure: PaywallView()
124
+ case .success(.some(.verified(let transaction))) where transaction.revocationDate == nil:
125
+ PremiumContentView()
126
+ case .success:
127
+ PaywallView()
128
+ }
129
+ }
130
+ .currentEntitlementTask(for: ProductID.premium) { state in
131
+ self.state = state
132
+ }
133
+ }
134
+ }
135
+ ```
136
+
137
+ ## SubscriptionStoreView
138
+
139
+ Built-in SwiftUI view for subscription paywalls. Handles product loading,
140
+ purchase UI, and restore purchases automatically.
141
+
142
+ ```swift
143
+ SubscriptionStoreView(groupID: "YOUR_GROUP_ID")
144
+ .subscriptionStoreControlStyle(.prominentPicker)
145
+ .subscriptionStoreButtonLabel(.multiline)
146
+ .storeButton(.visible, for: .restorePurchases)
147
+ .storeButton(.visible, for: .redeemCode)
148
+ .subscriptionStorePolicyDestination(url: termsURL, for: .termsOfService)
149
+ .subscriptionStorePolicyDestination(url: privacyURL, for: .privacyPolicy)
150
+ .onInAppPurchaseCompletion { product, result in
151
+ if case .success(.success(.verified(let transaction))) = result {
152
+ await deliverContent(for: transaction)
153
+ await transaction.finish()
154
+ }
155
+ }
156
+ ```
157
+
158
+ ### Custom Marketing Content
159
+
160
+ ```swift
161
+ SubscriptionStoreView(groupID: "YOUR_GROUP_ID") {
162
+ VStack {
163
+ Image(systemName: "crown.fill").font(.system(size: 60)).foregroundStyle(.yellow)
164
+ Text("Unlock Premium").font(.largeTitle.bold())
165
+ Text("Access all features").foregroundStyle(.secondary)
166
+ }
167
+ }
168
+ .containerBackground(.blue.gradient, for: .subscriptionStore)
169
+ ```
170
+
171
+ For hierarchical layouts (`SubscriptionOptionGroup`, `SubscriptionOptionSection`,
172
+ `SubscriptionPeriodGroupSet`) and control styles see
173
+ [storekit-advanced.md](storekit-advanced.md).
174
+
175
+ ## StoreView and ProductView
176
+
177
+ Merchandise multiple products with localized names, prices, and purchase buttons.
178
+
179
+ ```swift
180
+ StoreView(ids: [ProductID.gems100, ProductID.premium], prefersPromotionalIcon: true)
181
+ .productViewStyle(.large)
182
+ .storeButton(.visible, for: .restorePurchases)
183
+ .onInAppPurchaseCompletion { product, result in
184
+ if case .success(.success(.verified(let transaction))) = result {
185
+ await deliverContent(for: transaction)
186
+ await transaction.finish()
187
+ }
188
+ }
189
+ ```
190
+
191
+ ```swift
192
+ ProductView(id: ProductID.premium) { iconPhase in
193
+ switch iconPhase {
194
+ case .success(let image): image.resizable().scaledToFit()
195
+ case .loading: ProgressView()
196
+ default: Image(systemName: "star.fill")
197
+ }
198
+ }
199
+ .productViewStyle(.large)
200
+ ```
201
+
202
+ ## Subscription Status Checking
203
+
204
+ ```swift
205
+ func checkSubscriptionActive(groupID: String) async throws -> Bool {
206
+ let statuses = try await Product.SubscriptionInfo.status(for: groupID)
207
+ for status in statuses {
208
+ guard case .verified = status.renewalInfo,
209
+ case .verified = status.transaction else { continue }
210
+ if status.state == .subscribed || status.state == .inGracePeriod {
211
+ return true
212
+ }
213
+ }
214
+ return false
215
+ }
216
+ ```
217
+
218
+ ## App Transaction
219
+
220
+ Verify the legitimacy of the app installation. Use for business model changes
221
+ or detecting tampered installations (iOS 16+).
222
+
223
+ ```swift
224
+ func verifyAppPurchase() async {
225
+ do {
226
+ let result = try await AppTransaction.shared
227
+ switch result {
228
+ case .verified(let appTransaction):
229
+ let originalVersion = appTransaction.originalAppVersion
230
+ let purchaseDate = appTransaction.originalPurchaseDate
231
+ // Migration logic for users who paid before subscription model
232
+ case .unverified:
233
+ // Potentially tampered -- restrict features as appropriate
234
+ break
235
+ }
236
+ } catch { /* Could not retrieve app transaction */ }
237
+ }
238
+ ```
239
+
240
+ ## SwiftUI Purchase Callbacks
241
+
242
+ ```swift
243
+ .onInAppPurchaseStart { product in
244
+ await analytics.trackPurchaseStarted(product.id)
245
+ }
246
+ .onInAppPurchaseCompletion { product, result in
247
+ if case .success(.success(.verified(let transaction))) = result {
248
+ await deliverContent(for: transaction)
249
+ await transaction.finish()
250
+ }
251
+ }
252
+ .inAppPurchaseOptions { product in
253
+ [.appAccountToken(userAccountToken)]
254
+ }
255
+ ```
256
+
257
+ ## Common Mistakes
258
+
259
+ ### 1. Not starting Transaction.updates at app launch
260
+
261
+ ```swift
262
+ // WRONG: No listener -- misses renewals, refunds, Ask to Buy approvals
263
+ @main struct MyApp: App {
264
+ var body: some Scene { WindowGroup { ContentView() } }
265
+ }
266
+ // CORRECT: Start listener in App init (see Transaction.updates Listener above)
267
+ ```
268
+
269
+ ### 2. Forgetting transaction.finish()
270
+
271
+ ```swift
272
+ // WRONG: Never finished -- reappears in unfinished queue forever
273
+ let transaction = try checkVerified(verification)
274
+ unlockFeature(transaction.productID)
275
+
276
+ // CORRECT: Deliver durably, then finish. If delivery fails, do not finish yet.
277
+ let transaction = try checkVerified(verification)
278
+ try await recordDelivery(transaction)
279
+ await transaction.finish()
280
+ ```
281
+
282
+ ### 3. Ignoring verification result
283
+
284
+ ```swift
285
+ // WRONG: Using unverified transaction -- security risk
286
+ let transaction = verification.unsafePayloadValue
287
+
288
+ // CORRECT: Verify before using
289
+ let transaction = try checkVerified(verification)
290
+ ```
291
+
292
+ ### 4. Using original In-App Purchase APIs in new StoreKit 2 code
293
+
294
+ ```swift
295
+ // AVOID: Original In-App Purchase APIs
296
+ let request = SKProductsRequest(productIdentifiers: ["com.app.premium"])
297
+ SKPaymentQueue.default().add(payment)
298
+
299
+ // PREFERRED: StoreKit 2
300
+ let products = try await Product.products(for: ["com.app.premium"])
301
+ let result = try await product.purchase()
302
+ ```
303
+
304
+ ### 5. Not checking revocationDate
305
+
306
+ ```swift
307
+ // WRONG: Grants access to refunded purchases
308
+ if case .verified(let transaction) = result {
309
+ purchased.insert(transaction.productID)
310
+ }
311
+
312
+ // CORRECT: Skip revoked transactions
313
+ if case .verified(let transaction) = result, transaction.revocationDate == nil {
314
+ purchased.insert(transaction.productID)
315
+ }
316
+ ```
317
+
318
+ ### 6. Hardcoding prices
319
+
320
+ ```swift
321
+ // WRONG: Wrong for other currencies and regions
322
+ Text("Buy Premium for $4.99")
323
+
324
+ // CORRECT: Localized price from Product
325
+ Text("Buy \(product.displayName) for \(product.displayPrice)")
326
+ ```
327
+
328
+ ### 7. Not handling .pending purchase result
329
+
330
+ ```swift
331
+ // WRONG: Silently drops pending Ask to Buy
332
+ default: break
333
+
334
+ // CORRECT: Explain approval is pending; unlock only after Transaction.updates
335
+ case .pending:
336
+ showPendingApprovalMessage()
337
+ ```
338
+
339
+ ### 8. Checking entitlements only once at launch
340
+
341
+ ```swift
342
+ // WRONG: Check once, never update
343
+ func appDidFinish() { Task { await updateEntitlements() } }
344
+
345
+ // CORRECT: Re-check on Transaction.updates AND on foreground return
346
+ // Transaction.updates listener handles mid-session changes.
347
+ // Also use .task { await storeManager.updateEntitlements() } on content views.
348
+ ```
349
+
350
+ ### 9. Missing restore purchases button
351
+
352
+ ```swift
353
+ // WRONG: No restore option -- App Store rejection risk
354
+ SubscriptionStoreView(groupID: "group_id")
355
+
356
+ // CORRECT
357
+ SubscriptionStoreView(groupID: "group_id")
358
+ .storeButton(.visible, for: .restorePurchases)
359
+ ```
360
+
361
+ ### 10. Subscription views without policy links
362
+
363
+ ```swift
364
+ // WRONG: No terms or privacy policy
365
+ SubscriptionStoreView(groupID: "group_id")
366
+
367
+ // CORRECT
368
+ SubscriptionStoreView(groupID: "group_id")
369
+ .subscriptionStorePolicyDestination(url: termsURL, for: .termsOfService)
370
+ .subscriptionStorePolicyDestination(url: privacyURL, for: .privacyPolicy)
371
+ ```
@@ -225,18 +225,9 @@ struct ToggleFavoriteIntent: AppIntent {
225
225
  }
226
226
  }
227
227
 
228
- struct InteractiveWidgetView: View {
229
- let entry: FavoriteEntry
230
- var body: some View {
231
- HStack {
232
- Text(entry.itemName)
233
- Spacer()
234
- Button(intent: ToggleFavoriteIntent(itemID: entry.itemID)) {
235
- Image(systemName: entry.isFavorite ? "star.fill" : "star")
236
- }
237
- }
238
- .padding()
239
- }
228
+ // In the widget view:
229
+ Button(intent: ToggleFavoriteIntent(itemID: entry.itemID)) {
230
+ Image(systemName: entry.isFavorite ? "star.fill" : "star")
240
231
  }
241
232
  ```
242
233
 
@@ -261,53 +252,11 @@ struct DeliveryAttributes: ActivityAttributes {
261
252
 
262
253
  ### ActivityConfiguration
263
254
 
264
- Provide Lock Screen content and Dynamic Island closures in the widget bundle.
265
-
266
- ```swift
267
- struct DeliveryActivityWidget: Widget {
268
- var body: some WidgetConfiguration {
269
- ActivityConfiguration(for: DeliveryAttributes.self) { context in
270
- VStack(alignment: .leading) {
271
- Text(context.attributes.restaurantName).font(.headline)
272
- HStack {
273
- Text("Driver: \(context.state.driverName)")
274
- Spacer()
275
- Text(timerInterval: context.state.estimatedDeliveryTime, countsDown: true)
276
- }
277
- }
278
- .padding()
279
- } dynamicIsland: { context in
280
- DynamicIsland {
281
- DynamicIslandExpandedRegion(.leading) {
282
- Image(systemName: "box.truck.fill").font(.title2)
283
- }
284
- DynamicIslandExpandedRegion(.trailing) {
285
- Text(timerInterval: context.state.estimatedDeliveryTime, countsDown: true)
286
- .font(.caption)
287
- }
288
- DynamicIslandExpandedRegion(.center) {
289
- Text(context.attributes.restaurantName).font(.headline)
290
- }
291
- DynamicIslandExpandedRegion(.bottom) {
292
- HStack {
293
- ForEach(DeliveryStep.allCases, id: \.self) { step in
294
- Image(systemName: step.icon)
295
- .foregroundStyle(step <= context.state.currentStep ? .primary : .tertiary)
296
- }
297
- }
298
- }
299
- } compactLeading: {
300
- Image(systemName: "box.truck.fill")
301
- } compactTrailing: {
302
- Text(timerInterval: context.state.estimatedDeliveryTime, countsDown: true)
303
- .frame(width: 40).monospacedDigit()
304
- } minimal: {
305
- Image(systemName: "box.truck.fill")
306
- }
307
- }
308
- }
309
- }
310
- ```
255
+ Provide Lock Screen content and Dynamic Island closures in the widget bundle. The
256
+ `ActivityConfiguration(for:)` body returns the Lock Screen view; its
257
+ `dynamicIsland` closure declares the expanded regions plus `compactLeading`,
258
+ `compactTrailing`, and `minimal`. See [references/widgetkit-advanced.md](references/widgetkit-advanced.md)
259
+ ("Live Activity Full ActivityConfiguration Example") for a complete implementation.
311
260
 
312
261
  ### Dynamic Island Regions
313
262
 
@@ -339,8 +288,11 @@ await activity.end(final, dismissalPolicy: .after(.now.addingTimeInterval(3600))
339
288
 
340
289
  ## Control Center Widgets (iOS 18+)
341
290
 
291
+ Wrap a `ControlWidgetButton` or `ControlWidgetToggle` in a
292
+ `StaticControlConfiguration` (or `AppIntentControlConfiguration`). Each control
293
+ runs an `AppIntent` action.
294
+
342
295
  ```swift
343
- // Button control
344
296
  struct OpenCameraControl: ControlWidget {
345
297
  var body: some ControlWidgetConfiguration {
346
298
  StaticControlConfiguration(kind: "OpenCamera") {
@@ -351,41 +303,18 @@ struct OpenCameraControl: ControlWidget {
351
303
  .displayName("Open Camera")
352
304
  }
353
305
  }
354
-
355
- // Toggle control with value provider
356
- struct FlashlightControl: ControlWidget {
357
- var body: some ControlWidgetConfiguration {
358
- StaticControlConfiguration(kind: "Flashlight", provider: FlashlightValueProvider()) { value in
359
- ControlWidgetToggle(isOn: value, action: ToggleFlashlightIntent()) {
360
- Label("Flashlight", systemImage: value ? "flashlight.on.fill" : "flashlight.off.fill")
361
- }
362
- }
363
- .displayName("Flashlight")
364
- }
365
- }
366
306
  ```
367
307
 
368
- ## Lock Screen Widgets
308
+ See [references/widgetkit-advanced.md](references/widgetkit-advanced.md) ("Control Center Control
309
+ Examples") for a toggle control backed by a value provider.
369
310
 
370
- Use accessory families and `AccessoryWidgetBackground`.
311
+ ## Lock Screen Widgets
371
312
 
372
- ```swift
373
- struct StepsWidget: Widget {
374
- let kind = "StepsWidget"
375
- var body: some WidgetConfiguration {
376
- StaticConfiguration(kind: kind, provider: StepsProvider()) { entry in
377
- ZStack {
378
- AccessoryWidgetBackground()
379
- VStack {
380
- Image(systemName: "figure.walk")
381
- Text("\(entry.stepCount)").font(.headline)
382
- }
383
- }
384
- }
385
- .supportedFamilies([.accessoryCircular, .accessoryRectangular, .accessoryInline])
386
- }
387
- }
388
- ```
313
+ Use accessory families (`.accessoryCircular`, `.accessoryRectangular`,
314
+ `.accessoryInline`) and wrap content in `AccessoryWidgetBackground()` for the
315
+ standard translucent backing. See [references/widgetkit-advanced.md](references/widgetkit-advanced.md)
316
+ ("Lock Screen Accessory Widget Example") for a full widget, and
317
+ "AccessoryWidgetBackground" for rendering-mode adaptation.
389
318
 
390
319
  ## StandBy Mode
391
320
 
@@ -430,16 +359,11 @@ Adapt widgets to the Liquid Glass visual style using `WidgetAccentedRenderingMod
430
359
 
431
360
  ### WidgetPushHandler
432
361
 
433
- Enable push-based timeline reloads without scheduled polling.
434
-
435
- ```swift
436
- struct MyWidgetPushHandler: WidgetPushHandler {
437
- func pushTokenDidChange(_ pushInfo: WidgetPushInfo, widgets: [WidgetInfo]) {
438
- let tokenString = pushInfo.token.map { String(format: "%02x", $0) }.joined()
439
- // Send tokenString to your server
440
- }
441
- }
442
- ```
362
+ Enable push-based timeline reloads without scheduled polling: implement
363
+ `WidgetPushHandler.pushTokenDidChange(_:widgets:)`, register the token with your
364
+ server, and send an APNs push to trigger a reload. See
365
+ [references/widgetkit-advanced.md](references/widgetkit-advanced.md) ("Push-Based Timeline Reloads") for
366
+ the handler plus the `ControlPushHandler` equivalent.
443
367
 
444
368
  ### CarPlay Widgets
445
369
 
@@ -9,6 +9,9 @@
9
9
  - [Multiple Widget Support in WidgetBundle](#multiple-widget-support-in-widgetbundle)
10
10
  - [Widget Previews and Snapshots](#widget-previews-and-snapshots)
11
11
  - [AccessoryWidgetBackground](#accessorywidgetbackground)
12
+ - [Lock Screen Accessory Widget Example](#lock-screen-accessory-widget-example)
13
+ - [Live Activity Full ActivityConfiguration Example](#live-activity-full-activityconfiguration-example)
14
+ - [Control Center Control Examples](#control-center-control-examples)
12
15
  - [Dynamic Island Expanded Layout Patterns](#dynamic-island-expanded-layout-patterns)
13
16
  - [Alert Configuration for Live Activities](#alert-configuration-for-live-activities)
14
17
  - [Push Notification Support for Live Activities](#push-notification-support-for-live-activities)
@@ -390,6 +393,110 @@ var body: some View {
390
393
  Use `.widgetAccentable()` to mark views that should receive the accent tint in
391
394
  `.accented` rendering mode.
392
395
 
396
+ ## Lock Screen Accessory Widget Example
397
+
398
+ A full Lock Screen widget using accessory families and `AccessoryWidgetBackground`.
399
+
400
+ ```swift
401
+ struct StepsWidget: Widget {
402
+ let kind = "StepsWidget"
403
+ var body: some WidgetConfiguration {
404
+ StaticConfiguration(kind: kind, provider: StepsProvider()) { entry in
405
+ ZStack {
406
+ AccessoryWidgetBackground()
407
+ VStack {
408
+ Image(systemName: "figure.walk")
409
+ Text("\(entry.stepCount)").font(.headline)
410
+ }
411
+ }
412
+ }
413
+ .supportedFamilies([.accessoryCircular, .accessoryRectangular, .accessoryInline])
414
+ }
415
+ }
416
+ ```
417
+
418
+ ## Live Activity Full ActivityConfiguration Example
419
+
420
+ A complete `ActivityConfiguration` with Lock Screen content and the Dynamic
421
+ Island closures (expanded regions, compact, minimal).
422
+
423
+ ```swift
424
+ struct DeliveryActivityWidget: Widget {
425
+ var body: some WidgetConfiguration {
426
+ ActivityConfiguration(for: DeliveryAttributes.self) { context in
427
+ VStack(alignment: .leading) {
428
+ Text(context.attributes.restaurantName).font(.headline)
429
+ HStack {
430
+ Text("Driver: \(context.state.driverName)")
431
+ Spacer()
432
+ Text(timerInterval: context.state.estimatedDeliveryTime, countsDown: true)
433
+ }
434
+ }
435
+ .padding()
436
+ } dynamicIsland: { context in
437
+ DynamicIsland {
438
+ DynamicIslandExpandedRegion(.leading) {
439
+ Image(systemName: "box.truck.fill").font(.title2)
440
+ }
441
+ DynamicIslandExpandedRegion(.trailing) {
442
+ Text(timerInterval: context.state.estimatedDeliveryTime, countsDown: true)
443
+ .font(.caption)
444
+ }
445
+ DynamicIslandExpandedRegion(.center) {
446
+ Text(context.attributes.restaurantName).font(.headline)
447
+ }
448
+ DynamicIslandExpandedRegion(.bottom) {
449
+ HStack {
450
+ ForEach(DeliveryStep.allCases, id: \.self) { step in
451
+ Image(systemName: step.icon)
452
+ .foregroundStyle(step <= context.state.currentStep ? .primary : .tertiary)
453
+ }
454
+ }
455
+ }
456
+ } compactLeading: {
457
+ Image(systemName: "box.truck.fill")
458
+ } compactTrailing: {
459
+ Text(timerInterval: context.state.estimatedDeliveryTime, countsDown: true)
460
+ .frame(width: 40).monospacedDigit()
461
+ } minimal: {
462
+ Image(systemName: "box.truck.fill")
463
+ }
464
+ }
465
+ }
466
+ }
467
+ ```
468
+
469
+ ## Control Center Control Examples
470
+
471
+ Button and toggle controls for Control Center (iOS 18+). A toggle can read its
472
+ current state from a value provider.
473
+
474
+ ```swift
475
+ // Button control
476
+ struct OpenCameraControl: ControlWidget {
477
+ var body: some ControlWidgetConfiguration {
478
+ StaticControlConfiguration(kind: "OpenCamera") {
479
+ ControlWidgetButton(action: OpenCameraIntent()) {
480
+ Label("Camera", systemImage: "camera.fill")
481
+ }
482
+ }
483
+ .displayName("Open Camera")
484
+ }
485
+ }
486
+
487
+ // Toggle control with value provider
488
+ struct FlashlightControl: ControlWidget {
489
+ var body: some ControlWidgetConfiguration {
490
+ StaticControlConfiguration(kind: "Flashlight", provider: FlashlightValueProvider()) { value in
491
+ ControlWidgetToggle(isOn: value, action: ToggleFlashlightIntent()) {
492
+ Label("Flashlight", systemImage: value ? "flashlight.on.fill" : "flashlight.off.fill")
493
+ }
494
+ }
495
+ .displayName("Flashlight")
496
+ }
497
+ }
498
+ ```
499
+
393
500
  ## Dynamic Island Expanded Layout Patterns
394
501
 
395
502
  ### Full Layout Example