buildanything 1.7.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +9 -3
- package/CHANGELOG.md +112 -0
- package/README.md +2 -2
- package/agents/a11y-architect.md +166 -0
- package/agents/business-model.md +80 -29
- package/agents/code-architect.md +75 -0
- package/agents/code-reviewer.md +255 -0
- package/agents/code-simplifier.md +64 -0
- package/agents/design-brand-guardian.md +293 -53
- package/agents/design-critic.md +139 -0
- package/agents/design-inclusive-visuals-specialist.md +6 -19
- package/agents/design-ui-designer.md +335 -56
- package/agents/design-ux-architect.md +403 -55
- package/agents/design-ux-researcher.md +264 -49
- package/agents/engineering-ai-engineer.md +26 -36
- package/agents/engineering-backend-architect.md +185 -36
- package/agents/engineering-data-engineer.md +225 -43
- package/agents/engineering-devops-automator.md +227 -74
- package/agents/engineering-frontend-developer.md +210 -34
- package/agents/engineering-mobile-app-builder.md +6 -1
- package/agents/engineering-rapid-prototyper.md +30 -9
- package/agents/engineering-security-engineer.md +263 -61
- package/agents/engineering-senior-developer.md +128 -19
- package/agents/engineering-sre.md +84 -0
- package/agents/engineering-technical-writer.md +285 -41
- package/agents/feature-intel.md +110 -0
- package/agents/ios-app-review-guardian.md +66 -0
- package/agents/ios-foundation-models-specialist.md +64 -0
- package/agents/ios-storekit-specialist.md +59 -0
- package/agents/ios-swift-architect.md +129 -0
- package/agents/ios-swift-search.md +137 -0
- package/agents/ios-swift-ui-design.md +136 -0
- package/agents/marketing-app-store-optimizer.md +246 -64
- package/agents/planner.md +216 -0
- package/agents/pr-test-analyzer.md +63 -0
- package/agents/product-feedback-synthesizer.md +8 -2
- package/agents/refactor-cleaner.md +102 -0
- package/agents/security-reviewer.md +128 -0
- package/agents/silent-failure-hunter.md +54 -0
- package/agents/swift-build-resolver.md +119 -0
- package/agents/swift-reviewer.md +112 -0
- package/agents/tech-feasibility.md +21 -1
- package/agents/testing-api-tester.md +236 -59
- package/agents/testing-evidence-collector.md +26 -1
- package/agents/testing-performance-benchmarker.md +21 -1
- package/agents/testing-reality-checker.md +6 -1
- package/agents/visual-research.md +116 -0
- package/bin/adapters/cycle-counter-tool.ts +155 -0
- package/bin/adapters/scribe-tool.ts +71 -0
- package/bin/adapters/state-save-tool.ts +130 -0
- package/bin/adapters/write-lease-tool.ts +127 -0
- package/bin/buildanything-runtime.js +15 -0
- package/bin/buildanything-runtime.ts +328 -0
- package/bin/setup.js +83 -8
- package/commands/add-feature.md +2 -0
- package/commands/build.md +752 -332
- package/commands/fix.md +65 -0
- package/commands/self-check.md +121 -0
- package/commands/setup.md +114 -0
- package/commands/ux-review.md +63 -0
- package/commands/verify.md +69 -0
- package/docs/migration/agents.yaml +729 -0
- package/docs/migration/phase-graph.yaml +1088 -0
- package/docs/migration/sdk-host-compat.md +18 -0
- package/hooks/compile-writer-owner-cache.ts +171 -0
- package/hooks/hooks.json +36 -0
- package/hooks/pre-tool-use +19 -0
- package/hooks/pre-tool-use.ts +776 -0
- package/hooks/record-mode-transitions.ts +178 -0
- package/hooks/session-start +89 -2
- package/hooks/subagent-start +17 -0
- package/hooks/subagent-start.ts +471 -0
- package/hooks/subagent-stop +17 -0
- package/hooks/subagent-stop.ts +153 -0
- package/package.json +28 -5
- package/protocols/architecture-schema.md +171 -0
- package/protocols/build-fix.md +52 -0
- package/protocols/cleanup.md +54 -0
- package/protocols/decision-log.md +131 -0
- package/protocols/eval-harness.md +61 -0
- package/protocols/fake-data-detector.md +64 -0
- package/protocols/ios-context.md +234 -0
- package/protocols/ios-frameworks-map.md +323 -0
- package/protocols/ios-phase-branches.md +337 -0
- package/protocols/ios-preflight.md +27 -0
- package/protocols/launch-readiness.md +258 -0
- package/protocols/metric-loop.md +153 -0
- package/protocols/smoke-test.md +118 -0
- package/protocols/state-schema.json +388 -0
- package/protocols/state-schema.md +172 -0
- package/protocols/verify.md +127 -0
- package/protocols/visual-dna.md +185 -0
- package/protocols/web-phase-branches.md +351 -0
- package/skills/ios/_VENDORED.md +62 -0
- package/skills/ios/activitykit/LICENSE +131 -0
- package/skills/ios/activitykit/SKILL.md +505 -0
- package/skills/ios/activitykit/references/activitykit-patterns.md +868 -0
- package/skills/ios/app-intents/LICENSE +131 -0
- package/skills/ios/app-intents/SKILL.md +494 -0
- package/skills/ios/app-intents/references/appintents-advanced.md +1076 -0
- package/skills/ios/app-store-connect-metadata/SKILL.md +148 -0
- package/skills/ios/apple-on-device-ai/LICENSE +131 -0
- package/skills/ios/apple-on-device-ai/SKILL.md +505 -0
- package/skills/ios/apple-on-device-ai/references/coreml-conversion.md +425 -0
- package/skills/ios/apple-on-device-ai/references/coreml-optimization.md +344 -0
- package/skills/ios/apple-on-device-ai/references/foundation-models.md +508 -0
- package/skills/ios/apple-on-device-ai/references/mlx-swift.md +285 -0
- package/skills/ios/asc-privacy-manifest/SKILL.md +350 -0
- package/skills/ios/hig-components-content/SKILL.md +86 -0
- package/skills/ios/hig-components-content/references/activity-views.md +79 -0
- package/skills/ios/hig-components-content/references/charts.md +180 -0
- package/skills/ios/hig-components-content/references/collections.md +48 -0
- package/skills/ios/hig-components-content/references/color-wells.md +42 -0
- package/skills/ios/hig-components-content/references/image-views.md +82 -0
- package/skills/ios/hig-components-content/references/image-wells.md +34 -0
- package/skills/ios/hig-components-content/references/lockups.md +78 -0
- package/skills/ios/hig-components-content/references/web-views.md +36 -0
- package/skills/ios/hig-components-controls/SKILL.md +88 -0
- package/skills/ios/hig-components-controls/references/combo-boxes.md +40 -0
- package/skills/ios/hig-components-controls/references/controls.md +112 -0
- package/skills/ios/hig-components-controls/references/gauges.md +74 -0
- package/skills/ios/hig-components-controls/references/labels.md +92 -0
- package/skills/ios/hig-components-controls/references/pickers.md +128 -0
- package/skills/ios/hig-components-controls/references/rating-indicators.md +38 -0
- package/skills/ios/hig-components-controls/references/segmented-controls.md +94 -0
- package/skills/ios/hig-components-controls/references/sliders.md +92 -0
- package/skills/ios/hig-components-controls/references/steppers.md +40 -0
- package/skills/ios/hig-components-controls/references/text-fields.md +88 -0
- package/skills/ios/hig-components-controls/references/text-views.md +56 -0
- package/skills/ios/hig-components-controls/references/toggles.md +127 -0
- package/skills/ios/hig-components-controls/references/token-fields.md +48 -0
- package/skills/ios/hig-components-controls/references/virtual-keyboards.md +156 -0
- package/skills/ios/hig-components-dialogs/SKILL.md +76 -0
- package/skills/ios/hig-components-dialogs/references/action-sheets.md +74 -0
- package/skills/ios/hig-components-dialogs/references/alerts.md +158 -0
- package/skills/ios/hig-components-dialogs/references/digit-entry-views.md +32 -0
- package/skills/ios/hig-components-dialogs/references/popovers.md +81 -0
- package/skills/ios/hig-components-dialogs/references/sheets.md +157 -0
- package/skills/ios/hig-components-layout/SKILL.md +99 -0
- package/skills/ios/hig-components-layout/references/boxes.md +48 -0
- package/skills/ios/hig-components-layout/references/column-views.md +44 -0
- package/skills/ios/hig-components-layout/references/lists-and-tables.md +99 -0
- package/skills/ios/hig-components-layout/references/ornaments.md +56 -0
- package/skills/ios/hig-components-layout/references/outline-views.md +64 -0
- package/skills/ios/hig-components-layout/references/panels.md +75 -0
- package/skills/ios/hig-components-layout/references/scroll-views.md +123 -0
- package/skills/ios/hig-components-layout/references/sidebars.md +109 -0
- package/skills/ios/hig-components-layout/references/split-views.md +110 -0
- package/skills/ios/hig-components-layout/references/tab-bars.md +173 -0
- package/skills/ios/hig-components-layout/references/tab-views.md +68 -0
- package/skills/ios/hig-components-layout/references/windows.md +188 -0
- package/skills/ios/hig-components-menus/SKILL.md +81 -0
- package/skills/ios/hig-components-menus/references/action-button.md +61 -0
- package/skills/ios/hig-components-menus/references/buttons.md +261 -0
- package/skills/ios/hig-components-menus/references/context-menus.md +105 -0
- package/skills/ios/hig-components-menus/references/disclosure-controls.md +84 -0
- package/skills/ios/hig-components-menus/references/dock-menus.md +40 -0
- package/skills/ios/hig-components-menus/references/edit-menus.md +88 -0
- package/skills/ios/hig-components-menus/references/menus.md +171 -0
- package/skills/ios/hig-components-menus/references/pop-up-buttons.md +70 -0
- package/skills/ios/hig-components-menus/references/pull-down-buttons.md +77 -0
- package/skills/ios/hig-components-menus/references/the-menu-bar.md +303 -0
- package/skills/ios/hig-components-menus/references/toolbars.md +256 -0
- package/skills/ios/hig-components-search/SKILL.md +68 -0
- package/skills/ios/hig-components-search/references/page-controls.md +120 -0
- package/skills/ios/hig-components-search/references/path-controls.md +40 -0
- package/skills/ios/hig-components-search/references/search-fields.md +189 -0
- package/skills/ios/hig-components-status/SKILL.md +80 -0
- package/skills/ios/hig-components-status/references/activity-rings.md +105 -0
- package/skills/ios/hig-components-status/references/progress-indicators.md +116 -0
- package/skills/ios/hig-components-status/references/status-bars.md +38 -0
- package/skills/ios/hig-components-system/SKILL.md +88 -0
- package/skills/ios/hig-components-system/references/app-clips.md +387 -0
- package/skills/ios/hig-components-system/references/app-shortcuts.md +114 -0
- package/skills/ios/hig-components-system/references/complications.md +425 -0
- package/skills/ios/hig-components-system/references/home-screen-quick-actions.md +42 -0
- package/skills/ios/hig-components-system/references/live-activities.md +442 -0
- package/skills/ios/hig-components-system/references/notifications.md +153 -0
- package/skills/ios/hig-components-system/references/top-shelf.md +135 -0
- package/skills/ios/hig-components-system/references/watch-faces.md +40 -0
- package/skills/ios/hig-components-system/references/widgets.md +517 -0
- package/skills/ios/hig-foundations/SKILL.md +98 -0
- package/skills/ios/hig-foundations/references/accessibility.md +291 -0
- package/skills/ios/hig-foundations/references/app-icons.md +210 -0
- package/skills/ios/hig-foundations/references/branding.md +44 -0
- package/skills/ios/hig-foundations/references/color.md +274 -0
- package/skills/ios/hig-foundations/references/dark-mode.md +116 -0
- package/skills/ios/hig-foundations/references/icons.md +263 -0
- package/skills/ios/hig-foundations/references/images.md +176 -0
- package/skills/ios/hig-foundations/references/immersive-experiences.md +174 -0
- package/skills/ios/hig-foundations/references/inclusion.md +189 -0
- package/skills/ios/hig-foundations/references/layout.md +425 -0
- package/skills/ios/hig-foundations/references/materials.md +238 -0
- package/skills/ios/hig-foundations/references/motion.md +103 -0
- package/skills/ios/hig-foundations/references/privacy.md +231 -0
- package/skills/ios/hig-foundations/references/right-to-left.md +206 -0
- package/skills/ios/hig-foundations/references/sf-symbols.md +310 -0
- package/skills/ios/hig-foundations/references/spatial-layout.md +142 -0
- package/skills/ios/hig-foundations/references/typography.md +1146 -0
- package/skills/ios/hig-foundations/references/writing.md +91 -0
- package/skills/ios/hig-inputs/SKILL.md +94 -0
- package/skills/ios/hig-inputs/references/apple-pencil-and-scribble.md +148 -0
- package/skills/ios/hig-inputs/references/camera-control.md +107 -0
- package/skills/ios/hig-inputs/references/digital-crown.md +83 -0
- package/skills/ios/hig-inputs/references/eyes.md +120 -0
- package/skills/ios/hig-inputs/references/focus-and-selection.md +120 -0
- package/skills/ios/hig-inputs/references/game-controls.md +156 -0
- package/skills/ios/hig-inputs/references/gestures.md +208 -0
- package/skills/ios/hig-inputs/references/gyro-and-accelerometer.md +40 -0
- package/skills/ios/hig-inputs/references/keyboards.md +234 -0
- package/skills/ios/hig-inputs/references/nearby-interactions.md +70 -0
- package/skills/ios/hig-inputs/references/pointing-devices.md +237 -0
- package/skills/ios/hig-inputs/references/remotes.md +67 -0
- package/skills/ios/hig-inputs/references/spatial-interactions.md +70 -0
- package/skills/ios/hig-patterns/SKILL.md +104 -0
- package/skills/ios/hig-patterns/references/charting-data.md +81 -0
- package/skills/ios/hig-patterns/references/collaboration-and-sharing.md +86 -0
- package/skills/ios/hig-patterns/references/drag-and-drop.md +134 -0
- package/skills/ios/hig-patterns/references/entering-data.md +69 -0
- package/skills/ios/hig-patterns/references/feedback.md +67 -0
- package/skills/ios/hig-patterns/references/file-management.md +135 -0
- package/skills/ios/hig-patterns/references/going-full-screen.md +79 -0
- package/skills/ios/hig-patterns/references/launching.md +81 -0
- package/skills/ios/hig-patterns/references/live-viewing-apps.md +79 -0
- package/skills/ios/hig-patterns/references/loading.md +59 -0
- package/skills/ios/hig-patterns/references/managing-accounts.md +107 -0
- package/skills/ios/hig-patterns/references/managing-notifications.md +99 -0
- package/skills/ios/hig-patterns/references/modality.md +82 -0
- package/skills/ios/hig-patterns/references/multitasking.md +131 -0
- package/skills/ios/hig-patterns/references/offering-help.md +117 -0
- package/skills/ios/hig-patterns/references/onboarding.md +69 -0
- package/skills/ios/hig-patterns/references/playing-audio.md +124 -0
- package/skills/ios/hig-patterns/references/playing-haptics.md +280 -0
- package/skills/ios/hig-patterns/references/playing-video.md +180 -0
- package/skills/ios/hig-patterns/references/printing.md +50 -0
- package/skills/ios/hig-patterns/references/ratings-and-reviews.md +48 -0
- package/skills/ios/hig-patterns/references/searching.md +70 -0
- package/skills/ios/hig-patterns/references/settings.md +84 -0
- package/skills/ios/hig-patterns/references/undo-and-redo.md +58 -0
- package/skills/ios/hig-patterns/references/workouts.md +76 -0
- package/skills/ios/hig-platforms/SKILL.md +84 -0
- package/skills/ios/hig-platforms/references/designing-for-games.md +159 -0
- package/skills/ios/hig-platforms/references/designing-for-ios.md +66 -0
- package/skills/ios/hig-platforms/references/designing-for-ipados.md +64 -0
- package/skills/ios/hig-platforms/references/designing-for-macos.md +70 -0
- package/skills/ios/hig-platforms/references/designing-for-tvos.md +68 -0
- package/skills/ios/hig-platforms/references/designing-for-visionos.md +85 -0
- package/skills/ios/hig-platforms/references/designing-for-watchos.md +74 -0
- package/skills/ios/hig-project-context/SKILL.md +133 -0
- package/skills/ios/hig-technologies/SKILL.md +107 -0
- package/skills/ios/hig-technologies/references/airplay.md +125 -0
- package/skills/ios/hig-technologies/references/always-on.md +62 -0
- package/skills/ios/hig-technologies/references/apple-pay.md +441 -0
- package/skills/ios/hig-technologies/references/augmented-reality.md +247 -0
- package/skills/ios/hig-technologies/references/carekit.md +224 -0
- package/skills/ios/hig-technologies/references/carplay.md +119 -0
- package/skills/ios/hig-technologies/references/game-center.md +343 -0
- package/skills/ios/hig-technologies/references/generative-ai.md +110 -0
- package/skills/ios/hig-technologies/references/healthkit.md +120 -0
- package/skills/ios/hig-technologies/references/homekit.md +343 -0
- package/skills/ios/hig-technologies/references/icloud.md +52 -0
- package/skills/ios/hig-technologies/references/id-verifier.md +73 -0
- package/skills/ios/hig-technologies/references/imessage-apps-and-stickers.md +105 -0
- package/skills/ios/hig-technologies/references/in-app-purchase.md +263 -0
- package/skills/ios/hig-technologies/references/live-photos.md +54 -0
- package/skills/ios/hig-technologies/references/mac-catalyst.md +216 -0
- package/skills/ios/hig-technologies/references/machine-learning.md +394 -0
- package/skills/ios/hig-technologies/references/maps.md +221 -0
- package/skills/ios/hig-technologies/references/nfc.md +51 -0
- package/skills/ios/hig-technologies/references/photo-editing.md +40 -0
- package/skills/ios/hig-technologies/references/researchkit.md +134 -0
- package/skills/ios/hig-technologies/references/shareplay.md +142 -0
- package/skills/ios/hig-technologies/references/shazamkit.md +47 -0
- package/skills/ios/hig-technologies/references/sign-in-with-apple.md +288 -0
- package/skills/ios/hig-technologies/references/siri.md +523 -0
- package/skills/ios/hig-technologies/references/tap-to-pay-on-iphone.md +208 -0
- package/skills/ios/hig-technologies/references/voiceover.md +90 -0
- package/skills/ios/hig-technologies/references/wallet.md +420 -0
- package/skills/ios/ios-26-platform/SKILL.md +53 -0
- package/skills/ios/ios-26-platform/references/automatic-adoption.md +161 -0
- package/skills/ios/ios-26-platform/references/backward-compat.md +238 -0
- package/skills/ios/ios-26-platform/references/liquid-glass.md +255 -0
- package/skills/ios/ios-26-platform/references/swiftui-apis.md +277 -0
- package/skills/ios/ios-26-platform/references/toolbar-navigation.md +250 -0
- package/skills/ios/ios-bootstrap/SKILL.md +107 -0
- package/skills/ios/ios-bootstrap/references/apple-docs-mcp-config.md +28 -0
- package/skills/ios/ios-bootstrap/references/new-project-dialog.md +41 -0
- package/skills/ios/ios-bootstrap/references/xcode-mcp-config.md +29 -0
- package/skills/ios/ios-debugger-agent/LICENSE +21 -0
- package/skills/ios/ios-debugger-agent/SKILL.md +58 -0
- package/skills/ios/ios-debugger-agent/agents/openai.yaml +4 -0
- package/skills/ios/ios-entitlements-generator/SKILL.md +47 -0
- package/skills/ios/ios-info-plist-hardening/SKILL.md +130 -0
- package/skills/ios/ios-maestro-flow-author/SKILL.md +68 -0
- package/skills/ios/ios-maestro-flow-author/references/input-and-scroll.yaml +17 -0
- package/skills/ios/ios-maestro-flow-author/references/modal-and-dismiss.yaml +14 -0
- package/skills/ios/ios-maestro-flow-author/references/onboarding-flow.yaml +16 -0
- package/skills/ios/ios-maestro-flow-author/references/tab-navigation.yaml +13 -0
- package/skills/ios/ios-maestro-flow-author/references/tap-and-assert.yaml +9 -0
- package/skills/ios/swift-accessibility/LICENSE +21 -0
- package/skills/ios/swift-accessibility/SKILL.md +371 -0
- package/skills/ios/swift-accessibility/examples/before-after-appkit.md +446 -0
- package/skills/ios/swift-accessibility/examples/before-after-swiftui.md +441 -0
- package/skills/ios/swift-accessibility/examples/before-after-uikit.md +464 -0
- package/skills/ios/swift-accessibility/references/assistive-access.md +441 -0
- package/skills/ios/swift-accessibility/references/display-settings.md +491 -0
- package/skills/ios/swift-accessibility/references/dynamic-type.md +420 -0
- package/skills/ios/swift-accessibility/references/media-accessibility.md +421 -0
- package/skills/ios/swift-accessibility/references/motor-input.md +393 -0
- package/skills/ios/swift-accessibility/references/nutrition-labels.md +362 -0
- package/skills/ios/swift-accessibility/references/platform-specifics.md +515 -0
- package/skills/ios/swift-accessibility/references/semantic-structure.md +585 -0
- package/skills/ios/swift-accessibility/references/testing-auditing.md +507 -0
- package/skills/ios/swift-accessibility/references/voice-control.md +317 -0
- package/skills/ios/swift-accessibility/references/voiceover-swiftui.md +584 -0
- package/skills/ios/swift-accessibility/references/voiceover-uikit.md +519 -0
- package/skills/ios/swift-accessibility/references/wcag-mapping.md +167 -0
- package/skills/ios/swift-accessibility/resources/audit-template.swift +128 -0
- package/skills/ios/swift-accessibility/resources/qa-checklist.md +258 -0
- package/skills/ios/swift-actor-persistence/SKILL.md +143 -0
- package/skills/ios/swift-concurrency/LICENSE +21 -0
- package/skills/ios/swift-concurrency/SKILL.md +171 -0
- package/skills/ios/swift-concurrency/references/_index.md +50 -0
- package/skills/ios/swift-concurrency/references/actors.md +660 -0
- package/skills/ios/swift-concurrency/references/async-algorithms.md +847 -0
- package/skills/ios/swift-concurrency/references/async-await-basics.md +266 -0
- package/skills/ios/swift-concurrency/references/async-sequences.md +710 -0
- package/skills/ios/swift-concurrency/references/core-data.md +560 -0
- package/skills/ios/swift-concurrency/references/glossary.md +135 -0
- package/skills/ios/swift-concurrency/references/linting.md +155 -0
- package/skills/ios/swift-concurrency/references/memory-management.md +569 -0
- package/skills/ios/swift-concurrency/references/migration.md +1104 -0
- package/skills/ios/swift-concurrency/references/performance.md +593 -0
- package/skills/ios/swift-concurrency/references/sendable.md +598 -0
- package/skills/ios/swift-concurrency/references/tasks.md +636 -0
- package/skills/ios/swift-concurrency/references/testing.md +592 -0
- package/skills/ios/swift-concurrency/references/threading.md +495 -0
- package/skills/ios/swift-concurrency-6-2/SKILL.md +216 -0
- package/skills/ios/swift-protocol-di-testing/SKILL.md +190 -0
- package/skills/ios/swift-security-expert/LICENSE +21 -0
- package/skills/ios/swift-security-expert/SKILL.md +470 -0
- package/skills/ios/swift-security-expert/references/biometric-authentication.md +565 -0
- package/skills/ios/swift-security-expert/references/certificate-trust.md +592 -0
- package/skills/ios/swift-security-expert/references/common-anti-patterns.md +690 -0
- package/skills/ios/swift-security-expert/references/compliance-owasp-mapping.md +537 -0
- package/skills/ios/swift-security-expert/references/credential-storage-patterns.md +721 -0
- package/skills/ios/swift-security-expert/references/cryptokit-public-key.md +505 -0
- package/skills/ios/swift-security-expert/references/cryptokit-symmetric.md +497 -0
- package/skills/ios/swift-security-expert/references/keychain-access-control.md +508 -0
- package/skills/ios/swift-security-expert/references/keychain-fundamentals.md +596 -0
- package/skills/ios/swift-security-expert/references/keychain-item-classes.md +476 -0
- package/skills/ios/swift-security-expert/references/keychain-sharing.md +458 -0
- package/skills/ios/swift-security-expert/references/migration-legacy-stores.md +727 -0
- package/skills/ios/swift-security-expert/references/secure-enclave.md +539 -0
- package/skills/ios/swift-security-expert/references/testing-security-code.md +781 -0
- package/skills/ios/swift-testing-expert/LICENSE +21 -0
- package/skills/ios/swift-testing-expert/SKILL.md +79 -0
- package/skills/ios/swift-testing-expert/references/_index.md +12 -0
- package/skills/ios/swift-testing-expert/references/async-testing-and-waiting.md +127 -0
- package/skills/ios/swift-testing-expert/references/expectations.md +145 -0
- package/skills/ios/swift-testing-expert/references/fundamentals.md +141 -0
- package/skills/ios/swift-testing-expert/references/migration-from-xctest.md +127 -0
- package/skills/ios/swift-testing-expert/references/parallelization-and-isolation.md +95 -0
- package/skills/ios/swift-testing-expert/references/parameterized-testing.md +284 -0
- package/skills/ios/swift-testing-expert/references/performance-and-best-practices.md +187 -0
- package/skills/ios/swift-testing-expert/references/traits-and-tags.md +114 -0
- package/skills/ios/swift-testing-expert/references/xcode-workflows.md +70 -0
- package/skills/ios/swiftdata-pro/LICENSE +21 -0
- package/skills/ios/swiftdata-pro/SKILL.md +102 -0
- package/skills/ios/swiftdata-pro/agents/openai.yaml +10 -0
- package/skills/ios/swiftdata-pro/assets/swiftdata-pro-icon.png +0 -0
- package/skills/ios/swiftdata-pro/assets/swiftdata-pro-icon.svg +29 -0
- package/skills/ios/swiftdata-pro/references/class-inheritance.md +104 -0
- package/skills/ios/swiftdata-pro/references/cloudkit.md +10 -0
- package/skills/ios/swiftdata-pro/references/core-rules.md +20 -0
- package/skills/ios/swiftdata-pro/references/indexing.md +27 -0
- package/skills/ios/swiftdata-pro/references/predicates.md +73 -0
- package/skills/ios/swiftui-design-principles/AGENTS.md +21 -0
- package/skills/ios/swiftui-design-principles/LICENSE +21 -0
- package/skills/ios/swiftui-design-principles/README.md +41 -0
- package/skills/ios/swiftui-design-principles/SKILL.md +605 -0
- package/skills/ios/swiftui-design-principles/metadata.json +10 -0
- package/skills/ios/swiftui-design-tokens/SKILL.md +475 -0
- package/skills/ios/swiftui-liquid-glass/LICENSE +21 -0
- package/skills/ios/swiftui-liquid-glass/SKILL.md +95 -0
- package/skills/ios/swiftui-liquid-glass/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-liquid-glass/references/liquid-glass.md +280 -0
- package/skills/ios/swiftui-performance-audit/LICENSE +21 -0
- package/skills/ios/swiftui-performance-audit/SKILL.md +111 -0
- package/skills/ios/swiftui-performance-audit/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-performance-audit/references/code-smells.md +150 -0
- package/skills/ios/swiftui-performance-audit/references/demystify-swiftui-performance-wwdc23.md +46 -0
- package/skills/ios/swiftui-performance-audit/references/optimizing-swiftui-performance-instruments.md +29 -0
- package/skills/ios/swiftui-performance-audit/references/profiling-intake.md +44 -0
- package/skills/ios/swiftui-performance-audit/references/report-template.md +47 -0
- package/skills/ios/swiftui-performance-audit/references/understanding-hangs-in-your-app.md +33 -0
- package/skills/ios/swiftui-performance-audit/references/understanding-improving-swiftui-performance.md +52 -0
- package/skills/ios/swiftui-pro/LICENSE +21 -0
- package/skills/ios/swiftui-pro/SKILL.md +108 -0
- package/skills/ios/swiftui-pro/agents/openai.yaml +10 -0
- package/skills/ios/swiftui-pro/assets/swiftui-pro-icon.png +0 -0
- package/skills/ios/swiftui-pro/assets/swiftui-pro-icon.svg +29 -0
- package/skills/ios/swiftui-pro/references/accessibility.md +13 -0
- package/skills/ios/swiftui-pro/references/api.md +39 -0
- package/skills/ios/swiftui-pro/references/data.md +43 -0
- package/skills/ios/swiftui-pro/references/design.md +31 -0
- package/skills/ios/swiftui-pro/references/hygiene.md +9 -0
- package/skills/ios/swiftui-pro/references/navigation.md +14 -0
- package/skills/ios/swiftui-pro/references/performance.md +46 -0
- package/skills/ios/swiftui-pro/references/swift.md +56 -0
- package/skills/ios/swiftui-pro/references/views.md +35 -0
- package/skills/ios/swiftui-ui-patterns/LICENSE +21 -0
- package/skills/ios/swiftui-ui-patterns/SKILL.md +100 -0
- package/skills/ios/swiftui-ui-patterns/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-ui-patterns/references/app-wiring.md +201 -0
- package/skills/ios/swiftui-ui-patterns/references/async-state.md +96 -0
- package/skills/ios/swiftui-ui-patterns/references/components-index.md +50 -0
- package/skills/ios/swiftui-ui-patterns/references/controls.md +57 -0
- package/skills/ios/swiftui-ui-patterns/references/deeplinks.md +66 -0
- package/skills/ios/swiftui-ui-patterns/references/focus.md +90 -0
- package/skills/ios/swiftui-ui-patterns/references/form.md +97 -0
- package/skills/ios/swiftui-ui-patterns/references/grids.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/haptics.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/input-toolbar.md +51 -0
- package/skills/ios/swiftui-ui-patterns/references/lightweight-clients.md +93 -0
- package/skills/ios/swiftui-ui-patterns/references/list.md +86 -0
- package/skills/ios/swiftui-ui-patterns/references/loading-placeholders.md +38 -0
- package/skills/ios/swiftui-ui-patterns/references/macos-settings.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/matched-transitions.md +59 -0
- package/skills/ios/swiftui-ui-patterns/references/media.md +73 -0
- package/skills/ios/swiftui-ui-patterns/references/menu-bar.md +101 -0
- package/skills/ios/swiftui-ui-patterns/references/navigationstack.md +159 -0
- package/skills/ios/swiftui-ui-patterns/references/overlay.md +45 -0
- package/skills/ios/swiftui-ui-patterns/references/performance.md +62 -0
- package/skills/ios/swiftui-ui-patterns/references/previews.md +48 -0
- package/skills/ios/swiftui-ui-patterns/references/scroll-reveal.md +133 -0
- package/skills/ios/swiftui-ui-patterns/references/scrollview.md +87 -0
- package/skills/ios/swiftui-ui-patterns/references/searchable.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/sheets.md +155 -0
- package/skills/ios/swiftui-ui-patterns/references/split-views.md +72 -0
- package/skills/ios/swiftui-ui-patterns/references/tabview.md +114 -0
- package/skills/ios/swiftui-ui-patterns/references/theming.md +71 -0
- package/skills/ios/swiftui-ui-patterns/references/title-menus.md +93 -0
- package/skills/ios/swiftui-ui-patterns/references/top-bar.md +49 -0
- package/skills/ios/swiftui-view-refactor/LICENSE +21 -0
- package/skills/ios/swiftui-view-refactor/SKILL.md +207 -0
- package/skills/ios/swiftui-view-refactor/agents/openai.yaml +4 -0
- package/skills/ios/swiftui-view-refactor/references/mv-patterns.md +161 -0
- package/skills/ios/widgetkit/LICENSE +131 -0
- package/skills/ios/widgetkit/SKILL.md +502 -0
- package/skills/ios/widgetkit/references/widgetkit-advanced.md +871 -0
- package/skills/ios/writing-for-interfaces/SKILL.md +75 -0
- package/skills/web/accessibility/SKILL.md +146 -0
- package/skills/web/aceternity-ui/SKILL.md +719 -0
- package/skills/web/aceternity-ui/metadata.json +10 -0
- package/skills/web/api-design/SKILL.md +523 -0
- package/skills/web/chart-accessibility/SKILL.md +332 -0
- package/skills/web/composition-patterns/AGENTS.md +946 -0
- package/skills/web/composition-patterns/README.md +60 -0
- package/skills/web/composition-patterns/SKILL.md +89 -0
- package/skills/web/composition-patterns/metadata.json +11 -0
- package/skills/web/composition-patterns/rules/_sections.md +29 -0
- package/skills/web/composition-patterns/rules/_template.md +24 -0
- package/skills/web/composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
- package/skills/web/composition-patterns/rules/architecture-compound-components.md +112 -0
- package/skills/web/composition-patterns/rules/patterns-children-over-render-props.md +87 -0
- package/skills/web/composition-patterns/rules/patterns-explicit-variants.md +100 -0
- package/skills/web/composition-patterns/rules/react19-no-forwardref.md +42 -0
- package/skills/web/composition-patterns/rules/state-context-interface.md +191 -0
- package/skills/web/composition-patterns/rules/state-decouple-implementation.md +113 -0
- package/skills/web/composition-patterns/rules/state-lift-state.md +125 -0
- package/skills/web/cost-aware-llm-pipeline/SKILL.md +183 -0
- package/skills/web/database-migrations/SKILL.md +429 -0
- package/skills/web/deployment-patterns/SKILL.md +427 -0
- package/skills/web/docker-patterns/SKILL.md +364 -0
- package/skills/web/e2e-testing/SKILL.md +326 -0
- package/skills/web/lighthouse-ci/SKILL.md +361 -0
- package/skills/web/mcp-server-patterns/SKILL.md +69 -0
- package/skills/web/next-best-practices/SKILL.md +153 -0
- package/skills/web/next-best-practices/async-patterns.md +87 -0
- package/skills/web/next-best-practices/bundling.md +180 -0
- package/skills/web/next-best-practices/data-patterns.md +297 -0
- package/skills/web/next-best-practices/debug-tricks.md +105 -0
- package/skills/web/next-best-practices/directives.md +73 -0
- package/skills/web/next-best-practices/error-handling.md +227 -0
- package/skills/web/next-best-practices/file-conventions.md +140 -0
- package/skills/web/next-best-practices/font.md +245 -0
- package/skills/web/next-best-practices/functions.md +108 -0
- package/skills/web/next-best-practices/hydration-error.md +91 -0
- package/skills/web/next-best-practices/image.md +173 -0
- package/skills/web/next-best-practices/metadata.md +301 -0
- package/skills/web/next-best-practices/parallel-routes.md +287 -0
- package/skills/web/next-best-practices/route-handlers.md +146 -0
- package/skills/web/next-best-practices/rsc-boundaries.md +159 -0
- package/skills/web/next-best-practices/runtime-selection.md +39 -0
- package/skills/web/next-best-practices/scripts.md +141 -0
- package/skills/web/next-best-practices/self-hosting.md +371 -0
- package/skills/web/next-best-practices/suspense-boundaries.md +67 -0
- package/skills/web/next-cache-components/SKILL.md +411 -0
- package/skills/web/postgres-best-practices/SKILL.md +14 -0
- package/skills/web/postgres-best-practices/references/schema-design.md +9 -0
- package/skills/web/react-best-practices/AGENTS.md +3810 -0
- package/skills/web/react-best-practices/README.md +123 -0
- package/skills/web/react-best-practices/SKILL.md +149 -0
- package/skills/web/react-best-practices/metadata.json +15 -0
- package/skills/web/react-best-practices/rules/_sections.md +46 -0
- package/skills/web/react-best-practices/rules/_template.md +28 -0
- package/skills/web/react-best-practices/rules/advanced-effect-event-deps.md +56 -0
- package/skills/web/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/skills/web/react-best-practices/rules/advanced-init-once.md +42 -0
- package/skills/web/react-best-practices/rules/advanced-use-latest.md +39 -0
- package/skills/web/react-best-practices/rules/async-api-routes.md +38 -0
- package/skills/web/react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
- package/skills/web/react-best-practices/rules/async-defer-await.md +82 -0
- package/skills/web/react-best-practices/rules/async-dependencies.md +51 -0
- package/skills/web/react-best-practices/rules/async-parallel.md +28 -0
- package/skills/web/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/skills/web/react-best-practices/rules/bundle-analyzable-paths.md +63 -0
- package/skills/web/react-best-practices/rules/bundle-barrel-imports.md +60 -0
- package/skills/web/react-best-practices/rules/bundle-conditional.md +31 -0
- package/skills/web/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/skills/web/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/skills/web/react-best-practices/rules/bundle-preload.md +50 -0
- package/skills/web/react-best-practices/rules/client-event-listeners.md +74 -0
- package/skills/web/react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/skills/web/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/skills/web/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/skills/web/react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/skills/web/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/skills/web/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/skills/web/react-best-practices/rules/js-cache-storage.md +70 -0
- package/skills/web/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/skills/web/react-best-practices/rules/js-early-exit.md +50 -0
- package/skills/web/react-best-practices/rules/js-flatmap-filter.md +60 -0
- package/skills/web/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/skills/web/react-best-practices/rules/js-index-maps.md +37 -0
- package/skills/web/react-best-practices/rules/js-length-check-first.md +49 -0
- package/skills/web/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/skills/web/react-best-practices/rules/js-request-idle-callback.md +105 -0
- package/skills/web/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/skills/web/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/skills/web/react-best-practices/rules/rendering-activity.md +26 -0
- package/skills/web/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/skills/web/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/skills/web/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/skills/web/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/skills/web/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/skills/web/react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/skills/web/react-best-practices/rules/rendering-resource-hints.md +85 -0
- package/skills/web/react-best-practices/rules/rendering-script-defer-async.md +68 -0
- package/skills/web/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/skills/web/react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/skills/web/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/skills/web/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/skills/web/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/skills/web/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/skills/web/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/skills/web/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/skills/web/react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/skills/web/react-best-practices/rules/rerender-memo.md +44 -0
- package/skills/web/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/skills/web/react-best-practices/rules/rerender-no-inline-components.md +82 -0
- package/skills/web/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/skills/web/react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
- package/skills/web/react-best-practices/rules/rerender-transitions.md +40 -0
- package/skills/web/react-best-practices/rules/rerender-use-deferred-value.md +59 -0
- package/skills/web/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/skills/web/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/skills/web/react-best-practices/rules/server-auth-actions.md +96 -0
- package/skills/web/react-best-practices/rules/server-cache-lru.md +41 -0
- package/skills/web/react-best-practices/rules/server-cache-react.md +76 -0
- package/skills/web/react-best-practices/rules/server-dedup-props.md +65 -0
- package/skills/web/react-best-practices/rules/server-hoist-static-io.md +149 -0
- package/skills/web/react-best-practices/rules/server-no-shared-module-state.md +50 -0
- package/skills/web/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/skills/web/react-best-practices/rules/server-parallel-nested-fetching.md +34 -0
- package/skills/web/react-best-practices/rules/server-serialization.md +38 -0
- package/skills/web/seo/SKILL.md +154 -0
- package/skills/web/web-design-guidelines/SKILL.md +39 -0
- package/skills/web/zap-scan-config/SKILL.md +444 -0
- package/skills/web/zap-scan-config/assets/.gitkeep +9 -0
- package/skills/web/zap-scan-config/assets/github_action.yml +207 -0
- package/skills/web/zap-scan-config/assets/gitlab_ci.yml +226 -0
- package/skills/web/zap-scan-config/assets/zap_automation.yaml +196 -0
- package/skills/web/zap-scan-config/assets/zap_context.xml +192 -0
- package/skills/web/zap-scan-config/references/EXAMPLE.md +40 -0
- package/skills/web/zap-scan-config/references/api_testing_guide.md +475 -0
- package/skills/web/zap-scan-config/references/authentication_guide.md +431 -0
- package/skills/web/zap-scan-config/references/false_positive_handling.md +427 -0
- package/skills/web/zap-scan-config/references/owasp_mapping.md +255 -0
- package/src/lrr/aggregator.ts +80 -0
- package/src/orchestrator/hooks/context-header.ts +95 -0
- package/src/orchestrator/hooks/token-accounting-emitter.ts +77 -0
- package/src/orchestrator/hooks/token-accounting.ts +101 -0
- package/src/orchestrator/mcp/cycle-counter.ts +129 -0
- package/src/orchestrator/mcp/scribe.ts +283 -0
- package/src/orchestrator/mcp/state-save.ts +149 -0
- package/src/orchestrator/mcp/write-lease.ts +167 -0
- package/src/orchestrator/phase4-shared-context.ts +41 -0
- package/src/orchestrator/schemas/backward-edge.ts +46 -0
- package/agents/agentic-identity-trust.md +0 -121
- package/agents/data-consolidation-agent.md +0 -39
- package/agents/design-image-prompt-engineer.md +0 -105
- package/agents/design-visual-storyteller.md +0 -147
- package/agents/design-whimsy-injector.md +0 -89
- package/agents/engineering-autonomous-optimization-architect.md +0 -105
- package/agents/market-intel.md +0 -35
- package/agents/marketing-instagram-curator.md +0 -111
- package/agents/marketing-reddit-community-builder.md +0 -121
- package/agents/marketing-social-media-strategist.md +0 -74
- package/agents/marketing-tiktok-strategist.md +0 -123
- package/agents/marketing-twitter-engager.md +0 -124
- package/agents/marketing-wechat-official-account.md +0 -143
- package/agents/marketing-xiaohongshu-specialist.md +0 -136
- package/agents/marketing-zhihu-strategist.md +0 -160
- package/agents/product-behavioral-nudge-engine.md +0 -78
- package/agents/project-management-experiment-tracker.md +0 -102
- package/agents/report-distribution-agent.md +0 -43
- package/agents/risk-analysis.md +0 -45
- package/agents/sales-data-extraction-agent.md +0 -46
- package/agents/specialized-cultural-intelligence-strategist.md +0 -65
- package/agents/specialized-developer-advocate.md +0 -146
- package/agents/support-analytics-reporter.md +0 -133
- package/agents/support-executive-summary-generator.md +0 -64
- package/agents/support-finance-tracker.md +0 -145
- package/agents/support-legal-compliance-checker.md +0 -129
- package/agents/support-support-responder.md +0 -91
- package/agents/testing-accessibility-auditor.md +0 -110
- package/agents/testing-test-results-analyzer.md +0 -97
- package/agents/testing-tool-evaluator.md +0 -76
- package/agents/testing-workflow-optimizer.md +0 -99
- package/agents/user-research.md +0 -40
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
# Keychain Fundamentals
|
|
2
|
+
|
|
3
|
+
> **Scope:** SecItem\* CRUD operations, query dictionary structure, kSecClass types, OSStatus error handling, actor-based wrapper patterns. This is the foundation file — all other reference files assume familiarity with these patterns.
|
|
4
|
+
>
|
|
5
|
+
> **Key APIs:** `SecItemAdd`, `SecItemCopyMatching`, `SecItemUpdate`, `SecItemDelete`, `kSecClassGenericPassword`, `kSecClassInternetPassword`, `kSecClassKey`, `kSecClassCertificate`, `kSecClassIdentity`
|
|
6
|
+
>
|
|
7
|
+
> **Apple Documentation:** [Keychain Services](https://developer.apple.com/documentation/security/keychain_services), [TN3137](https://developer.apple.com/documentation/technotes/tn3137-on-mac-keychains), Quinn "The Eskimo!" DTS posts: "SecItem: Fundamentals" and "SecItem: Pitfalls and Best Practices"
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Architecture Overview
|
|
12
|
+
|
|
13
|
+
The Keychain Services API exposes four C functions that map to database CRUD operations. Every call is an IPC round-trip to the `securityd` daemon, backed by an encrypted SQLite database. This means every call **blocks the calling thread** and must never execute on `@MainActor`.
|
|
14
|
+
|
|
15
|
+
Internally, keychain items use **two-tier AES-256-GCM encryption** (per the Apple Platform Security Guide): a table-level **metadata key** cached in the Application Processor for fast attribute searches, and a **per-row secret key** requiring a Secure Enclave round-trip for `kSecValueData` decryption. This two-tier design has direct performance implications covered in the Performance section below.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## The Four Functions and Their Dictionary Contracts
|
|
20
|
+
|
|
21
|
+
Each function accepts a specific _type_ of dictionary. Confusing which keys belong in which dictionary is the single most common source of bugs. Quinn (Apple DTS) defines five property groups:
|
|
22
|
+
|
|
23
|
+
1. **Item class** — `kSecClass`
|
|
24
|
+
2. **Item attributes** — `kSecAttrAccount`, `kSecAttrService`, etc.
|
|
25
|
+
3. **Search properties** — `kSecMatchLimit`
|
|
26
|
+
4. **Return type properties** — `kSecReturnData`, `kSecReturnAttributes`, `kSecReturnRef`, `kSecReturnPersistentRef`
|
|
27
|
+
5. **Value type properties** — `kSecValueData`, `kSecValueRef`
|
|
28
|
+
|
|
29
|
+
| Function | Dictionary Type | Supports Return Keys? | Default `kSecMatchLimit` | Since |
|
|
30
|
+
| --------------------------- | -------------------------------------------- | ----------------------- | ------------------------ | ------- |
|
|
31
|
+
| `SecItemAdd(_:_:)` | Add dictionary (class + attrs + values) | ✅ Optional | N/A | iOS 2.0 |
|
|
32
|
+
| `SecItemCopyMatching(_:_:)` | Query + return (all 5 groups) | ✅ Required for results | `kSecMatchLimitOne` | iOS 2.0 |
|
|
33
|
+
| `SecItemUpdate(_:_:)` | Pure query (param 1) + update dict (param 2) | ❌ | **`kSecMatchLimitAll`** | iOS 2.0 |
|
|
34
|
+
| `SecItemDelete(_:)` | Pure query | ❌ | **`kSecMatchLimitAll`** | iOS 2.0 |
|
|
35
|
+
|
|
36
|
+
**Critical detail:** `kSecMatchLimit` defaults to `kSecMatchLimitOne` for `SecItemCopyMatching` but **`kSecMatchLimitAll` for `SecItemUpdate` and `SecItemDelete`**. An under-specified delete query will wipe every matching item in the keychain.
|
|
37
|
+
|
|
38
|
+
**Dictionary hygiene:** Use a fresh dictionary for each call. Putting `kSecReturnData` in an add dictionary or `kSecClass` in an update dictionary produces `errSecParam` (-50). Quinn's guidance: "Use a new dictionary for each call. That prevents state from one call accidentally leaking into a subsequent call."
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Uniqueness and Primary Keys
|
|
43
|
+
|
|
44
|
+
For `kSecClassGenericPassword`, uniqueness is determined by the combination of:
|
|
45
|
+
|
|
46
|
+
- `kSecAttrAccount` + `kSecAttrService` + `kSecAttrAccessGroup` + `kSecAttrSynchronizable`
|
|
47
|
+
|
|
48
|
+
Other attributes like `kSecAttrGeneric`, `kSecAttrLabel`, or `kSecAttrDescription` **do not participate in uniqueness**. This means a query filtering on non-unique attributes can return `errSecItemNotFound` while a subsequent add still hits `errSecDuplicateItem`.
|
|
49
|
+
|
|
50
|
+
For `kSecClassInternetPassword`, the uniqueness set includes: `kSecAttrAccount` + `kSecAttrServer` + `kSecAttrProtocol` + `kSecAttrAuthenticationType` + `kSecAttrPort` + `kSecAttrPath` + `kSecAttrSecurityDomain` + `kSecAttrAccessGroup` + `kSecAttrSynchronizable`.
|
|
51
|
+
|
|
52
|
+
**Immutable attributes:** `kSecAttrAccount` and `kSecClass` cannot be changed via `SecItemUpdate`. To change them, delete and re-add the item (see `keychain-item-classes.md`).
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## The Add-or-Update Pattern
|
|
57
|
+
|
|
58
|
+
The most common AI-generated keychain bug is calling `SecItemAdd` without handling `errSecDuplicateItem` (-25299).
|
|
59
|
+
|
|
60
|
+
❌ **Naive add that silently fails on duplicate:**
|
|
61
|
+
|
|
62
|
+
```swift
|
|
63
|
+
// ❌ WRONG — silently fails if item already exists
|
|
64
|
+
func savePassword(_ password: String, account: String) {
|
|
65
|
+
let query: [CFString: Any] = [
|
|
66
|
+
kSecClass: kSecClassGenericPassword,
|
|
67
|
+
kSecAttrService: "com.example.app",
|
|
68
|
+
kSecAttrAccount: account,
|
|
69
|
+
kSecValueData: Data(password.utf8)
|
|
70
|
+
]
|
|
71
|
+
SecItemAdd(query as CFDictionary, nil) // Return value IGNORED!
|
|
72
|
+
// If item exists → errSecDuplicateItem (-25299) — password never saved
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
✅ **Correct add-or-update with exhaustive OSStatus handling:**
|
|
77
|
+
|
|
78
|
+
```swift
|
|
79
|
+
// ✅ CORRECT — attempts add, falls back to update on duplicate
|
|
80
|
+
func savePassword(_ password: String, account: String) throws {
|
|
81
|
+
let baseQuery: [CFString: Any] = [
|
|
82
|
+
kSecClass: kSecClassGenericPassword,
|
|
83
|
+
kSecAttrService: "com.example.app",
|
|
84
|
+
kSecAttrAccount: account
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
var addQuery = baseQuery
|
|
88
|
+
addQuery[kSecValueData] = Data(password.utf8)
|
|
89
|
+
addQuery[kSecAttrAccessible] = kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
|
|
90
|
+
|
|
91
|
+
let addStatus = SecItemAdd(addQuery as CFDictionary, nil)
|
|
92
|
+
|
|
93
|
+
switch addStatus {
|
|
94
|
+
case errSecSuccess:
|
|
95
|
+
return
|
|
96
|
+
|
|
97
|
+
case errSecDuplicateItem:
|
|
98
|
+
// Item exists — update it
|
|
99
|
+
let updates: [CFString: Any] = [kSecValueData: Data(password.utf8)]
|
|
100
|
+
let updateStatus = SecItemUpdate(
|
|
101
|
+
baseQuery as CFDictionary,
|
|
102
|
+
updates as CFDictionary
|
|
103
|
+
)
|
|
104
|
+
guard updateStatus == errSecSuccess else {
|
|
105
|
+
throw KeychainError(status: updateStatus)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
case errSecInteractionNotAllowed:
|
|
109
|
+
// Device locked — do NOT delete-and-retry!
|
|
110
|
+
throw KeychainError(status: addStatus)
|
|
111
|
+
|
|
112
|
+
default:
|
|
113
|
+
throw KeychainError(status: addStatus)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Key points in this pattern:
|
|
119
|
+
|
|
120
|
+
- **Separate dictionaries** for add vs. update — the update dictionary contains only the attributes to change, never `kSecClass` or search properties.
|
|
121
|
+
- **`errSecInteractionNotAllowed`** (-25308) means the device is locked and data protection prevents access. Never delete items in response to this error; the item is valid but temporarily inaccessible.
|
|
122
|
+
- **Prefer update over delete-then-add** — update preserves persistent references and avoids the race condition window between delete and add.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Reading from the Keychain: Return Flags and Type Casting
|
|
127
|
+
|
|
128
|
+
The second most common bug is calling `SecItemCopyMatching` without `kSecReturn*` flags. The function may return `errSecSuccess` with a `nil` result — this is "success but nil," not a real success.
|
|
129
|
+
|
|
130
|
+
❌ **Query that returns no data because `kSecReturnData` is missing:**
|
|
131
|
+
|
|
132
|
+
```swift
|
|
133
|
+
// ❌ WRONG — no kSecReturn* flags, result is always nil
|
|
134
|
+
func loadPassword(account: String) -> Data? {
|
|
135
|
+
let query: [CFString: Any] = [
|
|
136
|
+
kSecClass: kSecClassGenericPassword,
|
|
137
|
+
kSecAttrService: "com.example.app",
|
|
138
|
+
kSecAttrAccount: account,
|
|
139
|
+
kSecMatchLimit: kSecMatchLimitOne
|
|
140
|
+
// BUG: Missing kSecReturnData: true
|
|
141
|
+
]
|
|
142
|
+
var result: CFTypeRef?
|
|
143
|
+
SecItemCopyMatching(query as CFDictionary, &result)
|
|
144
|
+
return result as? Data // Always nil — no return type was requested
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
✅ **Correct query with proper return flags and exhaustive error handling:**
|
|
149
|
+
|
|
150
|
+
```swift
|
|
151
|
+
// ✅ CORRECT — explicitly requests data, handles all error states
|
|
152
|
+
func loadPassword(account: String) throws -> Data? {
|
|
153
|
+
let query: [CFString: Any] = [
|
|
154
|
+
kSecClass: kSecClassGenericPassword,
|
|
155
|
+
kSecAttrService: "com.example.app",
|
|
156
|
+
kSecAttrAccount: account,
|
|
157
|
+
kSecMatchLimit: kSecMatchLimitOne,
|
|
158
|
+
kSecReturnData: true // ← REQUIRED to get the secret
|
|
159
|
+
]
|
|
160
|
+
|
|
161
|
+
var result: CFTypeRef?
|
|
162
|
+
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
163
|
+
|
|
164
|
+
switch status {
|
|
165
|
+
case errSecSuccess:
|
|
166
|
+
guard let data = result as? Data else {
|
|
167
|
+
throw KeychainError(status: errSecParam)
|
|
168
|
+
}
|
|
169
|
+
return data
|
|
170
|
+
|
|
171
|
+
case errSecItemNotFound:
|
|
172
|
+
return nil // Legitimate "not found" — not an error
|
|
173
|
+
|
|
174
|
+
case errSecInteractionNotAllowed:
|
|
175
|
+
throw KeychainError(status: status)
|
|
176
|
+
|
|
177
|
+
default:
|
|
178
|
+
throw KeychainError(status: status)
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Return Type Cheat Sheet
|
|
184
|
+
|
|
185
|
+
The `CFTypeRef` type depends entirely on which return flags and match limits are set:
|
|
186
|
+
|
|
187
|
+
```text
|
|
188
|
+
kSecReturnData only + kSecMatchLimitOne → Data
|
|
189
|
+
kSecReturnAttributes + kSecMatchLimitOne → [String: Any]
|
|
190
|
+
kSecReturnData + Attrs + kSecMatchLimitOne → [String: Any] (data under kSecValueData key)
|
|
191
|
+
kSecReturnRef + kSecMatchLimitOne → SecKey / SecCertificate / SecIdentity
|
|
192
|
+
kSecReturnPersistentRef + kSecMatchLimitOne → Data (opaque handle)
|
|
193
|
+
Any combination + kSecMatchLimitAll → Array of the above type
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Note:** Combining `kSecReturnData` with `kSecMatchLimitAll` may be restricted for password classes on some OS versions. For listing items, prefer `kSecReturnAttributes` or `kSecReturnRef` with `kSecMatchLimitAll`, then fetch data per-item as needed.
|
|
197
|
+
|
|
198
|
+
### String Keys vs. kSec\* Constants
|
|
199
|
+
|
|
200
|
+
Never use raw string literals (`"svce"`, `"class"`) instead of `kSec*` constants. The constants are `CFString` values with specific internal representations. Two equally valid dictionary key styles exist:
|
|
201
|
+
|
|
202
|
+
```swift
|
|
203
|
+
// Style A: CFString keys (fewer casts at definition, cast once at call site)
|
|
204
|
+
let query: [CFString: Any] = [kSecClass: kSecClassGenericPassword]
|
|
205
|
+
SecItemAdd(query as CFDictionary, nil)
|
|
206
|
+
|
|
207
|
+
// Style B: String keys (more common in community code)
|
|
208
|
+
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword]
|
|
209
|
+
SecItemAdd(query as CFDictionary, nil)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Both are correct. Pick one style and use it consistently across your codebase.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Centralized Query Builder
|
|
217
|
+
|
|
218
|
+
Both research sources recommend centralizing query construction to prevent flag omissions and key typos:
|
|
219
|
+
|
|
220
|
+
```swift
|
|
221
|
+
enum KeychainQueryBuilder {
|
|
222
|
+
static func buildQuery(
|
|
223
|
+
forClass secClass: CFString = kSecClassGenericPassword,
|
|
224
|
+
account: String? = nil,
|
|
225
|
+
service: String? = nil,
|
|
226
|
+
accessGroup: String? = nil,
|
|
227
|
+
returnData: Bool = false,
|
|
228
|
+
returnAttributes: Bool = false,
|
|
229
|
+
matchLimit: CFString = kSecMatchLimitOne
|
|
230
|
+
) -> [String: Any] {
|
|
231
|
+
var query: [String: Any] = [kSecClass as String: secClass]
|
|
232
|
+
|
|
233
|
+
if let account { query[kSecAttrAccount as String] = account }
|
|
234
|
+
if let service { query[kSecAttrService as String] = service }
|
|
235
|
+
if let group = accessGroup { query[kSecAttrAccessGroup as String] = group }
|
|
236
|
+
if returnData { query[kSecReturnData as String] = kCFBooleanTrue! }
|
|
237
|
+
if returnAttributes { query[kSecReturnAttributes as String] = kCFBooleanTrue! }
|
|
238
|
+
query[kSecMatchLimit as String] = matchLimit
|
|
239
|
+
|
|
240
|
+
return query
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
This pattern ensures return flags are set deliberately and provides a single site to audit query construction.
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## OSStatus Error Handling
|
|
250
|
+
|
|
251
|
+
Never treat all non-zero `OSStatus` values as fatal errors. Several codes represent expected operational states:
|
|
252
|
+
|
|
253
|
+
| OSStatus Code | Constant | Meaning | Correct Response |
|
|
254
|
+
| ------------- | ----------------------------- | -------------------------------------------------- | ------------------------------------------ |
|
|
255
|
+
| `0` | `errSecSuccess` | Operation succeeded | Proceed normally |
|
|
256
|
+
| `-25299` | `errSecDuplicateItem` | Item already exists (on add) | Fall back to `SecItemUpdate` |
|
|
257
|
+
| `-25300` | `errSecItemNotFound` | No matching item found | Return `nil` / treat as success for delete |
|
|
258
|
+
| `-25308` | `errSecInteractionNotAllowed` | Device locked, data protection active | Retry later — **never delete** |
|
|
259
|
+
| `-25293` | `errSecUserCanceled` | User cancelled biometric prompt | Propagate cancellation to UI |
|
|
260
|
+
| `-50` | `errSecParam` | Invalid parameter / wrong dictionary keys | Developer error — fix query |
|
|
261
|
+
| `-25244` | `errSecNoSuchAttr` | Attribute not supported (data protection keychain) | Check for unsupported attributes |
|
|
262
|
+
|
|
263
|
+
Map raw codes to a domain-specific Swift error:
|
|
264
|
+
|
|
265
|
+
```swift
|
|
266
|
+
struct KeychainError: Error, CustomStringConvertible {
|
|
267
|
+
let status: OSStatus
|
|
268
|
+
|
|
269
|
+
var description: String {
|
|
270
|
+
let msg = SecCopyErrorMessageString(status, nil) as String? ?? "Unknown"
|
|
271
|
+
return "KeychainError(\(status)): \(msg)"
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**Logging safety:** Log only the query shape and resulting status code. Never log secret data (`kSecValueData`), tokens, or keys.
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## Actor-Isolated Keychain Manager (iOS 17+ / macOS 14+)
|
|
281
|
+
|
|
282
|
+
Every `SecItem*` function blocks the calling thread due to IPC to `securityd` and potential Secure Enclave round-trips. For biometry-protected items, the block can last several seconds during user authentication (WWDC 2014 Session 711).
|
|
283
|
+
|
|
284
|
+
❌ **@MainActor keychain access that blocks the UI:**
|
|
285
|
+
|
|
286
|
+
```swift
|
|
287
|
+
// ❌ WRONG — blocks main thread, freezes UI during securityd IPC
|
|
288
|
+
@MainActor
|
|
289
|
+
class SettingsViewModel: ObservableObject {
|
|
290
|
+
@Published var token: String = ""
|
|
291
|
+
|
|
292
|
+
func loadToken() {
|
|
293
|
+
let query: [CFString: Any] = [
|
|
294
|
+
kSecClass: kSecClassGenericPassword,
|
|
295
|
+
kSecAttrService: "com.example.app",
|
|
296
|
+
kSecAttrAccount: "authToken",
|
|
297
|
+
kSecReturnData: true,
|
|
298
|
+
kSecMatchLimit: kSecMatchLimitOne
|
|
299
|
+
]
|
|
300
|
+
var result: CFTypeRef?
|
|
301
|
+
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
302
|
+
if status == errSecSuccess, let data = result as? Data {
|
|
303
|
+
self.token = String(data: data, encoding: .utf8) ?? ""
|
|
304
|
+
}
|
|
305
|
+
// UI frozen for entire duration of securityd IPC + potential SE round-trip
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
✅ **Actor-isolated keychain manager with full CRUD:**
|
|
311
|
+
|
|
312
|
+
```swift
|
|
313
|
+
// ✅ CORRECT — dedicated actor keeps all SecItem calls off @MainActor
|
|
314
|
+
actor KeychainManager {
|
|
315
|
+
static let shared = KeychainManager()
|
|
316
|
+
|
|
317
|
+
private let service: String
|
|
318
|
+
|
|
319
|
+
init(service: String = Bundle.main.bundleIdentifier ?? "default") {
|
|
320
|
+
self.service = service
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// MARK: - Save (add-or-update)
|
|
324
|
+
|
|
325
|
+
func save(_ data: Data, for key: String,
|
|
326
|
+
accessibility: CFTypeRef = kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
|
|
327
|
+
) throws {
|
|
328
|
+
let baseQuery: [CFString: Any] = [
|
|
329
|
+
kSecClass: kSecClassGenericPassword,
|
|
330
|
+
kSecAttrService: service,
|
|
331
|
+
kSecAttrAccount: key
|
|
332
|
+
]
|
|
333
|
+
|
|
334
|
+
var addQuery = baseQuery
|
|
335
|
+
addQuery[kSecValueData] = data
|
|
336
|
+
addQuery[kSecAttrAccessible] = accessibility
|
|
337
|
+
|
|
338
|
+
let addStatus = SecItemAdd(addQuery as CFDictionary, nil)
|
|
339
|
+
|
|
340
|
+
switch addStatus {
|
|
341
|
+
case errSecSuccess:
|
|
342
|
+
return
|
|
343
|
+
case errSecDuplicateItem:
|
|
344
|
+
let updates: [CFString: Any] = [kSecValueData: data]
|
|
345
|
+
let updateStatus = SecItemUpdate(
|
|
346
|
+
baseQuery as CFDictionary,
|
|
347
|
+
updates as CFDictionary
|
|
348
|
+
)
|
|
349
|
+
guard updateStatus == errSecSuccess else {
|
|
350
|
+
throw KeychainError(status: updateStatus)
|
|
351
|
+
}
|
|
352
|
+
case errSecInteractionNotAllowed:
|
|
353
|
+
throw KeychainError(status: addStatus)
|
|
354
|
+
default:
|
|
355
|
+
throw KeychainError(status: addStatus)
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// MARK: - Load
|
|
360
|
+
|
|
361
|
+
func load(for key: String) throws -> Data? {
|
|
362
|
+
let query: [CFString: Any] = [
|
|
363
|
+
kSecClass: kSecClassGenericPassword,
|
|
364
|
+
kSecAttrService: service,
|
|
365
|
+
kSecAttrAccount: key,
|
|
366
|
+
kSecReturnData: true,
|
|
367
|
+
kSecMatchLimit: kSecMatchLimitOne
|
|
368
|
+
]
|
|
369
|
+
|
|
370
|
+
var result: CFTypeRef?
|
|
371
|
+
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
372
|
+
|
|
373
|
+
switch status {
|
|
374
|
+
case errSecSuccess:
|
|
375
|
+
return result as? Data
|
|
376
|
+
case errSecItemNotFound:
|
|
377
|
+
return nil
|
|
378
|
+
case errSecInteractionNotAllowed:
|
|
379
|
+
throw KeychainError(status: status)
|
|
380
|
+
default:
|
|
381
|
+
throw KeychainError(status: status)
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// MARK: - Delete (idempotent)
|
|
386
|
+
|
|
387
|
+
func delete(key: String) throws {
|
|
388
|
+
let query: [CFString: Any] = [
|
|
389
|
+
kSecClass: kSecClassGenericPassword,
|
|
390
|
+
kSecAttrService: service,
|
|
391
|
+
kSecAttrAccount: key
|
|
392
|
+
]
|
|
393
|
+
let status = SecItemDelete(query as CFDictionary)
|
|
394
|
+
guard status == errSecSuccess || status == errSecItemNotFound else {
|
|
395
|
+
throw KeychainError(status: status)
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// MARK: - List all accounts (attributes only — fast)
|
|
400
|
+
|
|
401
|
+
func allAccounts() throws -> [String] {
|
|
402
|
+
let query: [CFString: Any] = [
|
|
403
|
+
kSecClass: kSecClassGenericPassword,
|
|
404
|
+
kSecAttrService: service,
|
|
405
|
+
kSecMatchLimit: kSecMatchLimitAll,
|
|
406
|
+
kSecReturnAttributes: true // No kSecReturnData → skips SE round-trip
|
|
407
|
+
]
|
|
408
|
+
|
|
409
|
+
var result: CFTypeRef?
|
|
410
|
+
let status = SecItemCopyMatching(query as CFDictionary, &result)
|
|
411
|
+
|
|
412
|
+
switch status {
|
|
413
|
+
case errSecSuccess:
|
|
414
|
+
guard let items = result as? [[String: Any]] else { return [] }
|
|
415
|
+
return items.compactMap { $0[kSecAttrAccount as String] as? String }
|
|
416
|
+
case errSecItemNotFound:
|
|
417
|
+
return []
|
|
418
|
+
default:
|
|
419
|
+
throw KeychainError(status: status)
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
**Calling from SwiftUI:**
|
|
426
|
+
|
|
427
|
+
```swift
|
|
428
|
+
@MainActor
|
|
429
|
+
class AuthViewModel: ObservableObject {
|
|
430
|
+
@Published var isAuthenticated = false
|
|
431
|
+
|
|
432
|
+
func loadToken() async {
|
|
433
|
+
do {
|
|
434
|
+
// Crosses actor boundary — suspends, does NOT block MainActor
|
|
435
|
+
let data = try await KeychainManager.shared.load(for: "authToken")
|
|
436
|
+
isAuthenticated = data != nil
|
|
437
|
+
} catch {
|
|
438
|
+
isAuthenticated = false
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### Why Actors over GCD
|
|
445
|
+
|
|
446
|
+
| Dimension | Actor (iOS 17+) | GCD Serial Queue |
|
|
447
|
+
| --------------------- | --------------------------------- | --------------------------------------- |
|
|
448
|
+
| UI blocking | Low — compiler-enforced isolation | Low (if dispatched correctly) |
|
|
449
|
+
| Thread safety | Serialized by actor runtime | Manual — developer discipline |
|
|
450
|
+
| Readability | Linear async/await | Nested completion handlers |
|
|
451
|
+
| Compiler guarantees | Enforced `Sendable` + isolation | None — silent data races possible |
|
|
452
|
+
| Swift 6 compatibility | Native — actors are `Sendable` | Requires manual `@Sendable` annotations |
|
|
453
|
+
|
|
454
|
+
### Legacy GCD Pattern (iOS 13–16 codebases)
|
|
455
|
+
|
|
456
|
+
```swift
|
|
457
|
+
class LegacyKeychainManager {
|
|
458
|
+
private let queue = DispatchQueue(label: "com.app.keychain",
|
|
459
|
+
qos: .userInitiated)
|
|
460
|
+
|
|
461
|
+
func load(key: String, completion: @escaping (Result<Data?, Error>) -> Void) {
|
|
462
|
+
queue.async {
|
|
463
|
+
// ... SecItemCopyMatching on background queue ...
|
|
464
|
+
DispatchQueue.main.async { completion(result) }
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
## Performance Architecture
|
|
473
|
+
|
|
474
|
+
### Two-Tier Encryption and Query Cost
|
|
475
|
+
|
|
476
|
+
Because of the two-tier encryption design:
|
|
477
|
+
|
|
478
|
+
- **`kSecReturnAttributes` only** → uses cached metadata key → **fast** (no Secure Enclave round-trip)
|
|
479
|
+
- **`kSecReturnData`** → requires per-row secret key from Secure Enclave → **slower**
|
|
480
|
+
|
|
481
|
+
For listing operations, always use `kSecReturnAttributes` or `kSecReturnRef` and fetch secret data only for the specific item the user selects.
|
|
482
|
+
|
|
483
|
+
### Query Specificity
|
|
484
|
+
|
|
485
|
+
The underlying SQLite database benefits from narrow constraints. A query specifying only `kSecClass: kSecClassGenericPassword` with `kSecMatchLimitAll` performs a **full table scan**. Adding `kSecAttrService` and `kSecAttrAccount` enables indexed lookup. Always include all relevant uniqueness attributes in production queries.
|
|
486
|
+
|
|
487
|
+
### App Launch Performance
|
|
488
|
+
|
|
489
|
+
Keychain access during app launch is a measurable performance risk:
|
|
490
|
+
|
|
491
|
+
- Each call requires IPC to `securityd` plus potential Secure Enclave latency
|
|
492
|
+
- Items with `kSecAttrAccessibleWhenUnlocked` may be unavailable before first unlock (iOS can launch apps before the user unlocks — e.g., background refresh, VoIP pushes)
|
|
493
|
+
- **Best practice:** Defer keychain reads until actually needed. Never call SecItem synchronously in `application(_:didFinishLaunchingWithOptions:)`.
|
|
494
|
+
- Handle `errSecInteractionNotAllowed` gracefully — never destructively.
|
|
495
|
+
|
|
496
|
+
### Batch Operations
|
|
497
|
+
|
|
498
|
+
There is **no batch API** for SecItem. Each function operates individually with one partial exception: `SecItemAdd` supports `kSecUseItemList` to add multiple certificates or keys (not passwords) in a single call. For batch reads, `SecItemCopyMatching` with `kSecMatchLimitAll` retrieves all matching items at once.
|
|
499
|
+
|
|
500
|
+
---
|
|
501
|
+
|
|
502
|
+
## macOS Keychain Routing (TN3137)
|
|
503
|
+
|
|
504
|
+
On macOS, the SecItem API can target two different implementations:
|
|
505
|
+
|
|
506
|
+
| Implementation | Activated By | Behavior |
|
|
507
|
+
| ------------------------------ | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
508
|
+
| **Legacy file-based keychain** | Default on macOS (without opt-in) | Silently ignores unsupported attributes; inconsistent `kSecMatchLimit` defaults; different `SecItemAdd` return types |
|
|
509
|
+
| **Data protection keychain** | `kSecUseDataProtectionKeychain: true` (macOS 10.15+) or `kSecAttrSynchronizable: true` | Parity with iOS; required for iCloud Keychain sync, biometric protection, and Secure Enclave key storage |
|
|
510
|
+
|
|
511
|
+
**Modern apps must always target the data protection keychain.** Mac Catalyst and iOS Apps on Mac use it automatically.
|
|
512
|
+
|
|
513
|
+
```swift
|
|
514
|
+
// macOS: Always opt into data protection keychain
|
|
515
|
+
var query: [CFString: Any] = [
|
|
516
|
+
kSecClass: kSecClassGenericPassword,
|
|
517
|
+
kSecAttrService: "com.example.app",
|
|
518
|
+
kSecAttrAccount: "token"
|
|
519
|
+
]
|
|
520
|
+
#if os(macOS)
|
|
521
|
+
query[kSecUseDataProtectionKeychain] = true
|
|
522
|
+
#endif
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
The file-based keychain's shim layer has documented bugs — it silently ignores unsupported attributes where the data protection keychain correctly returns `errSecNoSuchAttr` (-25244). Debugging keychain issues on macOS often starts with confirming which implementation is in use.
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
## Accessibility and Data Protection Classes
|
|
530
|
+
|
|
531
|
+
The `kSecAttrAccessible` attribute controls when a keychain item's secret data can be decrypted. Brief guidance here; see `keychain-access-control.md` for full coverage.
|
|
532
|
+
|
|
533
|
+
| Constant | Available When | Survives Backup? | Use Case |
|
|
534
|
+
| -------------------------------------------------- | -------------------------------- | ---------------- | -------------------------------------------- |
|
|
535
|
+
| `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` | After unlock, until lock | No (device-only) | Default for most secrets |
|
|
536
|
+
| `kSecAttrAccessibleAfterFirstUnlock` | After first unlock until restart | Yes | Background processing tokens |
|
|
537
|
+
| `kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly` | Only if passcode set + unlocked | No | Highest-sensitivity data (OWASP recommended) |
|
|
538
|
+
| `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` | After first unlock until restart | No | Background + device-only |
|
|
539
|
+
|
|
540
|
+
**Deprecated:** `kSecAttrAccessibleAlways` — deprecated in iOS 12, unsupported on Apple Silicon Macs. Never use.
|
|
541
|
+
|
|
542
|
+
---
|
|
543
|
+
|
|
544
|
+
## Cross-References
|
|
545
|
+
|
|
546
|
+
- **Item class deep dive** (required vs optional attributes per kSecClass) → `keychain-item-classes.md`
|
|
547
|
+
- **Access control flags and SecAccessControl** → `keychain-access-control.md`
|
|
548
|
+
- **Biometric-gated keychain access** (LAContext integration) → `biometric-authentication.md`
|
|
549
|
+
- **Secure Enclave key storage** → `secure-enclave.md`
|
|
550
|
+
- **Credential lifecycle patterns** (OAuth tokens, API keys) → `credential-storage-patterns.md`
|
|
551
|
+
- **Access groups and sharing** → `keychain-sharing.md`
|
|
552
|
+
- **Testing keychain code** (mocks, CI/CD) → `testing-security-code.md`
|
|
553
|
+
- **Common anti-patterns** (comprehensive catalog) → `common-anti-patterns.md`
|
|
554
|
+
|
|
555
|
+
---
|
|
556
|
+
|
|
557
|
+
## Authoritative References
|
|
558
|
+
|
|
559
|
+
| Source | Relevance |
|
|
560
|
+
| ------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- |
|
|
561
|
+
| [Keychain Services](https://developer.apple.com/documentation/security/keychain_services) | Main API landing page |
|
|
562
|
+
| [TN3137: On Mac Keychain APIs and Implementations](https://developer.apple.com/documentation/technotes/tn3137-on-mac-keychains) | macOS data protection vs file-based routing |
|
|
563
|
+
| Quinn "The Eskimo!" — "SecItem: Fundamentals" / "SecItem: Pitfalls and Best Practices" | Most practical DTS reference, updated through 2025 |
|
|
564
|
+
| [Apple Platform Security Guide](https://support.apple.com/guide/security/welcome/web) — Keychain Data Protection chapter | Two-tier encryption architecture |
|
|
565
|
+
| WWDC 2014 Session 711 — "Keychain and Authentication with Touch ID" | Touch ID/keychain integration patterns |
|
|
566
|
+
| WWDC 2019 Session 516 — "What's New in Authentication" | Modern credential management |
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
570
|
+
## Contradictions Between Research Sources
|
|
571
|
+
|
|
572
|
+
During cross-validation of research inputs, the following discrepancies were noted:
|
|
573
|
+
|
|
574
|
+
1. **Dictionary key type convention:** Claude source uses `[CFString: Any]`; Parallel source uses `[String: Any]` with `kSec* as String` casts. **Resolution:** Both are correct. The `[CFString: Any]` style is slightly more concise; the `[String: Any]` style is more common in community code. This file uses `[CFString: Any]` for conciseness but shows both styles in the String Keys section.
|
|
575
|
+
|
|
576
|
+
2. **Default accessibility recommendation:** Claude source cites OWASP recommending `kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly` for highly sensitive data; Parallel source defaults to `kSecAttrAccessibleWhenUnlockedThisDeviceOnly`. **Resolution:** Both are valid for different threat models. `WhenPasscodeSet` is strongest but items are deleted if the user removes their passcode. `WhenUnlockedThisDeviceOnly` is the safe general default for foreground-only access. The actor manager example uses `AfterFirstUnlockThisDeviceOnly` for background compatibility while remaining device-bound.
|
|
577
|
+
|
|
578
|
+
3. **`kSecReturnData` + `kSecMatchLimitAll` restriction:** Parallel source claims this combination is restricted for password classes. Claude source does not mention this. **Resolution:** This restriction exists in some OS versions / keychain implementations. Safest practice is to use `kSecReturnRef` or `kSecReturnAttributes` with `LimitAll`, then fetch data per-item. Noted in the Return Type Cheat Sheet.
|
|
579
|
+
|
|
580
|
+
---
|
|
581
|
+
|
|
582
|
+
## Summary Checklist
|
|
583
|
+
|
|
584
|
+
Before shipping keychain code, verify:
|
|
585
|
+
|
|
586
|
+
1. **OSStatus checked on every call** — exhaustive `switch` covering at minimum `errSecSuccess`, `errSecDuplicateItem`, `errSecItemNotFound`, `errSecInteractionNotAllowed`; no ignored return values
|
|
587
|
+
2. **Add-or-update pattern implemented** — `SecItemAdd` catches `-25299` and falls back to `SecItemUpdate`; duplicate saves never crash or silently fail
|
|
588
|
+
3. **Return flags explicitly set** — every `SecItemCopyMatching` call includes at least one `kSecReturn*` flag; no "success but nil" bugs
|
|
589
|
+
4. **CFTypeRef cast matches flags** — cast type corresponds to the combination of return flags and match limit (see Return Type Cheat Sheet)
|
|
590
|
+
5. **Zero SecItem calls on @MainActor** — all keychain access isolated in a dedicated `actor` (iOS 17+) or serial `DispatchQueue` (iOS 13–16)
|
|
591
|
+
6. **Fresh dictionaries per call** — no dictionary reuse across SecItem functions; add dict, query dict, and update dict are separate
|
|
592
|
+
7. **kSec\* constants used** — no raw string literals for dictionary keys; using either `[CFString: Any]` or `[String: Any]` with `as String` casts
|
|
593
|
+
8. **Queries are specific** — `kSecAttrService` + `kSecAttrAccount` included for GenericPassword; `kSecMatchLimitOne` used unless enumeration is needed
|
|
594
|
+
9. **Delete treats not-found as success** — `errSecItemNotFound` on delete is a valid postcondition, not an error
|
|
595
|
+
10. **macOS targets data protection keychain** — `kSecUseDataProtectionKeychain: true` set for macOS targets (automatic for Catalyst/iOS-on-Mac)
|
|
596
|
+
11. **errSecInteractionNotAllowed handled non-destructively** — device-locked state triggers retry-later logic, never delete-and-recreate
|