@devo-bmad-custom/agent-orchestration 1.0.2 → 1.0.3
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/lib/installer.js +33 -0
- package/package.json +1 -1
- package/src/.agents/skills/audit-website/README.md +20 -20
- package/src/.agents/skills/audit-website/SKILL.md +470 -470
- package/src/.agents/skills/audit-website/agents/openai.yaml +6 -6
- package/src/.agents/skills/audit-website/assets/icon-small.svg +41 -41
- package/src/.agents/skills/audit-website/references/OUTPUT-FORMAT.md +250 -250
- package/src/.agents/skills/clean-code-standards/SKILL.md +104 -104
- package/src/.agents/skills/excalidraw-dark-standard/SKILL.md +281 -281
- package/src/.agents/skills/frontend-responsive-design-standards/SKILL.md +434 -434
- package/src/.agents/skills/java-fundamentals/SKILL.md +116 -116
- package/src/.agents/skills/java-performance/SKILL.md +119 -119
- package/src/.agents/skills/next-best-practices/SKILL.md +153 -153
- package/src/.agents/skills/next-best-practices/async-patterns.md +87 -87
- package/src/.agents/skills/next-best-practices/bundling.md +180 -180
- package/src/.agents/skills/next-best-practices/data-patterns.md +297 -297
- package/src/.agents/skills/next-best-practices/debug-tricks.md +105 -105
- package/src/.agents/skills/next-best-practices/directives.md +73 -73
- package/src/.agents/skills/next-best-practices/error-handling.md +227 -227
- package/src/.agents/skills/next-best-practices/file-conventions.md +140 -140
- package/src/.agents/skills/next-best-practices/font.md +245 -245
- package/src/.agents/skills/next-best-practices/functions.md +108 -108
- package/src/.agents/skills/next-best-practices/hydration-error.md +91 -91
- package/src/.agents/skills/next-best-practices/image.md +173 -173
- package/src/.agents/skills/next-best-practices/metadata.md +301 -301
- package/src/.agents/skills/next-best-practices/parallel-routes.md +287 -287
- package/src/.agents/skills/next-best-practices/route-handlers.md +146 -146
- package/src/.agents/skills/next-best-practices/rsc-boundaries.md +159 -159
- package/src/.agents/skills/next-best-practices/runtime-selection.md +39 -39
- package/src/.agents/skills/next-best-practices/scripts.md +141 -141
- package/src/.agents/skills/next-best-practices/self-hosting.md +371 -371
- package/src/.agents/skills/next-best-practices/suspense-boundaries.md +67 -67
- package/src/.agents/skills/nextjs-app-router-patterns/SKILL.md +537 -537
- package/src/.agents/skills/postgresql-optimization/SKILL.md +404 -404
- package/src/.agents/skills/python-backend/SKILL.md +153 -153
- package/src/.agents/skills/python-fundamentals/SKILL.md +234 -234
- package/src/.agents/skills/python-performance/SKILL.md +404 -404
- package/src/.agents/skills/react-expert/SKILL.md +335 -335
- package/src/.agents/skills/redis-best-practices/SKILL.md +438 -438
- package/src/.agents/skills/security-best-practices/SKILL.md +288 -288
- package/src/.agents/skills/security-review/LICENSE +22 -22
- package/src/.agents/skills/security-review/SKILL.md +312 -312
- package/src/.agents/skills/security-review/infrastructure/docker.md +432 -432
- package/src/.agents/skills/security-review/languages/javascript.md +388 -388
- package/src/.agents/skills/security-review/languages/python.md +363 -363
- package/src/.agents/skills/security-review/references/api-security.md +519 -519
- package/src/.agents/skills/security-review/references/authentication.md +353 -353
- package/src/.agents/skills/security-review/references/authorization.md +372 -372
- package/src/.agents/skills/security-review/references/business-logic.md +443 -443
- package/src/.agents/skills/security-review/references/cryptography.md +329 -329
- package/src/.agents/skills/security-review/references/csrf.md +398 -398
- package/src/.agents/skills/security-review/references/data-protection.md +378 -378
- package/src/.agents/skills/security-review/references/deserialization.md +410 -410
- package/src/.agents/skills/security-review/references/error-handling.md +436 -436
- package/src/.agents/skills/security-review/references/file-security.md +457 -457
- package/src/.agents/skills/security-review/references/injection.md +259 -259
- package/src/.agents/skills/security-review/references/logging.md +433 -433
- package/src/.agents/skills/security-review/references/misconfiguration.md +435 -435
- package/src/.agents/skills/security-review/references/modern-threats.md +475 -475
- package/src/.agents/skills/security-review/references/ssrf.md +415 -415
- package/src/.agents/skills/security-review/references/supply-chain.md +405 -405
- package/src/.agents/skills/security-review/references/xss.md +336 -336
- package/src/.agents/skills/subagent-driven-development/SKILL.md +275 -275
- package/src/.agents/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -26
- package/src/.agents/skills/subagent-driven-development/implementer-prompt.md +113 -113
- package/src/.agents/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -61
- package/src/.agents/skills/systematic-debugging/CREATION-LOG.md +119 -119
- package/src/.agents/skills/systematic-debugging/SKILL.md +296 -296
- package/src/.agents/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
- package/src/.agents/skills/systematic-debugging/condition-based-waiting.md +115 -115
- package/src/.agents/skills/systematic-debugging/defense-in-depth.md +122 -122
- package/src/.agents/skills/systematic-debugging/root-cause-tracing.md +169 -169
- package/src/.agents/skills/systematic-debugging/test-academic.md +14 -14
- package/src/.agents/skills/systematic-debugging/test-pressure-1.md +58 -58
- package/src/.agents/skills/systematic-debugging/test-pressure-2.md +68 -68
- package/src/.agents/skills/systematic-debugging/test-pressure-3.md +69 -69
- package/src/.agents/skills/typescript-best-practices/SKILL.md +373 -373
- package/src/.agents/skills/ui-ux-pro-custom/SKILL.md +348 -348
- package/src/.agents/skills/ui-ux-pro-custom/data/charts.csv +26 -26
- package/src/.agents/skills/ui-ux-pro-custom/data/colors.csv +97 -97
- package/src/.agents/skills/ui-ux-pro-custom/data/icons.csv +101 -101
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/SKILL.md +106 -106
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/accessibility.md +475 -475
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/animation.md +466 -466
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/composition-locals.md +231 -231
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/deprecated-patterns.md +323 -323
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/lists-scrolling.md +400 -400
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/modifiers.md +331 -331
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/navigation.md +416 -416
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/performance.md +446 -446
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/side-effects.md +516 -516
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/foundation-source.md +13327 -13327
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/material3-source.md +19097 -19097
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/navigation-source.md +2947 -2947
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/runtime-source.md +11316 -11316
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/ui-source.md +7896 -7896
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/state-management.md +377 -377
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/styles-experimental.md +470 -470
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/theming-material3.md +349 -349
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/view-composition.md +595 -595
- package/src/.agents/skills/ui-ux-pro-custom/data/landing.csv +31 -31
- package/src/.agents/skills/ui-ux-pro-custom/data/mobile-ui-layout.md +654 -654
- package/src/.agents/skills/ui-ux-pro-custom/data/products.csv +96 -96
- package/src/.agents/skills/ui-ux-pro-custom/data/react-performance.csv +45 -45
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/astro.csv +54 -54
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/flutter.csv +53 -53
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/html-tailwind.csv +56 -56
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/jetpack-compose.csv +53 -53
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/nextjs.csv +53 -53
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/nuxt-ui.csv +51 -51
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/nuxtjs.csv +59 -59
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/react-native.csv +56 -56
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/react.csv +54 -54
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/shadcn.csv +61 -61
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/svelte.csv +54 -54
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/swiftui.csv +51 -51
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/vue.csv +50 -50
- package/src/.agents/skills/ui-ux-pro-custom/data/styles.csv +68 -68
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/alarmkit/SKILL.md +438 -438
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/alarmkit/references/alarmkit-patterns.md +584 -584
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-clips/SKILL.md +436 -436
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-intents/SKILL.md +489 -489
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-intents/references/appintents-advanced.md +1076 -1076
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-store-review/SKILL.md +340 -340
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-store-review/references/privacy-manifest.md +90 -90
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-store-review/references/review-checklists.md +106 -106
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/coreml-conversion.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/coreml-optimization.md +344 -344
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/foundation-models.md +508 -508
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/mlx-swift.md +285 -285
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/authentication/SKILL.md +496 -496
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/authentication/references/keychain-biometric.md +211 -211
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/background-processing/SKILL.md +499 -499
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/background-processing/references/background-task-patterns.md +390 -390
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/callkit-voip/SKILL.md +461 -461
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/callkit-voip/references/callkit-patterns.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/cloudkit-sync/SKILL.md +492 -492
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/cloudkit-sync/references/cloudkit-patterns.md +461 -461
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/codable-patterns/SKILL.md +467 -467
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/contacts-framework/SKILL.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/contacts-framework/references/contacts-patterns.md +409 -409
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-bluetooth/SKILL.md +491 -491
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-bluetooth/references/ble-patterns.md +435 -435
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-motion/SKILL.md +388 -388
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-motion/references/motion-patterns.md +405 -405
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-nfc/SKILL.md +495 -495
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-nfc/references/nfc-patterns.md +420 -420
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/coreml/SKILL.md +459 -459
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/coreml/references/coreml-swift-integration.md +765 -765
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/debugging-instruments/SKILL.md +422 -422
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/debugging-instruments/references/instruments-guide.md +387 -387
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/debugging-instruments/references/lldb-patterns.md +298 -298
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/device-integrity/SKILL.md +477 -477
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/energykit/SKILL.md +460 -460
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/energykit/references/energykit-patterns.md +541 -541
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/eventkit-calendar/SKILL.md +483 -483
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/eventkit-calendar/references/eventkit-patterns.md +326 -326
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/healthkit/SKILL.md +498 -498
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/healthkit/references/healthkit-patterns.md +602 -602
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/homekit-matter/SKILL.md +496 -496
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/homekit-matter/references/matter-commissioning.md +455 -455
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-accessibility/SKILL.md +301 -301
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-accessibility/references/a11y-patterns.md +140 -140
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-localization/SKILL.md +418 -418
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-localization/references/formatstyle-locale.md +627 -627
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-localization/references/string-catalogs.md +462 -462
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/SKILL.md +441 -441
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/background-websocket.md +862 -862
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/lightweight-clients.md +93 -93
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/network-framework.md +563 -563
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/urlsession-patterns.md +1116 -1116
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/SKILL.md +496 -496
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/app-review-guidelines.md +174 -174
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/cryptokit-advanced.md +296 -296
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/file-storage-patterns.md +354 -354
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/privacy-manifest.md +117 -117
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/live-activities/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/live-activities/references/live-activity-patterns.md +868 -868
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/mapkit-location/SKILL.md +485 -485
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/mapkit-location/references/corelocation-patterns.md +730 -730
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/mapkit-location/references/mapkit-patterns.md +748 -748
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/metrickit-diagnostics/SKILL.md +479 -479
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/musickit-audio/SKILL.md +395 -395
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/musickit-audio/references/musickit-patterns.md +363 -363
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/natural-language/SKILL.md +412 -412
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/natural-language/references/translation-patterns.md +311 -311
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/passkit-wallet/SKILL.md +398 -398
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/passkit-wallet/references/wallet-passes.md +254 -254
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/pencilkit-drawing/SKILL.md +387 -387
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/pencilkit-drawing/references/paperkit-integration.md +376 -376
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/pencilkit-drawing/references/pencilkit-patterns.md +302 -302
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/permissionkit/SKILL.md +446 -446
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/permissionkit/references/permissionkit-patterns.md +435 -435
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/av-playback.md +701 -701
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/camera-capture.md +774 -774
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/image-loading-caching.md +869 -869
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/photospicker-patterns.md +597 -597
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/push-notifications/SKILL.md +500 -500
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/push-notifications/references/notification-patterns.md +677 -677
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/push-notifications/references/rich-notifications.md +745 -745
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/realitykit-ar/SKILL.md +479 -479
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/realitykit-ar/references/realitykit-patterns.md +480 -480
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/shareplay-activities/SKILL.md +483 -483
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/shareplay-activities/references/shareplay-patterns.md +544 -544
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/speech-recognition/SKILL.md +485 -485
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/storekit/SKILL.md +478 -478
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/storekit/references/app-review-guidelines.md +58 -58
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/storekit/references/storekit-advanced.md +755 -755
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-charts/SKILL.md +487 -487
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-charts/references/charts-patterns.md +895 -895
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/SKILL.md +408 -408
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/approachable-concurrency.md +80 -80
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/swift-6-2-concurrency.md +233 -233
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/swiftui-concurrency.md +187 -187
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/synchronization-primitives.md +341 -341
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-language/SKILL.md +498 -498
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-language/references/swift-patterns-extended.md +505 -505
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-testing/SKILL.md +467 -467
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-testing/references/testing-patterns.md +504 -504
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/SKILL.md +334 -334
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/references/core-data-coexistence.md +504 -504
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/references/swiftdata-advanced.md +975 -975
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/references/swiftdata-queries.md +675 -675
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-animation/SKILL.md +481 -481
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-animation/references/animation-advanced.md +804 -804
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-animation/references/core-animation-bridge.md +553 -553
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-gestures/SKILL.md +450 -450
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-gestures/references/gesture-patterns.md +425 -425
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/SKILL.md +336 -336
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/form.md +97 -97
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/grids.md +69 -69
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/list.md +99 -99
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/scrollview.md +147 -147
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-liquid-glass/SKILL.md +325 -325
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-liquid-glass/references/liquid-glass.md +387 -387
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/SKILL.md +262 -262
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/deeplinks.md +207 -207
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/navigationstack.md +177 -177
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/sheets.md +169 -169
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/tabview.md +178 -178
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/SKILL.md +381 -381
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/architecture-patterns.md +486 -486
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/deprecated-migration.md +1097 -1097
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/design-polish.md +780 -780
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/platform-and-sharing.md +696 -696
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/SKILL.md +491 -491
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +46 -46
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +29 -29
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/understanding-hangs-in-your-app.md +33 -33
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/understanding-improving-swiftui-performance.md +52 -52
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-uikit-interop/SKILL.md +428 -428
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-uikit-interop/references/hosting-migration.md +534 -534
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-uikit-interop/references/representable-recipes.md +1133 -1133
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/tipkit/SKILL.md +494 -494
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/tipkit/references/tipkit-patterns.md +782 -782
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/SKILL.md +475 -475
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/references/vision-requests.md +736 -736
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/references/visionkit-scanner.md +738 -738
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/weatherkit/SKILL.md +410 -410
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/weatherkit/references/weatherkit-patterns.md +567 -567
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/widgetkit/SKILL.md +497 -497
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/widgetkit/references/widgetkit-advanced.md +871 -871
- package/src/.agents/skills/ui-ux-pro-custom/data/typography.csv +57 -57
- package/src/.agents/skills/ui-ux-pro-custom/data/ui-reasoning.csv +101 -101
- package/src/.agents/skills/ui-ux-pro-custom/data/ux-guidelines.csv +99 -99
- package/src/.agents/skills/ui-ux-pro-custom/data/web-interface.csv +31 -31
- package/src/.agents/skills/ui-ux-pro-custom/scripts/core.py +253 -253
- package/src/.agents/skills/ui-ux-pro-custom/scripts/design_system.py +1067 -1067
- package/src/.agents/skills/ui-ux-pro-custom/scripts/search.py +114 -114
- package/src/.agents/skills/ux-audit/SKILL.md +150 -150
- package/src/.agents/skills/websocket-engineer/SKILL.md +168 -168
- package/src/.agents/skills/websocket-engineer/references/alternatives.md +391 -391
- package/src/.agents/skills/websocket-engineer/references/patterns.md +400 -400
- package/src/.agents/skills/websocket-engineer/references/protocol.md +195 -195
- package/src/.agents/skills/websocket-engineer/references/scaling.md +333 -333
- package/src/.agents/skills/websocket-engineer/references/security.md +474 -474
- package/src/.agents/skills/writing-skills/SKILL.md +655 -655
- package/src/.agents/skills/writing-skills/anthropic-best-practices.md +1150 -1150
- package/src/.agents/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -189
- package/src/.agents/skills/writing-skills/graphviz-conventions.dot +171 -171
- package/src/.agents/skills/writing-skills/persuasion-principles.md +187 -187
- package/src/.agents/skills/writing-skills/render-graphs.js +168 -168
- package/src/.agents/skills/writing-skills/testing-skills-with-subagents.md +384 -384
- package/src/.claude/commands/master-orchestrator.md +15 -0
- package/src/_memory/config.yaml +11 -11
- package/src/_memory/master-orchestrator-sidecar/instructions.md +85 -32
- package/src/_memory/skills/nimbalyst-tracking/SKILL.md +103 -103
- package/src/_memory/skills/writing-skills/SKILL.md +655 -655
- package/src/bmb/agents/agent-builder.md +59 -59
- package/src/bmb/agents/module-builder.md +60 -60
- package/src/bmb/agents/workflow-builder.md +61 -61
- package/src/bmb/config.yaml +12 -12
- package/src/bmb/module-help.csv +13 -13
- package/src/bmb/workflows/agent/data/agent-architecture.md +258 -258
- package/src/bmb/workflows/agent/data/agent-compilation.md +185 -185
- package/src/bmb/workflows/agent/data/agent-menu-patterns.md +189 -189
- package/src/bmb/workflows/agent/data/agent-metadata.md +133 -133
- package/src/bmb/workflows/agent/data/agent-validation.md +111 -111
- package/src/bmb/workflows/agent/data/brainstorm-context.md +96 -96
- package/src/bmb/workflows/agent/data/communication-presets.csv +61 -61
- package/src/bmb/workflows/agent/data/critical-actions.md +75 -75
- package/src/bmb/workflows/agent/data/persona-properties.md +252 -252
- package/src/bmb/workflows/agent/data/principles-crafting.md +142 -142
- package/src/bmb/workflows/agent/data/reference/module-examples/architect.md +68 -68
- package/src/bmb/workflows/agent/data/reference/with-sidecar/journal-keeper/journal-keeper-sidecar/entries/yy-mm-dd-entry-template.md +16 -16
- package/src/bmb/workflows/agent/data/understanding-agent-types.md +126 -126
- package/src/bmb/workflows/agent/steps-c/step-01-brainstorm.md +129 -129
- package/src/bmb/workflows/agent/steps-c/step-02-discovery.md +170 -170
- package/src/bmb/workflows/agent/steps-c/step-03-sidecar-metadata.md +309 -309
- package/src/bmb/workflows/agent/steps-c/step-04-persona.md +213 -213
- package/src/bmb/workflows/agent/steps-c/step-05-commands-menu.md +179 -179
- package/src/bmb/workflows/agent/steps-c/step-06-activation.md +278 -278
- package/src/bmb/workflows/agent/steps-c/step-07-build-agent.md +316 -316
- package/src/bmb/workflows/agent/steps-c/step-08-celebrate.md +247 -247
- package/src/bmb/workflows/agent/steps-e/e-01-load-existing.md +221 -221
- package/src/bmb/workflows/agent/steps-e/e-02-discover-edits.md +195 -195
- package/src/bmb/workflows/agent/steps-e/e-04-sidecar-metadata.md +126 -126
- package/src/bmb/workflows/agent/steps-e/e-05-persona.md +135 -135
- package/src/bmb/workflows/agent/steps-e/e-06-commands-menu.md +123 -123
- package/src/bmb/workflows/agent/steps-e/e-07-activation.md +124 -124
- package/src/bmb/workflows/agent/steps-e/e-08-edit-agent.md +197 -197
- package/src/bmb/workflows/agent/steps-e/e-09-celebrate.md +155 -155
- package/src/bmb/workflows/agent/steps-v/v-01-load-review.md +137 -137
- package/src/bmb/workflows/agent/steps-v/v-02a-validate-metadata.md +116 -116
- package/src/bmb/workflows/agent/steps-v/v-02b-validate-persona.md +124 -124
- package/src/bmb/workflows/agent/steps-v/v-02c-validate-menu.md +127 -127
- package/src/bmb/workflows/agent/steps-v/v-02d-validate-structure.md +134 -134
- package/src/bmb/workflows/agent/steps-v/v-02e-validate-sidecar.md +134 -134
- package/src/bmb/workflows/agent/steps-v/v-03-summary.md +104 -104
- package/src/bmb/workflows/agent/templates/agent-plan.template.md +5 -5
- package/src/bmb/workflows/agent/templates/agent-template.md +89 -89
- package/src/bmb/workflows/agent/workflow-create-agent.md +72 -72
- package/src/bmb/workflows/agent/workflow-edit-agent.md +75 -75
- package/src/bmb/workflows/agent/workflow-validate-agent.md +73 -73
- package/src/bmb/workflows/module/data/agent-architecture.md +179 -179
- package/src/bmb/workflows/module/data/agent-spec-template.md +79 -79
- package/src/bmb/workflows/module/data/module-standards.md +263 -263
- package/src/bmb/workflows/module/data/module-yaml-conventions.md +392 -392
- package/src/bmb/workflows/module/module-help-generate.md +254 -254
- package/src/bmb/workflows/module/steps-b/step-01-welcome.md +148 -148
- package/src/bmb/workflows/module/steps-b/step-02-spark.md +141 -141
- package/src/bmb/workflows/module/steps-b/step-03-module-type.md +149 -149
- package/src/bmb/workflows/module/steps-b/step-04-vision.md +83 -83
- package/src/bmb/workflows/module/steps-b/step-05-identity.md +97 -97
- package/src/bmb/workflows/module/steps-b/step-06-users.md +86 -86
- package/src/bmb/workflows/module/steps-b/step-07-value.md +76 -76
- package/src/bmb/workflows/module/steps-b/step-08-agents.md +97 -97
- package/src/bmb/workflows/module/steps-b/step-09-workflows.md +83 -83
- package/src/bmb/workflows/module/steps-b/step-10-tools.md +91 -91
- package/src/bmb/workflows/module/steps-b/step-11-scenarios.md +84 -84
- package/src/bmb/workflows/module/steps-b/step-12-creative.md +95 -95
- package/src/bmb/workflows/module/steps-b/step-13-review.md +105 -105
- package/src/bmb/workflows/module/steps-b/step-14-finalize.md +117 -117
- package/src/bmb/workflows/module/steps-c/step-01-load-brief.md +179 -179
- package/src/bmb/workflows/module/steps-c/step-01b-continue.md +82 -82
- package/src/bmb/workflows/module/steps-c/step-02-structure.md +105 -105
- package/src/bmb/workflows/module/steps-c/step-03-config.md +119 -119
- package/src/bmb/workflows/module/steps-c/step-04-agents.md +168 -168
- package/src/bmb/workflows/module/steps-c/step-05-workflows.md +184 -184
- package/src/bmb/workflows/module/steps-c/step-06-docs.md +401 -401
- package/src/bmb/workflows/module/steps-c/step-07-complete.md +152 -152
- package/src/bmb/workflows/module/steps-e/step-01-load-target.md +81 -81
- package/src/bmb/workflows/module/steps-e/step-02-select-edit.md +77 -77
- package/src/bmb/workflows/module/steps-e/step-03-apply-edit.md +77 -77
- package/src/bmb/workflows/module/steps-e/step-04-review.md +80 -80
- package/src/bmb/workflows/module/steps-e/step-05-confirm.md +75 -75
- package/src/bmb/workflows/module/steps-v/step-01-load-target.md +96 -96
- package/src/bmb/workflows/module/steps-v/step-02-file-structure.md +93 -93
- package/src/bmb/workflows/module/steps-v/step-03-module-yaml.md +99 -99
- package/src/bmb/workflows/module/steps-v/step-04-agent-specs.md +152 -152
- package/src/bmb/workflows/module/steps-v/step-05-workflow-specs.md +152 -152
- package/src/bmb/workflows/module/steps-v/step-06-documentation.md +143 -143
- package/src/bmb/workflows/module/steps-v/step-07-installation.md +102 -102
- package/src/bmb/workflows/module/steps-v/step-08-report.md +197 -197
- package/src/bmb/workflows/module/templates/brief-template.md +154 -154
- package/src/bmb/workflows/module/templates/workflow-spec-template.md +96 -96
- package/src/bmb/workflows/module/workflow-create-module-brief.md +71 -71
- package/src/bmb/workflows/module/workflow-create-module.md +86 -86
- package/src/bmb/workflows/module/workflow-edit-module.md +66 -66
- package/src/bmb/workflows/module/workflow-validate-module.md +66 -66
- package/src/bmb/workflows/workflow/data/architecture.md +150 -150
- package/src/bmb/workflows/workflow/data/common-workflow-tools.csv +19 -19
- package/src/bmb/workflows/workflow/data/csv-data-file-standards.md +53 -53
- package/src/bmb/workflows/workflow/data/frontmatter-standards.md +184 -184
- package/src/bmb/workflows/workflow/data/input-discovery-standards.md +191 -191
- package/src/bmb/workflows/workflow/data/intent-vs-prescriptive-spectrum.md +44 -44
- package/src/bmb/workflows/workflow/data/menu-handling-standards.md +133 -133
- package/src/bmb/workflows/workflow/data/output-format-standards.md +135 -135
- package/src/bmb/workflows/workflow/data/step-file-rules.md +235 -235
- package/src/bmb/workflows/workflow/data/step-type-patterns.md +257 -257
- package/src/bmb/workflows/workflow/data/subprocess-optimization-patterns.md +188 -188
- package/src/bmb/workflows/workflow/data/trimodal-workflow-structure.md +164 -164
- package/src/bmb/workflows/workflow/data/workflow-chaining-standards.md +222 -222
- package/src/bmb/workflows/workflow/data/workflow-examples.md +232 -232
- package/src/bmb/workflows/workflow/data/workflow-type-criteria.md +134 -134
- package/src/bmb/workflows/workflow/steps-c/step-00-conversion.md +263 -263
- package/src/bmb/workflows/workflow/steps-c/step-01-discovery.md +194 -194
- package/src/bmb/workflows/workflow/steps-c/step-01b-continuation.md +3 -3
- package/src/bmb/workflows/workflow/steps-c/step-02-classification.md +270 -270
- package/src/bmb/workflows/workflow/steps-c/step-03-requirements.md +283 -283
- package/src/bmb/workflows/workflow/steps-c/step-04-tools.md +282 -282
- package/src/bmb/workflows/workflow/steps-c/step-05-plan-review.md +243 -243
- package/src/bmb/workflows/workflow/steps-c/step-06-design.md +330 -330
- package/src/bmb/workflows/workflow/steps-c/step-07-foundation.md +239 -239
- package/src/bmb/workflows/workflow/steps-c/step-08-build-step-01.md +379 -379
- package/src/bmb/workflows/workflow/steps-c/step-09-build-next-step.md +350 -350
- package/src/bmb/workflows/workflow/steps-c/step-10-confirmation.md +322 -322
- package/src/bmb/workflows/workflow/steps-c/step-11-completion.md +191 -191
- package/src/bmb/workflows/workflow/steps-e/step-e-01-assess-workflow.md +237 -237
- package/src/bmb/workflows/workflow/steps-e/step-e-02-discover-edits.md +251 -251
- package/src/bmb/workflows/workflow/steps-e/step-e-03-fix-validation.md +254 -254
- package/src/bmb/workflows/workflow/steps-e/step-e-04-direct-edit.md +277 -277
- package/src/bmb/workflows/workflow/steps-e/step-e-05-apply-edit.md +154 -154
- package/src/bmb/workflows/workflow/steps-e/step-e-06-validate-after.md +190 -190
- package/src/bmb/workflows/workflow/steps-e/step-e-07-complete.md +206 -206
- package/src/bmb/workflows/workflow/steps-v/step-01-validate-max-mode.md +109 -109
- package/src/bmb/workflows/workflow/steps-v/step-01-validate.md +221 -221
- package/src/bmb/workflows/workflow/steps-v/step-01b-structure.md +152 -152
- package/src/bmb/workflows/workflow/steps-v/step-02-frontmatter-validation.md +199 -199
- package/src/bmb/workflows/workflow/steps-v/step-02b-path-violations.md +265 -265
- package/src/bmb/workflows/workflow/steps-v/step-03-menu-validation.md +164 -164
- package/src/bmb/workflows/workflow/steps-v/step-04-step-type-validation.md +211 -211
- package/src/bmb/workflows/workflow/steps-v/step-05-output-format-validation.md +200 -200
- package/src/bmb/workflows/workflow/steps-v/step-06-validation-design-check.md +195 -195
- package/src/bmb/workflows/workflow/steps-v/step-07-instruction-style-check.md +209 -209
- package/src/bmb/workflows/workflow/steps-v/step-08-collaborative-experience-check.md +199 -199
- package/src/bmb/workflows/workflow/steps-v/step-08b-subprocess-optimization.md +179 -179
- package/src/bmb/workflows/workflow/steps-v/step-09-cohesive-review.md +186 -186
- package/src/bmb/workflows/workflow/steps-v/step-10-report-complete.md +154 -154
- package/src/bmb/workflows/workflow/steps-v/step-11-plan-validation.md +237 -237
- package/src/bmb/workflows/workflow/templates/minimal-output-template.md +11 -11
- package/src/bmb/workflows/workflow/templates/step-01-init-continuable-template.md +241 -241
- package/src/bmb/workflows/workflow/templates/step-1b-template.md +224 -224
- package/src/bmb/workflows/workflow/templates/step-template.md +294 -294
- package/src/bmb/workflows/workflow/templates/workflow-template.md +102 -102
- package/src/bmb/workflows/workflow/workflow-create-workflow.md +79 -79
- package/src/bmb/workflows/workflow/workflow-edit-workflow.md +65 -65
- package/src/bmb/workflows/workflow/workflow-rework-workflow.md +65 -65
- package/src/bmb/workflows/workflow/workflow-validate-max-parallel-workflow.md +66 -66
- package/src/bmb/workflows/workflow/workflow-validate-workflow.md +65 -65
- package/src/bmm/agents/analyst.md +104 -104
- package/src/bmm/agents/dev.md +100 -100
- package/src/bmm/agents/qa.md +100 -90
- package/src/bmm/agents/tech-writer/tech-writer.md +94 -94
- package/src/bmm/module-help.csv +31 -31
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-01-init.md +115 -115
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-01b-continue.md +107 -107
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-02-vision.md +141 -141
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-03-users.md +144 -144
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-04-metrics.md +147 -147
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-05-scope.md +161 -161
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-06-complete.md +99 -99
- package/src/bmm/workflows/1-analysis/create-product-brief/workflow.md +57 -57
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-01-init.md +87 -87
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-02-domain-analysis.md +156 -156
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-03-competitive-landscape.md +165 -165
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-04-regulatory-focus.md +140 -140
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-05-technical-trends.md +152 -152
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-06-research-synthesis.md +345 -345
- package/src/bmm/workflows/1-analysis/research/market-steps/step-01-init.md +92 -92
- package/src/bmm/workflows/1-analysis/research/market-steps/step-02-customer-behavior.md +164 -164
- package/src/bmm/workflows/1-analysis/research/market-steps/step-03-customer-pain-points.md +174 -174
- package/src/bmm/workflows/1-analysis/research/market-steps/step-04-customer-decisions.md +184 -184
- package/src/bmm/workflows/1-analysis/research/market-steps/step-05-competitive-analysis.md +105 -105
- package/src/bmm/workflows/1-analysis/research/market-steps/step-06-research-completion.md +360 -360
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-01-init.md +87 -87
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-02-technical-overview.md +165 -165
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-03-integration-patterns.md +174 -174
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-04-architectural-patterns.md +141 -141
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-05-implementation-research.md +159 -159
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-06-research-synthesis.md +387 -387
- package/src/bmm/workflows/1-analysis/research/workflow-domain-research.md +54 -54
- package/src/bmm/workflows/1-analysis/research/workflow-market-research.md +54 -54
- package/src/bmm/workflows/1-analysis/research/workflow-technical-research.md +54 -54
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-01b-continue.md +100 -100
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02-discovery.md +160 -160
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02b-vision.md +88 -88
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02c-executive-summary.md +99 -99
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-03-success.md +169 -169
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-04-journeys.md +156 -156
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-05-domain.md +136 -136
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-06-innovation.md +176 -176
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-07-project-type.md +184 -184
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-08-scoping.md +174 -174
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-09-functional.md +175 -175
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-10-nonfunctional.md +189 -189
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-11-polish.md +162 -162
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-12-complete.md +79 -79
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01-discovery.md +183 -183
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01b-legacy-conversion.md +149 -149
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-02-review.md +187 -187
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-03-edit.md +192 -192
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-04-complete.md +108 -108
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-01-discovery.md +166 -166
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02-format-detection.md +131 -131
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02b-parity-check.md +150 -150
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-03-density-validation.md +118 -118
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-04-brief-coverage-validation.md +155 -155
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-05-measurability-validation.md +170 -170
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-06-traceability-validation.md +158 -158
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-07-implementation-leakage-validation.md +147 -147
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-08-domain-compliance-validation.md +182 -182
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-09-project-type-validation.md +202 -202
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-10-smart-validation.md +148 -148
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-11-holistic-quality-validation.md +201 -201
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-12-completeness-validation.md +179 -179
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-13-report-complete.md +164 -164
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md +65 -65
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md +65 -65
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md +63 -63
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01b-continue.md +63 -63
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-02-discovery.md +106 -106
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-03-core-experience.md +111 -111
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-04-emotional-response.md +115 -115
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-05-inspiration.md +127 -127
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-06-design-system.md +167 -167
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-07-defining-experience.md +143 -143
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-08-visual-foundation.md +118 -118
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-09-design-directions.md +154 -154
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-10-user-journeys.md +136 -136
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-11-component-strategy.md +165 -165
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-12-ux-patterns.md +135 -135
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-13-responsive-accessibility.md +192 -192
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-14-complete.md +101 -101
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +45 -45
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-01-document-discovery.md +185 -185
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-02-prd-analysis.md +129 -129
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-03-epic-coverage-validation.md +130 -130
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-04-ux-alignment.md +93 -93
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-05-epic-quality-review.md +196 -196
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-06-final-assessment.md +129 -129
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md +54 -54
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-01b-continue.md +82 -82
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-02-context.md +106 -106
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-03-starter.md +138 -138
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md +129 -129
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-05-patterns.md +166 -166
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-06-structure.md +186 -186
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-07-validation.md +163 -163
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-08-complete.md +38 -38
- package/src/bmm/workflows/3-solutioning/create-architecture/workflow.md +49 -49
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-02-design-epics.md +124 -124
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-03-create-stories.md +122 -122
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-04-final-validation.md +84 -84
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +58 -58
- package/src/bmm/workflows/4-implementation/code-review/workflow.yaml +43 -43
- package/src/bmm/workflows/4-implementation/correct-course/workflow.yaml +53 -53
- package/src/bmm/workflows/4-implementation/create-story/checklist.md +159 -159
- package/src/bmm/workflows/4-implementation/create-story/template.md +79 -79
- package/src/bmm/workflows/4-implementation/create-story/workflow.yaml +52 -52
- package/src/bmm/workflows/4-implementation/dev-story/workflow.yaml +20 -20
- package/src/bmm/workflows/4-implementation/retrospective/workflow.yaml +52 -52
- package/src/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +52 -52
- package/src/bmm/workflows/4-implementation/sprint-status/workflow.yaml +25 -25
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-01-mode-detection.md +158 -158
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-02-context-gathering.md +122 -122
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-03-execute.md +93 -93
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-04-self-check.md +93 -93
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-05-adversarial-review.md +87 -87
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-06-resolve-findings.md +146 -146
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md +50 -50
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-02-investigate.md +152 -152
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-03-generate.md +123 -123
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-04-review.md +201 -201
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md +79 -79
- package/src/bmm/workflows/document-project/workflow.yaml +22 -22
- package/src/bmm/workflows/generate-project-context/steps/step-01-discover.md +184 -184
- package/src/bmm/workflows/generate-project-context/steps/step-02-generate.md +322 -322
- package/src/bmm/workflows/generate-project-context/steps/step-03-complete.md +235 -235
- package/src/bmm/workflows/generate-project-context/workflow.md +49 -49
- package/src/bmm/workflows/qa/automate/workflow.yaml +233 -233
- package/src/bmm/workflows/qa-generate-e2e-tests/workflow.yaml +42 -42
- package/src/core/config.yaml +9 -9
- package/src/core/module-help.csv +10 -10
- package/src/core/scripts/generate-loop-report.py +72 -72
- package/src/core/tasks/editorial-review-prose.xml +101 -101
- package/src/core/tasks/editorial-review-structure.xml +207 -207
- package/src/core/tasks/help.md +86 -86
- package/src/core/tasks/index-docs.xml +64 -64
- package/src/core/tasks/review-adversarial-general.xml +66 -66
- package/src/core/tasks/review-adversarial-loop.xml +46 -46
- package/src/core/tasks/review-edge-case-hunter.xml +63 -63
- package/src/core/tasks/review-party-loop.xml +46 -46
- package/src/core/tasks/shard-doc.xml +107 -107
- package/src/core/tasks/workflow.xml +235 -235
- package/src/core/templates/review-loop-report.html +88 -88
- package/src/core/templates/review-loop-report.md +5 -5
- package/src/core/workflows/advanced-elicitation/workflow.xml +117 -117
- package/src/core/workflows/brainstorming/steps/step-01-session-setup.md +212 -212
- package/src/core/workflows/brainstorming/steps/step-01b-continue.md +122 -122
- package/src/core/workflows/brainstorming/steps/step-02a-user-selected.md +225 -225
- package/src/core/workflows/brainstorming/steps/step-02b-ai-recommended.md +237 -237
- package/src/core/workflows/brainstorming/steps/step-02c-random-selection.md +209 -209
- package/src/core/workflows/brainstorming/steps/step-02d-progressive-flow.md +264 -264
- package/src/core/workflows/brainstorming/steps/step-02e-deep-dive.md +68 -68
- package/src/core/workflows/brainstorming/steps/step-03-technique-execution.md +403 -403
- package/src/core/workflows/brainstorming/steps/step-04-idea-organization.md +303 -303
- package/src/core/workflows/brainstorming/workflow.md +60 -60
- package/src/core/workflows/extract-trackers/workflow.md +45 -45
- package/src/core/workflows/party-mode/steps/step-01-agent-loading.md +142 -142
- package/src/core/workflows/party-mode/workflow.md +194 -194
- package/src/docs/dev/tmux/actions_popup.py +291 -291
- package/src/docs/dev/tmux/tmux-setup.md +62 -1
|
@@ -1,461 +1,461 @@
|
|
|
1
|
-
# CloudKit Patterns
|
|
2
|
-
|
|
3
|
-
Advanced CloudKit patterns including incremental sync with
|
|
4
|
-
CKFetchRecordZoneChangesOperation, sharing with CKShare, record zone
|
|
5
|
-
management, CKAsset file storage, batch operations, and CloudKit Dashboard.
|
|
6
|
-
|
|
7
|
-
## Contents
|
|
8
|
-
|
|
9
|
-
- [CKFetchRecordZoneChangesOperation (Incremental Sync)](#ckfetchrecordzonechangesoperation-incremental-sync)
|
|
10
|
-
- [Server Change Token Management](#server-change-token-management)
|
|
11
|
-
- [CKShare and Collaboration](#ckshare-and-collaboration)
|
|
12
|
-
- [UICloudSharingController](#uicloudsharingcontroller)
|
|
13
|
-
- [Record Zone Management](#record-zone-management)
|
|
14
|
-
- [CKAsset File Storage](#ckasset-file-storage)
|
|
15
|
-
- [Batch Operations](#batch-operations)
|
|
16
|
-
- [Operation Queues and QoS](#operation-queues-and-qos)
|
|
17
|
-
- [Encrypted Fields](#encrypted-fields)
|
|
18
|
-
- [CloudKit Dashboard](#cloudkit-dashboard)
|
|
19
|
-
- [CKFetchDatabaseChangesOperation](#ckfetchdatabasechangesoperation)
|
|
20
|
-
|
|
21
|
-
## CKFetchRecordZoneChangesOperation (Incremental Sync)
|
|
22
|
-
|
|
23
|
-
Fetches only records that changed since the last sync. Works with private and
|
|
24
|
-
shared databases only. Provide a server change token per zone; use `nil` for
|
|
25
|
-
the initial fetch.
|
|
26
|
-
|
|
27
|
-
```swift
|
|
28
|
-
import CloudKit
|
|
29
|
-
|
|
30
|
-
final class IncrementalSyncManager {
|
|
31
|
-
private let database: CKDatabase
|
|
32
|
-
private var changeTokens: [CKRecordZone.ID: CKServerChangeToken] = [:]
|
|
33
|
-
private let tokenCacheURL: URL
|
|
34
|
-
|
|
35
|
-
init(database: CKDatabase, cacheDirectory: URL) {
|
|
36
|
-
self.database = database
|
|
37
|
-
self.tokenCacheURL = cacheDirectory.appendingPathComponent("changeTokens.data")
|
|
38
|
-
loadTokens()
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
func fetchChanges(in zoneIDs: [CKRecordZone.ID]) async throws {
|
|
42
|
-
var configs: [CKRecordZone.ID: CKFetchRecordZoneChangesOperation.ZoneConfiguration] = [:]
|
|
43
|
-
for zoneID in zoneIDs {
|
|
44
|
-
let config = CKFetchRecordZoneChangesOperation.ZoneConfiguration()
|
|
45
|
-
config.previousServerChangeToken = changeTokens[zoneID]
|
|
46
|
-
configs[zoneID] = config
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
let operation = CKFetchRecordZoneChangesOperation(
|
|
50
|
-
recordZoneIDs: zoneIDs,
|
|
51
|
-
configurationsByRecordZoneID: configs
|
|
52
|
-
)
|
|
53
|
-
operation.fetchAllChanges = true
|
|
54
|
-
|
|
55
|
-
// Process changed records
|
|
56
|
-
operation.recordWasChangedBlock = { recordID, result in
|
|
57
|
-
switch result {
|
|
58
|
-
case .success(let record):
|
|
59
|
-
self.processChangedRecord(record)
|
|
60
|
-
case .failure(let error):
|
|
61
|
-
self.handleRecordError(recordID, error: error)
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Process deleted records
|
|
66
|
-
operation.recordWithIDWasDeletedBlock = { recordID, recordType in
|
|
67
|
-
self.processDeletedRecord(recordID, type: recordType)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Update change token as zones complete
|
|
71
|
-
operation.recordZoneFetchResultBlock = { zoneID, result in
|
|
72
|
-
switch result {
|
|
73
|
-
case .success(let (serverChangeToken, _, _)):
|
|
74
|
-
self.changeTokens[zoneID] = serverChangeToken
|
|
75
|
-
self.saveTokens()
|
|
76
|
-
case .failure(let error):
|
|
77
|
-
if let ckError = error as? CKError,
|
|
78
|
-
ckError.code == .changeTokenExpired {
|
|
79
|
-
// Clear token and refetch from scratch
|
|
80
|
-
self.changeTokens[zoneID] = nil
|
|
81
|
-
self.saveTokens()
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
operation.fetchRecordZoneChangesResultBlock = { result in
|
|
87
|
-
if case .failure(let error) = result {
|
|
88
|
-
print("Fetch zone changes failed: \(error)")
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
operation.qualityOfService = .utility
|
|
93
|
-
database.add(operation)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// MARK: - Token Persistence
|
|
97
|
-
|
|
98
|
-
private func loadTokens() {
|
|
99
|
-
guard let data = try? Data(contentsOf: tokenCacheURL),
|
|
100
|
-
let tokens = try? NSKeyedUnarchiver.unarchivedObject(
|
|
101
|
-
ofClasses: [NSDictionary.self, CKRecordZone.ID.self,
|
|
102
|
-
CKServerChangeToken.self],
|
|
103
|
-
from: data) as? [CKRecordZone.ID: CKServerChangeToken]
|
|
104
|
-
else { return }
|
|
105
|
-
changeTokens = tokens
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
private func saveTokens() {
|
|
109
|
-
guard let data = try? NSKeyedArchiver.archivedData(
|
|
110
|
-
withRootObject: changeTokens, requiringSecureCoding: true)
|
|
111
|
-
else { return }
|
|
112
|
-
try? data.write(to: tokenCacheURL)
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
## Server Change Token Management
|
|
118
|
-
|
|
119
|
-
Change tokens are opaque pointers to a point in a zone's change history. Rules:
|
|
120
|
-
|
|
121
|
-
- Tokens conform to `NSSecureCoding` -- safe to persist to disk.
|
|
122
|
-
- Zone change tokens are NOT interchangeable with database change tokens.
|
|
123
|
-
- A `.changeTokenExpired` error means the token is too old; reset to `nil` and
|
|
124
|
-
refetch all changes.
|
|
125
|
-
- On `recordZoneFetchResultBlock`, cache the returned token immediately.
|
|
126
|
-
|
|
127
|
-
```swift
|
|
128
|
-
// Persist token alongside zone ID
|
|
129
|
-
func cacheToken(_ token: CKServerChangeToken?, for zoneID: CKRecordZone.ID) {
|
|
130
|
-
guard let token else {
|
|
131
|
-
UserDefaults.standard.removeObject(forKey: "token-\(zoneID.zoneName)")
|
|
132
|
-
return
|
|
133
|
-
}
|
|
134
|
-
let data = try? NSKeyedArchiver.archivedData(
|
|
135
|
-
withRootObject: token, requiringSecureCoding: true)
|
|
136
|
-
UserDefaults.standard.set(data, forKey: "token-\(zoneID.zoneName)")
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
func cachedToken(for zoneID: CKRecordZone.ID) -> CKServerChangeToken? {
|
|
140
|
-
guard let data = UserDefaults.standard.data(forKey: "token-\(zoneID.zoneName)")
|
|
141
|
-
else { return nil }
|
|
142
|
-
return try? NSKeyedUnarchiver.unarchivedObject(
|
|
143
|
-
ofClass: CKServerChangeToken.self, from: data)
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
## CKShare and Collaboration
|
|
148
|
-
|
|
149
|
-
CKShare manages shared access to records or entire record zones. Limit: 100
|
|
150
|
-
participants per share. Available iOS 10+.
|
|
151
|
-
|
|
152
|
-
### Hierarchy-Based Sharing
|
|
153
|
-
|
|
154
|
-
Share a root record and its children (linked via `parent` references).
|
|
155
|
-
|
|
156
|
-
```swift
|
|
157
|
-
import CloudKit
|
|
158
|
-
|
|
159
|
-
// Create a share for a root record
|
|
160
|
-
let rootRecord = CKRecord(recordType: "Album", recordID: albumRecordID)
|
|
161
|
-
let share = CKShare(rootRecord: rootRecord)
|
|
162
|
-
share.publicPermission = .readOnly
|
|
163
|
-
|
|
164
|
-
// Customize share appearance
|
|
165
|
-
share[CKShare.SystemFieldKey.title] = "Vacation Photos" as CKRecordValue
|
|
166
|
-
share[CKShare.SystemFieldKey.shareType] = "com.example.album" as CKRecordValue
|
|
167
|
-
|
|
168
|
-
// Save share and root record together
|
|
169
|
-
let operation = CKModifyRecordsOperation(
|
|
170
|
-
recordsToSave: [rootRecord, share],
|
|
171
|
-
recordIDsToDelete: nil
|
|
172
|
-
)
|
|
173
|
-
operation.modifyRecordsResultBlock = { result in
|
|
174
|
-
switch result {
|
|
175
|
-
case .success:
|
|
176
|
-
print("Share URL: \(share.url?.absoluteString ?? "nil")")
|
|
177
|
-
case .failure(let error):
|
|
178
|
-
print("Sharing failed: \(error)")
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
privateDB.add(operation)
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
### Zone-Wide Sharing
|
|
185
|
-
|
|
186
|
-
Share all records in a custom zone.
|
|
187
|
-
|
|
188
|
-
```swift
|
|
189
|
-
let zoneID = CKRecordZone.ID(zoneName: "SharedAlbums")
|
|
190
|
-
let share = CKShare(recordZoneID: zoneID)
|
|
191
|
-
share.publicPermission = .readWrite
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
### Adding Participants
|
|
195
|
-
|
|
196
|
-
```swift
|
|
197
|
-
// Look up participants by email
|
|
198
|
-
let lookupInfo = CKUserIdentity.LookupInfo(emailAddress: "friend@example.com")
|
|
199
|
-
let participants = try await container.shareParticipants(
|
|
200
|
-
forEmailAddresses: ["friend@example.com"])
|
|
201
|
-
|
|
202
|
-
for participant in participants {
|
|
203
|
-
participant.permission = .readWrite
|
|
204
|
-
share.addParticipant(participant)
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Save the updated share
|
|
208
|
-
try await privateDB.save(share)
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### Accepting a Share
|
|
212
|
-
|
|
213
|
-
```swift
|
|
214
|
-
// In AppDelegate or SceneDelegate
|
|
215
|
-
func userDidAcceptCloudKitShare(with metadata: CKShare.Metadata) {
|
|
216
|
-
let container = CKContainer(identifier: metadata.containerIdentifier)
|
|
217
|
-
Task {
|
|
218
|
-
do {
|
|
219
|
-
let share = try await container.accept(metadata)
|
|
220
|
-
print("Accepted share: \(share.url?.absoluteString ?? "")")
|
|
221
|
-
// Fetch shared records from container.sharedCloudDatabase
|
|
222
|
-
} catch {
|
|
223
|
-
print("Accept failed: \(error)")
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
**Required**: add `CKSharingSupported = YES` to Info.plist so the system can
|
|
230
|
-
launch your app from share URLs.
|
|
231
|
-
|
|
232
|
-
## UICloudSharingController
|
|
233
|
-
|
|
234
|
-
Present the system sharing UI (iOS only).
|
|
235
|
-
|
|
236
|
-
```swift
|
|
237
|
-
import UIKit
|
|
238
|
-
import CloudKit
|
|
239
|
-
|
|
240
|
-
func presentSharingUI(for share: CKShare, container: CKContainer,
|
|
241
|
-
from viewController: UIViewController) {
|
|
242
|
-
let sharingController = UICloudSharingController(share: share, container: container)
|
|
243
|
-
sharingController.delegate = self
|
|
244
|
-
sharingController.availablePermissions = [.allowReadOnly, .allowReadWrite, .allowPrivate]
|
|
245
|
-
viewController.present(sharingController, animated: true)
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// UICloudSharingControllerDelegate
|
|
249
|
-
extension MyClass: UICloudSharingControllerDelegate {
|
|
250
|
-
func cloudSharingController(
|
|
251
|
-
_ csc: UICloudSharingController,
|
|
252
|
-
failedToSaveShareWithError error: Error
|
|
253
|
-
) {
|
|
254
|
-
print("Save share error: \(error)")
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
func itemTitle(for csc: UICloudSharingController) -> String? {
|
|
258
|
-
return "My Shared Album"
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
## Record Zone Management
|
|
264
|
-
|
|
265
|
-
Custom record zones in the private database enable atomic commits, change
|
|
266
|
-
tracking with tokens, and record sharing.
|
|
267
|
-
|
|
268
|
-
```swift
|
|
269
|
-
// Create
|
|
270
|
-
let zoneID = CKRecordZone.ID(zoneName: "NotesZone", ownerName: CKCurrentUserDefaultName)
|
|
271
|
-
let zone = CKRecordZone(zoneID: zoneID)
|
|
272
|
-
try await privateDB.save(zone)
|
|
273
|
-
|
|
274
|
-
// Fetch all zones
|
|
275
|
-
let zones = try await privateDB.allRecordZones()
|
|
276
|
-
|
|
277
|
-
// Delete
|
|
278
|
-
try await privateDB.deleteRecordZone(withID: zoneID)
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
**Note**: the default zone does not support custom change tokens or atomic
|
|
282
|
-
operations. Always use custom zones for sync.
|
|
283
|
-
|
|
284
|
-
## CKAsset File Storage
|
|
285
|
-
|
|
286
|
-
Use CKAsset for files, images, and binary data larger than a few KB. Assets do
|
|
287
|
-
not count toward the 1 MB record limit.
|
|
288
|
-
|
|
289
|
-
```swift
|
|
290
|
-
// Save an image as a CKAsset
|
|
291
|
-
let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent("photo.jpg")
|
|
292
|
-
try imageData.write(to: tempURL)
|
|
293
|
-
|
|
294
|
-
let record = CKRecord(recordType: "Photo")
|
|
295
|
-
record["image"] = CKAsset(fileURL: tempURL)
|
|
296
|
-
record["caption"] = "Sunset at the beach" as CKRecordValue
|
|
297
|
-
try await privateDB.save(record)
|
|
298
|
-
|
|
299
|
-
// Fetch and read the asset
|
|
300
|
-
let fetched = try await privateDB.record(for: record.recordID)
|
|
301
|
-
if let asset = fetched["image"] as? CKAsset,
|
|
302
|
-
let fileURL = asset.fileURL {
|
|
303
|
-
let data = try Data(contentsOf: fileURL)
|
|
304
|
-
// Move to app container immediately -- staging area is temporary
|
|
305
|
-
let permanentURL = documentsDir.appendingPathComponent("photo.jpg")
|
|
306
|
-
try FileManager.default.moveItem(at: fileURL, to: permanentURL)
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// Remove an asset (orphan it)
|
|
310
|
-
record["image"] = nil
|
|
311
|
-
try await privateDB.save(record)
|
|
312
|
-
// CloudKit periodically deletes orphaned assets from the server
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
**Exclude assets from fetch when not needed** using `desiredKeys` on operations
|
|
316
|
-
to save bandwidth.
|
|
317
|
-
|
|
318
|
-
## Batch Operations
|
|
319
|
-
|
|
320
|
-
Use `CKModifyRecordsOperation` for atomic saves and deletes.
|
|
321
|
-
|
|
322
|
-
```swift
|
|
323
|
-
let recordsToSave: [CKRecord] = [record1, record2, record3]
|
|
324
|
-
let idsToDelete: [CKRecord.ID] = [oldRecordID]
|
|
325
|
-
|
|
326
|
-
let operation = CKModifyRecordsOperation(
|
|
327
|
-
recordsToSave: recordsToSave,
|
|
328
|
-
recordIDsToDelete: idsToDelete
|
|
329
|
-
)
|
|
330
|
-
operation.savePolicy = .changedKeys // Only upload modified fields
|
|
331
|
-
operation.isAtomic = true // All or nothing (custom zones only)
|
|
332
|
-
|
|
333
|
-
operation.perRecordSaveBlock = { recordID, result in
|
|
334
|
-
switch result {
|
|
335
|
-
case .success(let record): print("Saved: \(recordID)")
|
|
336
|
-
case .failure(let error): print("Failed: \(recordID) \(error)")
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
operation.perRecordDeleteBlock = { recordID, result in
|
|
341
|
-
switch result {
|
|
342
|
-
case .success: print("Deleted: \(recordID)")
|
|
343
|
-
case .failure(let error): print("Delete failed: \(recordID) \(error)")
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
operation.modifyRecordsResultBlock = { result in
|
|
348
|
-
if case .failure(let error) = result {
|
|
349
|
-
print("Batch failed: \(error)")
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// CloudKit limits: 400 records per operation
|
|
354
|
-
operation.qualityOfService = .userInitiated
|
|
355
|
-
privateDB.add(operation)
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
**Max 400 records per operation.** For larger batches, split into chunks.
|
|
359
|
-
|
|
360
|
-
## Operation Queues and QoS
|
|
361
|
-
|
|
362
|
-
Set appropriate quality of service:
|
|
363
|
-
|
|
364
|
-
| Scenario | QoS |
|
|
365
|
-
|----------|-----|
|
|
366
|
-
| User triggered action | `.userInitiated` |
|
|
367
|
-
| Background sync | `.utility` |
|
|
368
|
-
| Pre-fetch / maintenance | `.background` |
|
|
369
|
-
|
|
370
|
-
```swift
|
|
371
|
-
// Use operation groups for related operations
|
|
372
|
-
let group = CKOperationGroup()
|
|
373
|
-
group.expectedSendSize = .kilobytes
|
|
374
|
-
group.expectedReceiveSize = .megabytes
|
|
375
|
-
|
|
376
|
-
let config = CKOperation.Configuration()
|
|
377
|
-
config.qualityOfService = .utility
|
|
378
|
-
config.group = group
|
|
379
|
-
|
|
380
|
-
let operation = CKQueryOperation(query: query)
|
|
381
|
-
operation.configuration = config
|
|
382
|
-
database.add(operation)
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
## Encrypted Fields
|
|
386
|
-
|
|
387
|
-
Use `encryptedValues` (iOS 15+) for sensitive data. Encrypted fields are not
|
|
388
|
-
searchable or sortable.
|
|
389
|
-
|
|
390
|
-
```swift
|
|
391
|
-
let record = CKRecord(recordType: "HealthEntry")
|
|
392
|
-
record.encryptedValues["heartRate"] = 72 as CKRecordValue
|
|
393
|
-
record.encryptedValues["notes"] = "Resting" as CKRecordValue
|
|
394
|
-
// Non-sensitive fields remain in plain text
|
|
395
|
-
record["date"] = Date() as CKRecordValue
|
|
396
|
-
|
|
397
|
-
try await privateDB.save(record)
|
|
398
|
-
|
|
399
|
-
// Read encrypted values
|
|
400
|
-
let fetched = try await privateDB.record(for: record.recordID)
|
|
401
|
-
let heartRate = fetched.encryptedValues["heartRate"] as? Int
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
## CloudKit Dashboard
|
|
405
|
-
|
|
406
|
-
Access at [iCloud Dashboard](https://icloud.developer.apple.com/dashboard).
|
|
407
|
-
Key capabilities:
|
|
408
|
-
|
|
409
|
-
- **Schema**: view/edit record types, fields, indexes. Add indexes only for
|
|
410
|
-
queried fields in production.
|
|
411
|
-
- **Records**: browse, create, edit, delete records in any database.
|
|
412
|
-
- **Subscriptions**: view active subscriptions.
|
|
413
|
-
- **Logs**: monitor API calls, errors, and latency.
|
|
414
|
-
- **Telemetry**: track request counts, error rates, latency percentiles.
|
|
415
|
-
- **Environment toggle**: switch between Development and Production. Simulator
|
|
416
|
-
only works with Development.
|
|
417
|
-
- **Deploy to Production**: migrate schema changes from dev to prod. Production
|
|
418
|
-
does not allow adding new record types or fields programmatically.
|
|
419
|
-
|
|
420
|
-
## CKFetchDatabaseChangesOperation
|
|
421
|
-
|
|
422
|
-
Discover which zones changed in a database. Use with shared database where you
|
|
423
|
-
do not know zone IDs in advance.
|
|
424
|
-
|
|
425
|
-
```swift
|
|
426
|
-
var dbChangeToken: CKServerChangeToken? = loadDatabaseChangeToken()
|
|
427
|
-
|
|
428
|
-
let operation = CKFetchDatabaseChangesOperation(
|
|
429
|
-
previousServerChangeToken: dbChangeToken
|
|
430
|
-
)
|
|
431
|
-
operation.fetchAllChanges = true
|
|
432
|
-
|
|
433
|
-
var changedZoneIDs: [CKRecordZone.ID] = []
|
|
434
|
-
var deletedZoneIDs: [CKRecordZone.ID] = []
|
|
435
|
-
|
|
436
|
-
operation.recordZoneWithIDChangedBlock = { zoneID in
|
|
437
|
-
changedZoneIDs.append(zoneID)
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
operation.recordZoneWithIDWasDeletedBlock = { zoneID in
|
|
441
|
-
deletedZoneIDs.append(zoneID)
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
operation.fetchDatabaseChangesResultBlock = { result in
|
|
445
|
-
switch result {
|
|
446
|
-
case .success(let (token, _)):
|
|
447
|
-
dbChangeToken = token
|
|
448
|
-
saveDatabaseChangeToken(token)
|
|
449
|
-
// Now fetch zone changes for changedZoneIDs
|
|
450
|
-
case .failure(let error):
|
|
451
|
-
if let ckError = error as? CKError,
|
|
452
|
-
ckError.code == .changeTokenExpired {
|
|
453
|
-
dbChangeToken = nil
|
|
454
|
-
// Refetch from scratch
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
operation.qualityOfService = .utility
|
|
460
|
-
sharedDB.add(operation)
|
|
461
|
-
```
|
|
1
|
+
# CloudKit Patterns
|
|
2
|
+
|
|
3
|
+
Advanced CloudKit patterns including incremental sync with
|
|
4
|
+
CKFetchRecordZoneChangesOperation, sharing with CKShare, record zone
|
|
5
|
+
management, CKAsset file storage, batch operations, and CloudKit Dashboard.
|
|
6
|
+
|
|
7
|
+
## Contents
|
|
8
|
+
|
|
9
|
+
- [CKFetchRecordZoneChangesOperation (Incremental Sync)](#ckfetchrecordzonechangesoperation-incremental-sync)
|
|
10
|
+
- [Server Change Token Management](#server-change-token-management)
|
|
11
|
+
- [CKShare and Collaboration](#ckshare-and-collaboration)
|
|
12
|
+
- [UICloudSharingController](#uicloudsharingcontroller)
|
|
13
|
+
- [Record Zone Management](#record-zone-management)
|
|
14
|
+
- [CKAsset File Storage](#ckasset-file-storage)
|
|
15
|
+
- [Batch Operations](#batch-operations)
|
|
16
|
+
- [Operation Queues and QoS](#operation-queues-and-qos)
|
|
17
|
+
- [Encrypted Fields](#encrypted-fields)
|
|
18
|
+
- [CloudKit Dashboard](#cloudkit-dashboard)
|
|
19
|
+
- [CKFetchDatabaseChangesOperation](#ckfetchdatabasechangesoperation)
|
|
20
|
+
|
|
21
|
+
## CKFetchRecordZoneChangesOperation (Incremental Sync)
|
|
22
|
+
|
|
23
|
+
Fetches only records that changed since the last sync. Works with private and
|
|
24
|
+
shared databases only. Provide a server change token per zone; use `nil` for
|
|
25
|
+
the initial fetch.
|
|
26
|
+
|
|
27
|
+
```swift
|
|
28
|
+
import CloudKit
|
|
29
|
+
|
|
30
|
+
final class IncrementalSyncManager {
|
|
31
|
+
private let database: CKDatabase
|
|
32
|
+
private var changeTokens: [CKRecordZone.ID: CKServerChangeToken] = [:]
|
|
33
|
+
private let tokenCacheURL: URL
|
|
34
|
+
|
|
35
|
+
init(database: CKDatabase, cacheDirectory: URL) {
|
|
36
|
+
self.database = database
|
|
37
|
+
self.tokenCacheURL = cacheDirectory.appendingPathComponent("changeTokens.data")
|
|
38
|
+
loadTokens()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
func fetchChanges(in zoneIDs: [CKRecordZone.ID]) async throws {
|
|
42
|
+
var configs: [CKRecordZone.ID: CKFetchRecordZoneChangesOperation.ZoneConfiguration] = [:]
|
|
43
|
+
for zoneID in zoneIDs {
|
|
44
|
+
let config = CKFetchRecordZoneChangesOperation.ZoneConfiguration()
|
|
45
|
+
config.previousServerChangeToken = changeTokens[zoneID]
|
|
46
|
+
configs[zoneID] = config
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
let operation = CKFetchRecordZoneChangesOperation(
|
|
50
|
+
recordZoneIDs: zoneIDs,
|
|
51
|
+
configurationsByRecordZoneID: configs
|
|
52
|
+
)
|
|
53
|
+
operation.fetchAllChanges = true
|
|
54
|
+
|
|
55
|
+
// Process changed records
|
|
56
|
+
operation.recordWasChangedBlock = { recordID, result in
|
|
57
|
+
switch result {
|
|
58
|
+
case .success(let record):
|
|
59
|
+
self.processChangedRecord(record)
|
|
60
|
+
case .failure(let error):
|
|
61
|
+
self.handleRecordError(recordID, error: error)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Process deleted records
|
|
66
|
+
operation.recordWithIDWasDeletedBlock = { recordID, recordType in
|
|
67
|
+
self.processDeletedRecord(recordID, type: recordType)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Update change token as zones complete
|
|
71
|
+
operation.recordZoneFetchResultBlock = { zoneID, result in
|
|
72
|
+
switch result {
|
|
73
|
+
case .success(let (serverChangeToken, _, _)):
|
|
74
|
+
self.changeTokens[zoneID] = serverChangeToken
|
|
75
|
+
self.saveTokens()
|
|
76
|
+
case .failure(let error):
|
|
77
|
+
if let ckError = error as? CKError,
|
|
78
|
+
ckError.code == .changeTokenExpired {
|
|
79
|
+
// Clear token and refetch from scratch
|
|
80
|
+
self.changeTokens[zoneID] = nil
|
|
81
|
+
self.saveTokens()
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
operation.fetchRecordZoneChangesResultBlock = { result in
|
|
87
|
+
if case .failure(let error) = result {
|
|
88
|
+
print("Fetch zone changes failed: \(error)")
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
operation.qualityOfService = .utility
|
|
93
|
+
database.add(operation)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// MARK: - Token Persistence
|
|
97
|
+
|
|
98
|
+
private func loadTokens() {
|
|
99
|
+
guard let data = try? Data(contentsOf: tokenCacheURL),
|
|
100
|
+
let tokens = try? NSKeyedUnarchiver.unarchivedObject(
|
|
101
|
+
ofClasses: [NSDictionary.self, CKRecordZone.ID.self,
|
|
102
|
+
CKServerChangeToken.self],
|
|
103
|
+
from: data) as? [CKRecordZone.ID: CKServerChangeToken]
|
|
104
|
+
else { return }
|
|
105
|
+
changeTokens = tokens
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private func saveTokens() {
|
|
109
|
+
guard let data = try? NSKeyedArchiver.archivedData(
|
|
110
|
+
withRootObject: changeTokens, requiringSecureCoding: true)
|
|
111
|
+
else { return }
|
|
112
|
+
try? data.write(to: tokenCacheURL)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Server Change Token Management
|
|
118
|
+
|
|
119
|
+
Change tokens are opaque pointers to a point in a zone's change history. Rules:
|
|
120
|
+
|
|
121
|
+
- Tokens conform to `NSSecureCoding` -- safe to persist to disk.
|
|
122
|
+
- Zone change tokens are NOT interchangeable with database change tokens.
|
|
123
|
+
- A `.changeTokenExpired` error means the token is too old; reset to `nil` and
|
|
124
|
+
refetch all changes.
|
|
125
|
+
- On `recordZoneFetchResultBlock`, cache the returned token immediately.
|
|
126
|
+
|
|
127
|
+
```swift
|
|
128
|
+
// Persist token alongside zone ID
|
|
129
|
+
func cacheToken(_ token: CKServerChangeToken?, for zoneID: CKRecordZone.ID) {
|
|
130
|
+
guard let token else {
|
|
131
|
+
UserDefaults.standard.removeObject(forKey: "token-\(zoneID.zoneName)")
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
let data = try? NSKeyedArchiver.archivedData(
|
|
135
|
+
withRootObject: token, requiringSecureCoding: true)
|
|
136
|
+
UserDefaults.standard.set(data, forKey: "token-\(zoneID.zoneName)")
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
func cachedToken(for zoneID: CKRecordZone.ID) -> CKServerChangeToken? {
|
|
140
|
+
guard let data = UserDefaults.standard.data(forKey: "token-\(zoneID.zoneName)")
|
|
141
|
+
else { return nil }
|
|
142
|
+
return try? NSKeyedUnarchiver.unarchivedObject(
|
|
143
|
+
ofClass: CKServerChangeToken.self, from: data)
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## CKShare and Collaboration
|
|
148
|
+
|
|
149
|
+
CKShare manages shared access to records or entire record zones. Limit: 100
|
|
150
|
+
participants per share. Available iOS 10+.
|
|
151
|
+
|
|
152
|
+
### Hierarchy-Based Sharing
|
|
153
|
+
|
|
154
|
+
Share a root record and its children (linked via `parent` references).
|
|
155
|
+
|
|
156
|
+
```swift
|
|
157
|
+
import CloudKit
|
|
158
|
+
|
|
159
|
+
// Create a share for a root record
|
|
160
|
+
let rootRecord = CKRecord(recordType: "Album", recordID: albumRecordID)
|
|
161
|
+
let share = CKShare(rootRecord: rootRecord)
|
|
162
|
+
share.publicPermission = .readOnly
|
|
163
|
+
|
|
164
|
+
// Customize share appearance
|
|
165
|
+
share[CKShare.SystemFieldKey.title] = "Vacation Photos" as CKRecordValue
|
|
166
|
+
share[CKShare.SystemFieldKey.shareType] = "com.example.album" as CKRecordValue
|
|
167
|
+
|
|
168
|
+
// Save share and root record together
|
|
169
|
+
let operation = CKModifyRecordsOperation(
|
|
170
|
+
recordsToSave: [rootRecord, share],
|
|
171
|
+
recordIDsToDelete: nil
|
|
172
|
+
)
|
|
173
|
+
operation.modifyRecordsResultBlock = { result in
|
|
174
|
+
switch result {
|
|
175
|
+
case .success:
|
|
176
|
+
print("Share URL: \(share.url?.absoluteString ?? "nil")")
|
|
177
|
+
case .failure(let error):
|
|
178
|
+
print("Sharing failed: \(error)")
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
privateDB.add(operation)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Zone-Wide Sharing
|
|
185
|
+
|
|
186
|
+
Share all records in a custom zone.
|
|
187
|
+
|
|
188
|
+
```swift
|
|
189
|
+
let zoneID = CKRecordZone.ID(zoneName: "SharedAlbums")
|
|
190
|
+
let share = CKShare(recordZoneID: zoneID)
|
|
191
|
+
share.publicPermission = .readWrite
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Adding Participants
|
|
195
|
+
|
|
196
|
+
```swift
|
|
197
|
+
// Look up participants by email
|
|
198
|
+
let lookupInfo = CKUserIdentity.LookupInfo(emailAddress: "friend@example.com")
|
|
199
|
+
let participants = try await container.shareParticipants(
|
|
200
|
+
forEmailAddresses: ["friend@example.com"])
|
|
201
|
+
|
|
202
|
+
for participant in participants {
|
|
203
|
+
participant.permission = .readWrite
|
|
204
|
+
share.addParticipant(participant)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Save the updated share
|
|
208
|
+
try await privateDB.save(share)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Accepting a Share
|
|
212
|
+
|
|
213
|
+
```swift
|
|
214
|
+
// In AppDelegate or SceneDelegate
|
|
215
|
+
func userDidAcceptCloudKitShare(with metadata: CKShare.Metadata) {
|
|
216
|
+
let container = CKContainer(identifier: metadata.containerIdentifier)
|
|
217
|
+
Task {
|
|
218
|
+
do {
|
|
219
|
+
let share = try await container.accept(metadata)
|
|
220
|
+
print("Accepted share: \(share.url?.absoluteString ?? "")")
|
|
221
|
+
// Fetch shared records from container.sharedCloudDatabase
|
|
222
|
+
} catch {
|
|
223
|
+
print("Accept failed: \(error)")
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**Required**: add `CKSharingSupported = YES` to Info.plist so the system can
|
|
230
|
+
launch your app from share URLs.
|
|
231
|
+
|
|
232
|
+
## UICloudSharingController
|
|
233
|
+
|
|
234
|
+
Present the system sharing UI (iOS only).
|
|
235
|
+
|
|
236
|
+
```swift
|
|
237
|
+
import UIKit
|
|
238
|
+
import CloudKit
|
|
239
|
+
|
|
240
|
+
func presentSharingUI(for share: CKShare, container: CKContainer,
|
|
241
|
+
from viewController: UIViewController) {
|
|
242
|
+
let sharingController = UICloudSharingController(share: share, container: container)
|
|
243
|
+
sharingController.delegate = self
|
|
244
|
+
sharingController.availablePermissions = [.allowReadOnly, .allowReadWrite, .allowPrivate]
|
|
245
|
+
viewController.present(sharingController, animated: true)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// UICloudSharingControllerDelegate
|
|
249
|
+
extension MyClass: UICloudSharingControllerDelegate {
|
|
250
|
+
func cloudSharingController(
|
|
251
|
+
_ csc: UICloudSharingController,
|
|
252
|
+
failedToSaveShareWithError error: Error
|
|
253
|
+
) {
|
|
254
|
+
print("Save share error: \(error)")
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
func itemTitle(for csc: UICloudSharingController) -> String? {
|
|
258
|
+
return "My Shared Album"
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Record Zone Management
|
|
264
|
+
|
|
265
|
+
Custom record zones in the private database enable atomic commits, change
|
|
266
|
+
tracking with tokens, and record sharing.
|
|
267
|
+
|
|
268
|
+
```swift
|
|
269
|
+
// Create
|
|
270
|
+
let zoneID = CKRecordZone.ID(zoneName: "NotesZone", ownerName: CKCurrentUserDefaultName)
|
|
271
|
+
let zone = CKRecordZone(zoneID: zoneID)
|
|
272
|
+
try await privateDB.save(zone)
|
|
273
|
+
|
|
274
|
+
// Fetch all zones
|
|
275
|
+
let zones = try await privateDB.allRecordZones()
|
|
276
|
+
|
|
277
|
+
// Delete
|
|
278
|
+
try await privateDB.deleteRecordZone(withID: zoneID)
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**Note**: the default zone does not support custom change tokens or atomic
|
|
282
|
+
operations. Always use custom zones for sync.
|
|
283
|
+
|
|
284
|
+
## CKAsset File Storage
|
|
285
|
+
|
|
286
|
+
Use CKAsset for files, images, and binary data larger than a few KB. Assets do
|
|
287
|
+
not count toward the 1 MB record limit.
|
|
288
|
+
|
|
289
|
+
```swift
|
|
290
|
+
// Save an image as a CKAsset
|
|
291
|
+
let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent("photo.jpg")
|
|
292
|
+
try imageData.write(to: tempURL)
|
|
293
|
+
|
|
294
|
+
let record = CKRecord(recordType: "Photo")
|
|
295
|
+
record["image"] = CKAsset(fileURL: tempURL)
|
|
296
|
+
record["caption"] = "Sunset at the beach" as CKRecordValue
|
|
297
|
+
try await privateDB.save(record)
|
|
298
|
+
|
|
299
|
+
// Fetch and read the asset
|
|
300
|
+
let fetched = try await privateDB.record(for: record.recordID)
|
|
301
|
+
if let asset = fetched["image"] as? CKAsset,
|
|
302
|
+
let fileURL = asset.fileURL {
|
|
303
|
+
let data = try Data(contentsOf: fileURL)
|
|
304
|
+
// Move to app container immediately -- staging area is temporary
|
|
305
|
+
let permanentURL = documentsDir.appendingPathComponent("photo.jpg")
|
|
306
|
+
try FileManager.default.moveItem(at: fileURL, to: permanentURL)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Remove an asset (orphan it)
|
|
310
|
+
record["image"] = nil
|
|
311
|
+
try await privateDB.save(record)
|
|
312
|
+
// CloudKit periodically deletes orphaned assets from the server
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
**Exclude assets from fetch when not needed** using `desiredKeys` on operations
|
|
316
|
+
to save bandwidth.
|
|
317
|
+
|
|
318
|
+
## Batch Operations
|
|
319
|
+
|
|
320
|
+
Use `CKModifyRecordsOperation` for atomic saves and deletes.
|
|
321
|
+
|
|
322
|
+
```swift
|
|
323
|
+
let recordsToSave: [CKRecord] = [record1, record2, record3]
|
|
324
|
+
let idsToDelete: [CKRecord.ID] = [oldRecordID]
|
|
325
|
+
|
|
326
|
+
let operation = CKModifyRecordsOperation(
|
|
327
|
+
recordsToSave: recordsToSave,
|
|
328
|
+
recordIDsToDelete: idsToDelete
|
|
329
|
+
)
|
|
330
|
+
operation.savePolicy = .changedKeys // Only upload modified fields
|
|
331
|
+
operation.isAtomic = true // All or nothing (custom zones only)
|
|
332
|
+
|
|
333
|
+
operation.perRecordSaveBlock = { recordID, result in
|
|
334
|
+
switch result {
|
|
335
|
+
case .success(let record): print("Saved: \(recordID)")
|
|
336
|
+
case .failure(let error): print("Failed: \(recordID) \(error)")
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
operation.perRecordDeleteBlock = { recordID, result in
|
|
341
|
+
switch result {
|
|
342
|
+
case .success: print("Deleted: \(recordID)")
|
|
343
|
+
case .failure(let error): print("Delete failed: \(recordID) \(error)")
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
operation.modifyRecordsResultBlock = { result in
|
|
348
|
+
if case .failure(let error) = result {
|
|
349
|
+
print("Batch failed: \(error)")
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// CloudKit limits: 400 records per operation
|
|
354
|
+
operation.qualityOfService = .userInitiated
|
|
355
|
+
privateDB.add(operation)
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
**Max 400 records per operation.** For larger batches, split into chunks.
|
|
359
|
+
|
|
360
|
+
## Operation Queues and QoS
|
|
361
|
+
|
|
362
|
+
Set appropriate quality of service:
|
|
363
|
+
|
|
364
|
+
| Scenario | QoS |
|
|
365
|
+
|----------|-----|
|
|
366
|
+
| User triggered action | `.userInitiated` |
|
|
367
|
+
| Background sync | `.utility` |
|
|
368
|
+
| Pre-fetch / maintenance | `.background` |
|
|
369
|
+
|
|
370
|
+
```swift
|
|
371
|
+
// Use operation groups for related operations
|
|
372
|
+
let group = CKOperationGroup()
|
|
373
|
+
group.expectedSendSize = .kilobytes
|
|
374
|
+
group.expectedReceiveSize = .megabytes
|
|
375
|
+
|
|
376
|
+
let config = CKOperation.Configuration()
|
|
377
|
+
config.qualityOfService = .utility
|
|
378
|
+
config.group = group
|
|
379
|
+
|
|
380
|
+
let operation = CKQueryOperation(query: query)
|
|
381
|
+
operation.configuration = config
|
|
382
|
+
database.add(operation)
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## Encrypted Fields
|
|
386
|
+
|
|
387
|
+
Use `encryptedValues` (iOS 15+) for sensitive data. Encrypted fields are not
|
|
388
|
+
searchable or sortable.
|
|
389
|
+
|
|
390
|
+
```swift
|
|
391
|
+
let record = CKRecord(recordType: "HealthEntry")
|
|
392
|
+
record.encryptedValues["heartRate"] = 72 as CKRecordValue
|
|
393
|
+
record.encryptedValues["notes"] = "Resting" as CKRecordValue
|
|
394
|
+
// Non-sensitive fields remain in plain text
|
|
395
|
+
record["date"] = Date() as CKRecordValue
|
|
396
|
+
|
|
397
|
+
try await privateDB.save(record)
|
|
398
|
+
|
|
399
|
+
// Read encrypted values
|
|
400
|
+
let fetched = try await privateDB.record(for: record.recordID)
|
|
401
|
+
let heartRate = fetched.encryptedValues["heartRate"] as? Int
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
## CloudKit Dashboard
|
|
405
|
+
|
|
406
|
+
Access at [iCloud Dashboard](https://icloud.developer.apple.com/dashboard).
|
|
407
|
+
Key capabilities:
|
|
408
|
+
|
|
409
|
+
- **Schema**: view/edit record types, fields, indexes. Add indexes only for
|
|
410
|
+
queried fields in production.
|
|
411
|
+
- **Records**: browse, create, edit, delete records in any database.
|
|
412
|
+
- **Subscriptions**: view active subscriptions.
|
|
413
|
+
- **Logs**: monitor API calls, errors, and latency.
|
|
414
|
+
- **Telemetry**: track request counts, error rates, latency percentiles.
|
|
415
|
+
- **Environment toggle**: switch between Development and Production. Simulator
|
|
416
|
+
only works with Development.
|
|
417
|
+
- **Deploy to Production**: migrate schema changes from dev to prod. Production
|
|
418
|
+
does not allow adding new record types or fields programmatically.
|
|
419
|
+
|
|
420
|
+
## CKFetchDatabaseChangesOperation
|
|
421
|
+
|
|
422
|
+
Discover which zones changed in a database. Use with shared database where you
|
|
423
|
+
do not know zone IDs in advance.
|
|
424
|
+
|
|
425
|
+
```swift
|
|
426
|
+
var dbChangeToken: CKServerChangeToken? = loadDatabaseChangeToken()
|
|
427
|
+
|
|
428
|
+
let operation = CKFetchDatabaseChangesOperation(
|
|
429
|
+
previousServerChangeToken: dbChangeToken
|
|
430
|
+
)
|
|
431
|
+
operation.fetchAllChanges = true
|
|
432
|
+
|
|
433
|
+
var changedZoneIDs: [CKRecordZone.ID] = []
|
|
434
|
+
var deletedZoneIDs: [CKRecordZone.ID] = []
|
|
435
|
+
|
|
436
|
+
operation.recordZoneWithIDChangedBlock = { zoneID in
|
|
437
|
+
changedZoneIDs.append(zoneID)
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
operation.recordZoneWithIDWasDeletedBlock = { zoneID in
|
|
441
|
+
deletedZoneIDs.append(zoneID)
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
operation.fetchDatabaseChangesResultBlock = { result in
|
|
445
|
+
switch result {
|
|
446
|
+
case .success(let (token, _)):
|
|
447
|
+
dbChangeToken = token
|
|
448
|
+
saveDatabaseChangeToken(token)
|
|
449
|
+
// Now fetch zone changes for changedZoneIDs
|
|
450
|
+
case .failure(let error):
|
|
451
|
+
if let ckError = error as? CKError,
|
|
452
|
+
ckError.code == .changeTokenExpired {
|
|
453
|
+
dbChangeToken = nil
|
|
454
|
+
// Refetch from scratch
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
operation.qualityOfService = .utility
|
|
460
|
+
sharedDB.add(operation)
|
|
461
|
+
```
|