@mmerterden/multi-agent-pipeline 20.6.0 → 20.8.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.
- package/CHANGELOG.md +27 -0
- package/LICENSE +0 -10
- package/docs/facts.json +1 -1
- package/manifest.json +328 -329
- package/package.json +2 -2
- package/pipeline/multi-agent-refs/features/design-conformance.md +62 -64
- package/pipeline/scripts/_notices.mjs +12 -1
- package/pipeline/scripts/gen-skills-index.mjs +1 -1
- package/pipeline/skills/.skill-manifest.json +106 -106
- package/pipeline/skills/shared/README.md +71 -71
- package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +1 -0
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
- package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
- package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
- package/pipeline/skills/shared/external/android-architecture/SKILL.md +2 -0
- package/pipeline/skills/shared/external/android-performance/SKILL.md +2 -0
- package/pipeline/skills/shared/external/android-security/SKILL.md +2 -0
- package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
- package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
- package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
- package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
- package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
- package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
- package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
- package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
- package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
- package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
- package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
- package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
- package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
- package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
- package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
- package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
- package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
- package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
- package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
- package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
- package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
- package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
- package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
- package/pipeline/skills/shared/external/backlog/BACKLOG.md +1 -1
- package/pipeline/skills/shared/external/backlog/SKILL.md +56 -33
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
- package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
- package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +1 -0
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
- package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
- package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
- package/pipeline/skills/shared/external/compose-components/SKILL.md +2 -0
- package/pipeline/skills/shared/external/compose-navigation/SKILL.md +3 -2
- package/pipeline/skills/shared/external/compose-testing/SKILL.md +2 -0
- package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
- package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
- package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
- package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
- package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
- package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
- package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
- package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
- package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
- package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
- package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
- package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
- package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
- package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
- package/pipeline/skills/shared/external/council/SKILL.md +1 -0
- package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
- package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
- package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
- package/pipeline/skills/shared/external/css-modern/SKILL.md +1 -0
- package/pipeline/skills/shared/external/database-patterns/SKILL.md +1 -0
- package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
- package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
- package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
- package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
- package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
- package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
- package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
- package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
- package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
- package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
- package/pipeline/skills/shared/external/evidence-github/SKILL.md +2 -0
- package/pipeline/skills/shared/external/evidence-registry/SKILL.md +2 -0
- package/pipeline/skills/shared/external/gradle-kotlin-dsl/SKILL.md +2 -0
- package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
- package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
- package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
- package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
- package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
- package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
- package/pipeline/skills/shared/external/html-semantic/SKILL.md +1 -0
- package/pipeline/skills/shared/external/humanizer/SKILL.md +1 -0
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
- package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
- package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
- package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
- package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
- package/pipeline/skills/shared/external/ios-coding-standard/SKILL.md +1 -0
- package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
- package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
- package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
- package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
- package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +1 -0
- package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
- package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
- package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
- package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
- package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
- package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
- package/pipeline/skills/shared/external/ios-security/SKILL.md +2 -0
- package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
- package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
- package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
- package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
- package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +91 -283
- package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +119 -151
- package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +60 -90
- package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +119 -156
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +726 -787
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +253 -288
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +243 -304
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +88 -104
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +181 -235
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-key-shots.py +198 -263
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +461 -466
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +145 -151
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +123 -141
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +146 -157
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/snapshot-resources.sh +22 -19
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +156 -140
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
- package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
- package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
- package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
- package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
- package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
- package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
- package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
- package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
- package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
- package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +1 -0
- package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
- package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
- package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
- package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
- package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
- package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
- package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
- package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
- package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
- package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
- package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
- package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
- package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
- package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
- package/pipeline/skills/shared/external/play-store-review/SKILL.md +2 -0
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
- package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
- package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
- package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
- package/pipeline/skills/shared/external/python-patterns/SKILL.md +1 -0
- package/pipeline/skills/shared/external/react-best-practices/SKILL.md +1 -0
- package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
- package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
- package/pipeline/skills/shared/external/rest-api-design/SKILL.md +1 -0
- package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +2 -0
- package/pipeline/skills/shared/external/room-database/SKILL.md +2 -0
- package/pipeline/skills/shared/external/search-first/SKILL.md +1 -0
- package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
- package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
- package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
- package/pipeline/skills/shared/external/signal-community/SKILL.md +2 -0
- package/pipeline/skills/shared/external/skill-creator/SKILL.md +80 -41
- package/pipeline/skills/shared/external/skill-creator/audit.md +63 -59
- package/pipeline/skills/shared/external/skill-creator/checklist.md +28 -20
- package/pipeline/skills/shared/external/skill-creator/examples.md +40 -40
- package/pipeline/skills/shared/external/skill-creator/label-check.md +48 -36
- package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +91 -100
- package/pipeline/skills/shared/external/skill-creator/template.md +51 -39
- package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
- package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
- package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
- package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
- package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
- package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
- package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
- package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
- package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
- package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
- package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
- package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
- package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
- package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
- package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
- package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
- package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
- package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
- package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
- package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
- package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
- package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
- package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
- package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
- package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
- package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
- package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
- package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
- package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
- package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
- package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
- package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
- package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
- package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
- package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
- package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
- package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
- package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
- package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
- package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
- package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
- package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
- package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
- package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
- package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
- package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
- package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
- package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
- package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
- package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
- package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
- package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
- package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
- package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
- package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
- package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
- package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
- package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
- package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
- package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
- package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
- package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
- package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
- package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
- package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
- package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
- package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
- package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
- package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
- package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
- package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
- package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
- package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
- package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
- package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
- package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
- package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
- package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
- package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
- package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
- package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
- package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
- package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
- package/pipeline/skills/shared/external/tailwind-css/SKILL.md +1 -0
- package/pipeline/skills/shared/external/testing-backend/SKILL.md +1 -0
- package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
- package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
- package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +1 -0
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
- package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
- package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
- package/pipeline/skills/shared/external/vue-composition/SKILL.md +1 -0
- package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
- package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
- package/pipeline/skills/shared/external/web-accessibility/SKILL.md +1 -0
- package/pipeline/skills/shared/external/web-performance/SKILL.md +1 -0
- package/pipeline/skills/shared/external/web-testing/SKILL.md +1 -0
- package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
- package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
- package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
|
@@ -1,500 +1,329 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: shareplay-activities
|
|
3
|
-
description: "
|
|
3
|
+
description: "GroupActivities and SharePlay on iOS, macOS, tvOS and visionOS: GroupActivity, GroupSession lifecycle, messengers, playback sync, file transfer, late joiners. Use for shared media, collaboration or synced game state over FaceTime, Messages, AirDrop or nearby sharing."
|
|
4
4
|
metadata:
|
|
5
|
-
source:
|
|
5
|
+
source: multi-agent-pipeline
|
|
6
6
|
---
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
- [Setup](#setup)
|
|
15
|
-
- [Defining a GroupActivity](#defining-a-groupactivity)
|
|
16
|
-
- [Session Lifecycle](#session-lifecycle)
|
|
17
|
-
- [Sending and Receiving Messages](#sending-and-receiving-messages)
|
|
18
|
-
- [Coordinated Media Playback](#coordinated-media-playback)
|
|
19
|
-
- [Starting SharePlay from Your App](#starting-shareplay-from-your-app)
|
|
20
|
-
- [GroupSessionJournal: File Transfer](#groupsessionjournal-file-transfer)
|
|
21
|
-
- [Common Mistakes](#common-mistakes)
|
|
22
|
-
- [Review Checklist](#review-checklist)
|
|
23
|
-
- [References](#references)
|
|
7
|
+
|
|
8
|
+
# SharePlay and Group Activities
|
|
9
|
+
|
|
10
|
+
SharePlay joins people in a FaceTime call, a Messages conversation, an AirDrop
|
|
11
|
+
exchange or, on visionOS, a room of nearby headsets, and keeps media, app state
|
|
12
|
+
or custom data in step between them. Code here is written for Swift 6.3 on
|
|
13
|
+
iOS 26 and newer.
|
|
24
14
|
|
|
25
15
|
## Setup
|
|
26
16
|
|
|
27
17
|
### Capability
|
|
28
18
|
|
|
29
|
-
Add
|
|
30
|
-
|
|
19
|
+
Add **Group Activities** to the app target under Signing & Capabilities. Xcode
|
|
20
|
+
then sets `com.apple.developer.group-session` to `true` in the entitlements
|
|
21
|
+
file and refreshes provisioning.
|
|
31
22
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
<true/>
|
|
35
|
-
```
|
|
23
|
+
Only app targets get it. Group Activities does not run in widgets, other
|
|
24
|
+
extensions or App Clips.
|
|
36
25
|
|
|
37
|
-
|
|
38
|
-
widgets, extensions, or App Clips.
|
|
26
|
+
### Eligibility
|
|
39
27
|
|
|
40
|
-
|
|
28
|
+
`GroupStateObserver` reports whether a FaceTime call, or a Messages chat, is
|
|
29
|
+
currently able to host an activity. Use it to decide whether a
|
|
30
|
+
SharePlay button appears.
|
|
41
31
|
|
|
42
32
|
```swift
|
|
43
33
|
import GroupActivities
|
|
44
34
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
Observe changes reactively:
|
|
54
|
-
|
|
55
|
-
```swift
|
|
56
|
-
for await isEligible in observer.$isEligibleForGroupSession.values {
|
|
57
|
-
showSharePlayButton(isEligible)
|
|
35
|
+
@MainActor
|
|
36
|
+
func watchEligibility(update: @escaping @MainActor (Bool) -> Void) async {
|
|
37
|
+
let observer = GroupStateObserver()
|
|
38
|
+
update(observer.isEligibleForGroupSession)
|
|
39
|
+
for await eligible in observer.$isEligibleForGroupSession.values {
|
|
40
|
+
update(eligible)
|
|
41
|
+
}
|
|
58
42
|
}
|
|
59
43
|
```
|
|
60
44
|
|
|
61
|
-
## Defining
|
|
62
|
-
|
|
63
|
-
Conform to `GroupActivity` and provide metadata:
|
|
45
|
+
## Defining an activity
|
|
64
46
|
|
|
65
47
|
```swift
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
struct WatchTogetherActivity: GroupActivity {
|
|
69
|
-
let movieID: String
|
|
70
|
-
let movieTitle: String
|
|
48
|
+
struct CookAlong: GroupActivity {
|
|
49
|
+
let recipeID: String
|
|
71
50
|
|
|
72
51
|
var metadata: GroupActivityMetadata {
|
|
73
|
-
var
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return
|
|
52
|
+
var info = GroupActivityMetadata()
|
|
53
|
+
info.title = "Cook together"
|
|
54
|
+
info.type = .exploreTogether
|
|
55
|
+
info.fallbackURL = URL(string: "https://recipes.example/r/\(recipeID)")
|
|
56
|
+
return info
|
|
78
57
|
}
|
|
79
58
|
}
|
|
80
59
|
```
|
|
81
60
|
|
|
82
|
-
### Activity
|
|
61
|
+
### Activity types
|
|
83
62
|
|
|
84
|
-
| Type |
|
|
85
|
-
|
|
86
|
-
| `.generic` |
|
|
87
|
-
| `.watchTogether` | Video
|
|
88
|
-
| `.listenTogether` | Audio
|
|
89
|
-
| `.createTogether` |
|
|
90
|
-
| `.exploreTogether` |
|
|
91
|
-
| `.learnTogether` |
|
|
92
|
-
| `.readTogether` |
|
|
93
|
-
| `.shopTogether` |
|
|
94
|
-
| `.workoutTogether` |
|
|
63
|
+
| Type | Fits |
|
|
64
|
+
| --- | --- |
|
|
65
|
+
| `.generic` | The default for anything custom |
|
|
66
|
+
| `.watchTogether` | Video |
|
|
67
|
+
| `.listenTogether` | Audio |
|
|
68
|
+
| `.createTogether` | Drawing, editing, building something together |
|
|
69
|
+
| `.exploreTogether` | Browsing, planning, exploring |
|
|
70
|
+
| `.learnTogether` | Study sessions |
|
|
71
|
+
| `.readTogether` | Reading |
|
|
72
|
+
| `.shopTogether` | Shopping |
|
|
73
|
+
| `.workoutTogether` | Fitness |
|
|
95
74
|
|
|
96
|
-
`GroupActivity`
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
75
|
+
`GroupActivity` inherits `Codable`, so every stored property must be codable.
|
|
76
|
+
Keep the payload tiny: an identifier or a URL, never the content itself. Add
|
|
77
|
+
`Transferable` only when the activity is offered through a SwiftUI `ShareLink`,
|
|
78
|
+
an AirDrop hand-off, or a UIKit or AppKit share sheet.
|
|
100
79
|
|
|
101
|
-
## Session
|
|
80
|
+
## Session lifecycle
|
|
102
81
|
|
|
103
|
-
###
|
|
82
|
+
### Receiving sessions
|
|
104
83
|
|
|
105
|
-
|
|
106
|
-
|
|
84
|
+
Sessions started by anyone arrive through `Activity.sessions()`. Iterate it in
|
|
85
|
+
a task owned by a long-lived object.
|
|
107
86
|
|
|
108
87
|
```swift
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
88
|
+
import GroupActivities
|
|
89
|
+
import Observation
|
|
90
|
+
|
|
91
|
+
@MainActor @Observable
|
|
92
|
+
final class KitchenSession {
|
|
93
|
+
private(set) var people: Set<Participant> = []
|
|
94
|
+
private(set) var session: GroupSession<CookAlong>?
|
|
95
|
+
private var jobs: [Task<Void, Never>] = []
|
|
113
96
|
private var messenger: GroupSessionMessenger?
|
|
114
|
-
private var sessionTasks: [Task<Void, Never>] = []
|
|
115
97
|
|
|
116
|
-
|
|
117
|
-
Task {
|
|
118
|
-
for await
|
|
119
|
-
self.configureSession(session)
|
|
120
|
-
}
|
|
98
|
+
init() {
|
|
99
|
+
Task { @MainActor [weak self] in
|
|
100
|
+
for await incoming in CookAlong.sessions() { self?.attach(incoming) }
|
|
121
101
|
}
|
|
122
102
|
}
|
|
123
103
|
|
|
124
|
-
private func
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
104
|
+
private func attach(_ incoming: GroupSession<CookAlong>) {
|
|
105
|
+
session = incoming
|
|
106
|
+
messenger = GroupSessionMessenger(session: incoming)
|
|
107
|
+
let states = incoming.$state.values
|
|
108
|
+
let rosters = incoming.$activeParticipants.values
|
|
109
|
+
jobs.append(Task { @MainActor [weak self] in
|
|
110
|
+
for await phase in states { self?.handle(phase) }
|
|
111
|
+
})
|
|
112
|
+
jobs.append(Task { @MainActor [weak self] in
|
|
113
|
+
for await roster in rosters { self?.people = roster }
|
|
114
|
+
})
|
|
115
|
+
incoming.join()
|
|
116
|
+
}
|
|
137
117
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
118
|
+
private func handle(_ state: GroupSession<CookAlong>.State) {
|
|
119
|
+
switch state {
|
|
120
|
+
case .waiting:
|
|
121
|
+
break
|
|
122
|
+
case .joined:
|
|
123
|
+
if let recipe = session?.activity.recipeID { RecipeStore.shared.open(recipe) }
|
|
124
|
+
case .invalidated:
|
|
125
|
+
teardown()
|
|
126
|
+
@unknown default:
|
|
127
|
+
break
|
|
143
128
|
}
|
|
144
|
-
sessionTasks.append(participantTask)
|
|
145
|
-
|
|
146
|
-
// Join the session
|
|
147
|
-
session.join()
|
|
148
129
|
}
|
|
149
130
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
131
|
+
func teardown() {
|
|
132
|
+
for job in jobs { job.cancel() }
|
|
133
|
+
jobs = []
|
|
153
134
|
session = nil
|
|
154
135
|
messenger = nil
|
|
155
136
|
}
|
|
156
137
|
}
|
|
157
138
|
```
|
|
158
139
|
|
|
159
|
-
###
|
|
160
|
-
|
|
161
|
-
| State | Description |
|
|
162
|
-
|---|---|
|
|
163
|
-
| `.waiting` | Session exists but local participant has not joined |
|
|
164
|
-
| `.joined` | Local participant is actively in the session |
|
|
165
|
-
| `.invalidated(reason:)` | Session ended (check reason for details) |
|
|
166
|
-
|
|
167
|
-
### Handling State Changes
|
|
168
|
-
|
|
169
|
-
```swift
|
|
170
|
-
private func handleState(_ state: GroupSession<WatchTogetherActivity>.State) {
|
|
171
|
-
switch state {
|
|
172
|
-
case .waiting:
|
|
173
|
-
print("Waiting to join")
|
|
174
|
-
case .joined:
|
|
175
|
-
print("Joined session")
|
|
176
|
-
loadActivity(session?.activity)
|
|
177
|
-
case .invalidated(let reason):
|
|
178
|
-
print("Session ended: \(reason)")
|
|
179
|
-
cleanUp()
|
|
180
|
-
@unknown default:
|
|
181
|
-
break
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
private func handleParticipants(_ participants: Set<Participant>) {
|
|
186
|
-
print("Active participants: \(participants.count)")
|
|
187
|
-
}
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
### Leaving and Ending
|
|
140
|
+
### States
|
|
191
141
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
session
|
|
142
|
+
| State | Meaning |
|
|
143
|
+
| --- | --- |
|
|
144
|
+
| `.waiting` | The session exists; the local person has not joined |
|
|
145
|
+
| `.joined` | The local person is in the session |
|
|
146
|
+
| `.invalidated(reason:)` | The session is over; the reason says why |
|
|
195
147
|
|
|
196
|
-
|
|
197
|
-
session?.end()
|
|
198
|
-
```
|
|
148
|
+
Participants come as a `Set<Participant>`.
|
|
199
149
|
|
|
200
|
-
|
|
150
|
+
### Leaving and ending
|
|
201
151
|
|
|
202
|
-
|
|
203
|
-
participants.
|
|
152
|
+
- `session?.leave()`: only this device leaves; everyone else carries on.
|
|
153
|
+
- `session?.end()`: the session ends for all participants.
|
|
204
154
|
|
|
205
|
-
|
|
155
|
+
## Messages
|
|
206
156
|
|
|
207
|
-
|
|
157
|
+
`GroupSessionMessenger` carries small, time-sensitive app state. Each message
|
|
158
|
+
is `Codable` and must stay under 256 KB.
|
|
208
159
|
|
|
209
160
|
```swift
|
|
210
|
-
struct
|
|
211
|
-
let
|
|
212
|
-
let timestamp: Date
|
|
213
|
-
let data: [String: String]
|
|
161
|
+
struct StepChanged: Codable, Sendable {
|
|
162
|
+
let stepIndex: Int
|
|
214
163
|
}
|
|
215
|
-
```
|
|
216
|
-
|
|
217
|
-
### Sending
|
|
218
|
-
|
|
219
|
-
```swift
|
|
220
|
-
func sendSync(_ message: SyncMessage) async throws {
|
|
221
|
-
guard let messenger else { return }
|
|
222
164
|
|
|
223
|
-
|
|
165
|
+
func announce(step: Int, via messenger: GroupSessionMessenger) async throws {
|
|
166
|
+
try await messenger.send(StepChanged(stepIndex: step), to: .all)
|
|
224
167
|
}
|
|
225
168
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
### Receiving
|
|
231
|
-
|
|
232
|
-
```swift
|
|
233
|
-
func observeMessages() {
|
|
234
|
-
guard let messenger else { return }
|
|
235
|
-
|
|
236
|
-
Task {
|
|
237
|
-
for await (message, context) in messenger.messages(of: SyncMessage.self) {
|
|
238
|
-
let sender = context.source
|
|
239
|
-
handleReceivedMessage(message, from: sender)
|
|
240
|
-
}
|
|
169
|
+
func listen(on messenger: GroupSessionMessenger) async {
|
|
170
|
+
for await (change, context) in messenger.messages(of: StepChanged.self) {
|
|
171
|
+
RecipeStore.shared.show(step: change.stepIndex, from: context.source)
|
|
241
172
|
}
|
|
242
173
|
}
|
|
243
174
|
```
|
|
244
175
|
|
|
245
|
-
|
|
176
|
+
Send to one person with `to: .only(participant)`; the sender of a received
|
|
177
|
+
message is `context.source`.
|
|
246
178
|
|
|
247
|
-
|
|
248
|
-
// Reliable (default) -- checked and retried for crucial state
|
|
249
|
-
let reliableMessenger = GroupSessionMessenger(
|
|
250
|
-
session: session,
|
|
251
|
-
deliveryMode: .reliable
|
|
252
|
-
)
|
|
253
|
-
|
|
254
|
-
// Unreliable -- lower latency, no delivery guarantee
|
|
255
|
-
let unreliableMessenger = GroupSessionMessenger(
|
|
256
|
-
session: session,
|
|
257
|
-
deliveryMode: .unreliable
|
|
258
|
-
)
|
|
259
|
-
```
|
|
179
|
+
Pick a delivery mode with `GroupSessionMessenger(session:deliveryMode:)`:
|
|
260
180
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
181
|
+
| Mode | Behaviour | Use for |
|
|
182
|
+
| --- | --- | --- |
|
|
183
|
+
| `.reliable` (default) | Delivery checked and retried | State changes: selections, turns |
|
|
184
|
+
| `.unreliable` | Lower latency, may drop | Frequent throwaway data: cursors, strokes, reactions |
|
|
264
185
|
|
|
265
|
-
## Coordinated
|
|
186
|
+
## Coordinated media playback
|
|
266
187
|
|
|
267
|
-
For
|
|
188
|
+
For `AVPlayer` content, hand the session to the player's coordinator:
|
|
268
189
|
|
|
269
190
|
```swift
|
|
270
191
|
import AVFoundation
|
|
271
|
-
import GroupActivities
|
|
272
192
|
|
|
273
|
-
func
|
|
274
|
-
session
|
|
275
|
-
player: AVPlayer
|
|
276
|
-
) {
|
|
277
|
-
// Connect the player's coordinator to the session
|
|
278
|
-
let coordinator = player.playbackCoordinator
|
|
279
|
-
coordinator.coordinateWithSession(session)
|
|
193
|
+
func syncPlayback(_ player: AVPlayer, with session: GroupSession<MovieNight>) {
|
|
194
|
+
player.playbackCoordinator.coordinateWithSession(session)
|
|
280
195
|
}
|
|
281
196
|
```
|
|
282
197
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
198
|
+
`AVPlaybackCoordinator` then keeps play, pause, seeks, rate and position in
|
|
199
|
+
step for everyone. Play state, position and rate never belong in your own
|
|
200
|
+
messages or state snapshots, late-joiner snapshots included. Custom messages are
|
|
201
|
+
for state that playback does not cover.
|
|
286
202
|
|
|
287
|
-
## Starting SharePlay from
|
|
288
|
-
|
|
289
|
-
### Using GroupActivitySharingController (UIKit)
|
|
203
|
+
## Starting SharePlay from the app
|
|
290
204
|
|
|
291
205
|
```swift
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
func startSharePlay() async throws {
|
|
296
|
-
let activity = WatchTogetherActivity(
|
|
297
|
-
movieID: "123",
|
|
298
|
-
movieTitle: "Great Movie"
|
|
299
|
-
)
|
|
300
|
-
|
|
301
|
-
switch await activity.prepareForActivation() {
|
|
302
|
-
case .activationPreferred:
|
|
303
|
-
// A conversation is active and the user chose to share.
|
|
304
|
-
_ = try await activity.activate()
|
|
305
|
-
|
|
206
|
+
func startCooking(_ activity: CookAlong) async {
|
|
207
|
+
let choice = await activity.prepareForActivation()
|
|
208
|
+
switch choice {
|
|
306
209
|
case .activationDisabled:
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
210
|
+
RecipeStore.shared.open(activity.recipeID)
|
|
211
|
+
case .activationPreferred:
|
|
212
|
+
do { _ = try await activity.activate() } catch { Log.share.error("\(error)") }
|
|
310
213
|
case .cancelled:
|
|
311
|
-
|
|
312
|
-
|
|
214
|
+
return
|
|
313
215
|
@unknown default:
|
|
314
|
-
|
|
216
|
+
return
|
|
315
217
|
}
|
|
316
218
|
}
|
|
317
219
|
```
|
|
318
220
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
present(controller, animated: true)
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
Use the `shareplay` SF Symbol for custom controls. Treat `GroupActivityMetadata`
|
|
328
|
-
as discovery copy: concise title, subtitle, image, and type aligned with the
|
|
329
|
-
entry point. Keep sibling domains out: GameKit owns auth, matchmaking,
|
|
330
|
-
leaderboards, achievements, and voice/chat; TabletopKit owns seats, board
|
|
331
|
-
equipment, spatial placement, turns, rules, and authoritative tabletop state;
|
|
332
|
-
AVKit owns playback UI. SharePlay owns invitations, lifecycle, participants, and
|
|
333
|
-
coordination handoffs. See [references/shareplay-patterns.md](references/shareplay-patterns.md) for SwiftUI `ShareLink`, AirDrop, and direct activation patterns.
|
|
334
|
-
|
|
335
|
-
## GroupSessionJournal: File Transfer
|
|
336
|
-
|
|
337
|
-
For larger, non-time-sensitive attachments, use `GroupSessionJournal` instead
|
|
338
|
-
of `GroupSessionMessenger`. Journal items must conform to `Transferable`, are
|
|
339
|
-
available to late joiners, and are limited to 100 MB. It requires iOS/iPadOS/tvOS
|
|
340
|
-
17+, macOS 14+, or visionOS 1+. For larger/protected assets, share a pointer or manifest and use server storage or app-managed file transfer.
|
|
341
|
-
|
|
342
|
-
```swift
|
|
343
|
-
import GroupActivities
|
|
344
|
-
|
|
345
|
-
let journal = GroupSessionJournal(session: session)
|
|
346
|
-
|
|
347
|
-
// Upload a Transferable file or data item
|
|
348
|
-
let attachment = try await journal.add(sharedImageItem)
|
|
349
|
-
|
|
350
|
-
// Observe incoming attachments
|
|
351
|
-
Task {
|
|
352
|
-
for await attachments in journal.attachments {
|
|
353
|
-
for attachment in attachments {
|
|
354
|
-
let data = try await attachment.load(Data.self)
|
|
355
|
-
handleReceivedFile(data)
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
## Common Mistakes
|
|
362
|
-
|
|
363
|
-
### DON'T: Forget to call session.join()
|
|
364
|
-
|
|
365
|
-
```swift
|
|
366
|
-
// WRONG -- session is received but never joined
|
|
367
|
-
for await session in MyActivity.sessions() {
|
|
368
|
-
self.session = session
|
|
369
|
-
// Session stays in .waiting state forever
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// CORRECT -- join after configuring
|
|
373
|
-
for await session in MyActivity.sessions() {
|
|
374
|
-
self.session = session
|
|
375
|
-
self.messenger = GroupSessionMessenger(session: session)
|
|
376
|
-
session.join()
|
|
377
|
-
}
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
### DON'T: Forget to leave or end sessions
|
|
221
|
+
- `.activationPreferred`: there is a live conversation and sharing was
|
|
222
|
+
chosen; call `activate()`.
|
|
223
|
+
- `.activationDisabled`: they chose to stay local, or sharing is not possible;
|
|
224
|
+
start the local experience.
|
|
225
|
+
- `.cancelled`: do nothing.
|
|
381
226
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
// Nothing -- session leaks
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
// CORRECT -- leave when the view is dismissed
|
|
389
|
-
func viewDidDisappear() {
|
|
390
|
-
session?.leave()
|
|
391
|
-
session = nil
|
|
392
|
-
messenger = nil
|
|
393
|
-
}
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
### DON'T: Assume all participants have the same state
|
|
227
|
+
With no conversation in progress (`isEligibleForGroupSession == false`),
|
|
228
|
+
present `GroupActivitySharingController` in UIKit so the person can pick
|
|
229
|
+
contacts:
|
|
397
230
|
|
|
398
231
|
```swift
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
// New participant has no idea what the current state is
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// CORRECT -- send full state to new participants
|
|
405
|
-
func handleParticipants(_ participants: Set<Participant>) {
|
|
406
|
-
let newParticipants = participants.subtracting(knownParticipants)
|
|
407
|
-
for participant in newParticipants {
|
|
408
|
-
Task {
|
|
409
|
-
try await messenger?.send(currentState, to: .only(participant))
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
knownParticipants = participants
|
|
413
|
-
}
|
|
232
|
+
let picker = try GroupActivitySharingController(CookAlong(recipeID: recipe.id))
|
|
233
|
+
hostViewController.present(picker, animated: true)
|
|
414
234
|
```
|
|
415
235
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
try await messenger.send(imageData, to: .all) // Too large
|
|
422
|
-
// CORRECT -- journal attachments up to 100 MB; otherwise share a pointer/manifest
|
|
423
|
-
let journal = GroupSessionJournal(session: session)
|
|
424
|
-
try await journal.add(sharedImageItem)
|
|
425
|
-
// Larger/protected assets: server storage or app-managed file transfer
|
|
426
|
-
```
|
|
236
|
+
Custom controls use the `shareplay` SF Symbol. Write
|
|
237
|
+
`GroupActivityMetadata` as discovery copy: a short title, a subtitle and image
|
|
238
|
+
where the surface shows them, and a type that matches where the activity was
|
|
239
|
+
started. SwiftUI buttons, `ShareLink` and AirDrop are covered in
|
|
240
|
+
[the patterns file](references/shareplay-patterns.md).
|
|
427
241
|
|
|
428
|
-
|
|
242
|
+
Boundaries with neighbouring frameworks:
|
|
429
243
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
244
|
+
- GameKit: sign-in, matchmaking, leaderboards, achievements, voice chat.
|
|
245
|
+
- TabletopKit: seating, pieces on the board, where things sit in space, turn
|
|
246
|
+
order, game rules, and which device holds the true table state.
|
|
247
|
+
- AVKit: playback UI.
|
|
248
|
+
- SharePlay (this skill): invitations, session lifecycle, participants and the
|
|
249
|
+
hand-off points to the others.
|
|
436
250
|
|
|
437
|
-
|
|
438
|
-
player.playbackCoordinator.coordinateWithSession(session)
|
|
439
|
-
player.play() // Automatically synced to all participants
|
|
440
|
-
```
|
|
251
|
+
## GroupSessionJournal: sharing files
|
|
441
252
|
|
|
442
|
-
|
|
253
|
+
For bigger attachments that are not time-critical, use `GroupSessionJournal`
|
|
254
|
+
instead of the messenger. Items must be `Transferable`, attachments stay
|
|
255
|
+
available to people who join later, and each attachment may be up to 100 MB.
|
|
256
|
+
Available on iOS, iPadOS and tvOS 17+, macOS 14+ and visionOS 1+.
|
|
443
257
|
|
|
444
258
|
```swift
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
Text("Hello")
|
|
449
|
-
.task {
|
|
450
|
-
for await session in MyActivity.sessions() { }
|
|
451
|
-
}
|
|
452
|
-
}
|
|
259
|
+
func sharePhoto(_ photo: DishPhoto, in session: GroupSession<CookAlong>) async throws {
|
|
260
|
+
let shelf = GroupSessionJournal(session: session)
|
|
261
|
+
_ = try await shelf.add(photo)
|
|
453
262
|
}
|
|
454
263
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
for await session in MyActivity.sessions() {
|
|
461
|
-
configureSession(session)
|
|
462
|
-
}
|
|
264
|
+
func collectPhotos(from shelf: GroupSessionJournal) async {
|
|
265
|
+
for await batch in shelf.attachments {
|
|
266
|
+
for item in batch {
|
|
267
|
+
guard let bytes = try? await item.load(Data.self) else { continue }
|
|
268
|
+
Gallery.shared.insert(bytes, id: item.id)
|
|
463
269
|
}
|
|
464
270
|
}
|
|
465
271
|
}
|
|
466
272
|
```
|
|
467
273
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
-
|
|
474
|
-
|
|
475
|
-
-
|
|
476
|
-
|
|
477
|
-
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
-
|
|
274
|
+
Anything larger, or anything that needs access control, goes to server storage
|
|
275
|
+
or an app-managed transfer; share only a pointer or manifest through SharePlay.
|
|
276
|
+
|
|
277
|
+
## Common mistakes
|
|
278
|
+
|
|
279
|
+
- **No `join()`.** The session sits in `.waiting` forever. Join once the
|
|
280
|
+
messenger and observers are set up.
|
|
281
|
+
- **Never leaving.** Navigating away without `session?.leave()` leaks the
|
|
282
|
+
session. Leave on disappear and drop the session and messenger.
|
|
283
|
+
- **Assuming everyone has the same state.** A late joiner knows nothing.
|
|
284
|
+
Compare `activeParticipants` with the set you already know and send the full
|
|
285
|
+
state `to: .only(participant)` to each newcomer.
|
|
286
|
+
- **Pushing big payloads through the messenger.** A 300 KB `Data` blob already
|
|
287
|
+
exceeds its limit. Use the journal up to 100 MB, and server storage or your
|
|
288
|
+
own transfer beyond that.
|
|
289
|
+
- **Duplicating playback commands.** With `AVPlaybackCoordinator` in charge,
|
|
290
|
+
`player.play()` is already shared; extra play or pause messages fight it.
|
|
291
|
+
- **Listening to `sessions()` in a view's `.task`.** Each appearance starts
|
|
292
|
+
another listener. Observe from a long-lived manager, for example from its
|
|
293
|
+
`init`.
|
|
294
|
+
|
|
295
|
+
## Review checklist
|
|
296
|
+
|
|
297
|
+
- [ ] Only the app target carries the capability
|
|
298
|
+
- [ ] `GroupActivity` is `Codable` and its metadata is meaningful
|
|
299
|
+
- [ ] `Transferable` added where `ShareLink`, AirDrop or share sheets are used
|
|
300
|
+
- [ ] A long-lived object, never a view body, iterates `sessions()`
|
|
301
|
+
- [ ] `join()` called after the session is received and configured
|
|
302
|
+
- [ ] `leave()` called when the person navigates away or dismisses
|
|
303
|
+
- [ ] Messenger payloads under 256 KB, with a suitable `deliveryMode`
|
|
304
|
+
- [ ] Late joiners receive the current state
|
|
305
|
+
- [ ] Both `$state` and `$activeParticipants` are observed
|
|
306
|
+
- [ ] Journal used for `Transferable` attachments that are not time-sensitive
|
|
307
|
+
- [ ] `AVPlaybackCoordinator` handles media sync
|
|
308
|
+
- [ ] `isEligibleForGroupSession` checked before SharePlay UI is shown
|
|
309
|
+
- [ ] `GroupActivitySharingController` offered when no conversation is active
|
|
310
|
+
- [ ] Invalidation tears down messenger, journal and tasks
|
|
484
311
|
|
|
485
312
|
## References
|
|
486
313
|
|
|
487
|
-
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
- [
|
|
491
|
-
- [
|
|
492
|
-
- [
|
|
493
|
-
- [
|
|
494
|
-
- [
|
|
495
|
-
- [
|
|
496
|
-
- [
|
|
497
|
-
- [
|
|
498
|
-
-
|
|
499
|
-
-
|
|
500
|
-
- [
|
|
314
|
+
- [Pattern catalogue](references/shareplay-patterns.md): shared canvas,
|
|
315
|
+
generic manager, SwiftUI buttons and ShareLink, state sync for late joiners,
|
|
316
|
+
participant tracking, visionOS nearby
|
|
317
|
+
- Capability setup: [Group Activities in Xcode](https://developer.apple.com/documentation/xcode/configuring-group-activities)
|
|
318
|
+
- [GroupActivities](https://developer.apple.com/documentation/groupactivities)
|
|
319
|
+
- [GroupActivity](https://developer.apple.com/documentation/groupactivities/groupactivity)
|
|
320
|
+
- [GroupSession](https://developer.apple.com/documentation/groupactivities/groupsession)
|
|
321
|
+
- [GroupSessionMessenger](https://developer.apple.com/documentation/groupactivities/groupsessionmessenger)
|
|
322
|
+
- [GroupSessionJournal](https://developer.apple.com/documentation/groupactivities/groupsessionjournal)
|
|
323
|
+
- [GroupStateObserver](https://developer.apple.com/documentation/groupactivities/groupstateobserver)
|
|
324
|
+
- [GroupActivitySharingController](https://developer.apple.com/documentation/groupactivities/groupactivitysharingcontroller-ybcy)
|
|
325
|
+
- Article, activity definitions: [link](https://developer.apple.com/documentation/groupactivities/defining-your-apps-shareplay-activities)
|
|
326
|
+
- Article, starting activities from app UI: [link](https://developer.apple.com/documentation/groupactivities/promoting-shareplay-activities-from-your-apps-ui)
|
|
327
|
+
- Article, keeping data in sync: [link](https://developer.apple.com/documentation/groupactivities/synchronizing-data-during-a-shareplay-activity)
|
|
328
|
+
- Article, coordinated playback: [link](https://developer.apple.com/documentation/avfoundation/supporting-coordinated-media-playback)
|
|
329
|
+
- Design guidance: [SharePlay HIG page](https://developer.apple.com/design/human-interface-guidelines/shareplay)
|