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.
Files changed (633) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/.claude-plugin/plugin.json +9 -3
  3. package/CHANGELOG.md +112 -0
  4. package/README.md +2 -2
  5. package/agents/a11y-architect.md +166 -0
  6. package/agents/business-model.md +80 -29
  7. package/agents/code-architect.md +75 -0
  8. package/agents/code-reviewer.md +255 -0
  9. package/agents/code-simplifier.md +64 -0
  10. package/agents/design-brand-guardian.md +293 -53
  11. package/agents/design-critic.md +139 -0
  12. package/agents/design-inclusive-visuals-specialist.md +6 -19
  13. package/agents/design-ui-designer.md +335 -56
  14. package/agents/design-ux-architect.md +403 -55
  15. package/agents/design-ux-researcher.md +264 -49
  16. package/agents/engineering-ai-engineer.md +26 -36
  17. package/agents/engineering-backend-architect.md +185 -36
  18. package/agents/engineering-data-engineer.md +225 -43
  19. package/agents/engineering-devops-automator.md +227 -74
  20. package/agents/engineering-frontend-developer.md +210 -34
  21. package/agents/engineering-mobile-app-builder.md +6 -1
  22. package/agents/engineering-rapid-prototyper.md +30 -9
  23. package/agents/engineering-security-engineer.md +263 -61
  24. package/agents/engineering-senior-developer.md +128 -19
  25. package/agents/engineering-sre.md +84 -0
  26. package/agents/engineering-technical-writer.md +285 -41
  27. package/agents/feature-intel.md +110 -0
  28. package/agents/ios-app-review-guardian.md +66 -0
  29. package/agents/ios-foundation-models-specialist.md +64 -0
  30. package/agents/ios-storekit-specialist.md +59 -0
  31. package/agents/ios-swift-architect.md +129 -0
  32. package/agents/ios-swift-search.md +137 -0
  33. package/agents/ios-swift-ui-design.md +136 -0
  34. package/agents/marketing-app-store-optimizer.md +246 -64
  35. package/agents/planner.md +216 -0
  36. package/agents/pr-test-analyzer.md +63 -0
  37. package/agents/product-feedback-synthesizer.md +8 -2
  38. package/agents/refactor-cleaner.md +102 -0
  39. package/agents/security-reviewer.md +128 -0
  40. package/agents/silent-failure-hunter.md +54 -0
  41. package/agents/swift-build-resolver.md +119 -0
  42. package/agents/swift-reviewer.md +112 -0
  43. package/agents/tech-feasibility.md +21 -1
  44. package/agents/testing-api-tester.md +236 -59
  45. package/agents/testing-evidence-collector.md +26 -1
  46. package/agents/testing-performance-benchmarker.md +21 -1
  47. package/agents/testing-reality-checker.md +6 -1
  48. package/agents/visual-research.md +116 -0
  49. package/bin/adapters/cycle-counter-tool.ts +155 -0
  50. package/bin/adapters/scribe-tool.ts +71 -0
  51. package/bin/adapters/state-save-tool.ts +130 -0
  52. package/bin/adapters/write-lease-tool.ts +127 -0
  53. package/bin/buildanything-runtime.js +15 -0
  54. package/bin/buildanything-runtime.ts +328 -0
  55. package/bin/setup.js +83 -8
  56. package/commands/add-feature.md +2 -0
  57. package/commands/build.md +752 -332
  58. package/commands/fix.md +65 -0
  59. package/commands/self-check.md +121 -0
  60. package/commands/setup.md +114 -0
  61. package/commands/ux-review.md +63 -0
  62. package/commands/verify.md +69 -0
  63. package/docs/migration/agents.yaml +729 -0
  64. package/docs/migration/phase-graph.yaml +1088 -0
  65. package/docs/migration/sdk-host-compat.md +18 -0
  66. package/hooks/compile-writer-owner-cache.ts +171 -0
  67. package/hooks/hooks.json +36 -0
  68. package/hooks/pre-tool-use +19 -0
  69. package/hooks/pre-tool-use.ts +776 -0
  70. package/hooks/record-mode-transitions.ts +178 -0
  71. package/hooks/session-start +89 -2
  72. package/hooks/subagent-start +17 -0
  73. package/hooks/subagent-start.ts +471 -0
  74. package/hooks/subagent-stop +17 -0
  75. package/hooks/subagent-stop.ts +153 -0
  76. package/package.json +28 -5
  77. package/protocols/architecture-schema.md +171 -0
  78. package/protocols/build-fix.md +52 -0
  79. package/protocols/cleanup.md +54 -0
  80. package/protocols/decision-log.md +131 -0
  81. package/protocols/eval-harness.md +61 -0
  82. package/protocols/fake-data-detector.md +64 -0
  83. package/protocols/ios-context.md +234 -0
  84. package/protocols/ios-frameworks-map.md +323 -0
  85. package/protocols/ios-phase-branches.md +337 -0
  86. package/protocols/ios-preflight.md +27 -0
  87. package/protocols/launch-readiness.md +258 -0
  88. package/protocols/metric-loop.md +153 -0
  89. package/protocols/smoke-test.md +118 -0
  90. package/protocols/state-schema.json +388 -0
  91. package/protocols/state-schema.md +172 -0
  92. package/protocols/verify.md +127 -0
  93. package/protocols/visual-dna.md +185 -0
  94. package/protocols/web-phase-branches.md +351 -0
  95. package/skills/ios/_VENDORED.md +62 -0
  96. package/skills/ios/activitykit/LICENSE +131 -0
  97. package/skills/ios/activitykit/SKILL.md +505 -0
  98. package/skills/ios/activitykit/references/activitykit-patterns.md +868 -0
  99. package/skills/ios/app-intents/LICENSE +131 -0
  100. package/skills/ios/app-intents/SKILL.md +494 -0
  101. package/skills/ios/app-intents/references/appintents-advanced.md +1076 -0
  102. package/skills/ios/app-store-connect-metadata/SKILL.md +148 -0
  103. package/skills/ios/apple-on-device-ai/LICENSE +131 -0
  104. package/skills/ios/apple-on-device-ai/SKILL.md +505 -0
  105. package/skills/ios/apple-on-device-ai/references/coreml-conversion.md +425 -0
  106. package/skills/ios/apple-on-device-ai/references/coreml-optimization.md +344 -0
  107. package/skills/ios/apple-on-device-ai/references/foundation-models.md +508 -0
  108. package/skills/ios/apple-on-device-ai/references/mlx-swift.md +285 -0
  109. package/skills/ios/asc-privacy-manifest/SKILL.md +350 -0
  110. package/skills/ios/hig-components-content/SKILL.md +86 -0
  111. package/skills/ios/hig-components-content/references/activity-views.md +79 -0
  112. package/skills/ios/hig-components-content/references/charts.md +180 -0
  113. package/skills/ios/hig-components-content/references/collections.md +48 -0
  114. package/skills/ios/hig-components-content/references/color-wells.md +42 -0
  115. package/skills/ios/hig-components-content/references/image-views.md +82 -0
  116. package/skills/ios/hig-components-content/references/image-wells.md +34 -0
  117. package/skills/ios/hig-components-content/references/lockups.md +78 -0
  118. package/skills/ios/hig-components-content/references/web-views.md +36 -0
  119. package/skills/ios/hig-components-controls/SKILL.md +88 -0
  120. package/skills/ios/hig-components-controls/references/combo-boxes.md +40 -0
  121. package/skills/ios/hig-components-controls/references/controls.md +112 -0
  122. package/skills/ios/hig-components-controls/references/gauges.md +74 -0
  123. package/skills/ios/hig-components-controls/references/labels.md +92 -0
  124. package/skills/ios/hig-components-controls/references/pickers.md +128 -0
  125. package/skills/ios/hig-components-controls/references/rating-indicators.md +38 -0
  126. package/skills/ios/hig-components-controls/references/segmented-controls.md +94 -0
  127. package/skills/ios/hig-components-controls/references/sliders.md +92 -0
  128. package/skills/ios/hig-components-controls/references/steppers.md +40 -0
  129. package/skills/ios/hig-components-controls/references/text-fields.md +88 -0
  130. package/skills/ios/hig-components-controls/references/text-views.md +56 -0
  131. package/skills/ios/hig-components-controls/references/toggles.md +127 -0
  132. package/skills/ios/hig-components-controls/references/token-fields.md +48 -0
  133. package/skills/ios/hig-components-controls/references/virtual-keyboards.md +156 -0
  134. package/skills/ios/hig-components-dialogs/SKILL.md +76 -0
  135. package/skills/ios/hig-components-dialogs/references/action-sheets.md +74 -0
  136. package/skills/ios/hig-components-dialogs/references/alerts.md +158 -0
  137. package/skills/ios/hig-components-dialogs/references/digit-entry-views.md +32 -0
  138. package/skills/ios/hig-components-dialogs/references/popovers.md +81 -0
  139. package/skills/ios/hig-components-dialogs/references/sheets.md +157 -0
  140. package/skills/ios/hig-components-layout/SKILL.md +99 -0
  141. package/skills/ios/hig-components-layout/references/boxes.md +48 -0
  142. package/skills/ios/hig-components-layout/references/column-views.md +44 -0
  143. package/skills/ios/hig-components-layout/references/lists-and-tables.md +99 -0
  144. package/skills/ios/hig-components-layout/references/ornaments.md +56 -0
  145. package/skills/ios/hig-components-layout/references/outline-views.md +64 -0
  146. package/skills/ios/hig-components-layout/references/panels.md +75 -0
  147. package/skills/ios/hig-components-layout/references/scroll-views.md +123 -0
  148. package/skills/ios/hig-components-layout/references/sidebars.md +109 -0
  149. package/skills/ios/hig-components-layout/references/split-views.md +110 -0
  150. package/skills/ios/hig-components-layout/references/tab-bars.md +173 -0
  151. package/skills/ios/hig-components-layout/references/tab-views.md +68 -0
  152. package/skills/ios/hig-components-layout/references/windows.md +188 -0
  153. package/skills/ios/hig-components-menus/SKILL.md +81 -0
  154. package/skills/ios/hig-components-menus/references/action-button.md +61 -0
  155. package/skills/ios/hig-components-menus/references/buttons.md +261 -0
  156. package/skills/ios/hig-components-menus/references/context-menus.md +105 -0
  157. package/skills/ios/hig-components-menus/references/disclosure-controls.md +84 -0
  158. package/skills/ios/hig-components-menus/references/dock-menus.md +40 -0
  159. package/skills/ios/hig-components-menus/references/edit-menus.md +88 -0
  160. package/skills/ios/hig-components-menus/references/menus.md +171 -0
  161. package/skills/ios/hig-components-menus/references/pop-up-buttons.md +70 -0
  162. package/skills/ios/hig-components-menus/references/pull-down-buttons.md +77 -0
  163. package/skills/ios/hig-components-menus/references/the-menu-bar.md +303 -0
  164. package/skills/ios/hig-components-menus/references/toolbars.md +256 -0
  165. package/skills/ios/hig-components-search/SKILL.md +68 -0
  166. package/skills/ios/hig-components-search/references/page-controls.md +120 -0
  167. package/skills/ios/hig-components-search/references/path-controls.md +40 -0
  168. package/skills/ios/hig-components-search/references/search-fields.md +189 -0
  169. package/skills/ios/hig-components-status/SKILL.md +80 -0
  170. package/skills/ios/hig-components-status/references/activity-rings.md +105 -0
  171. package/skills/ios/hig-components-status/references/progress-indicators.md +116 -0
  172. package/skills/ios/hig-components-status/references/status-bars.md +38 -0
  173. package/skills/ios/hig-components-system/SKILL.md +88 -0
  174. package/skills/ios/hig-components-system/references/app-clips.md +387 -0
  175. package/skills/ios/hig-components-system/references/app-shortcuts.md +114 -0
  176. package/skills/ios/hig-components-system/references/complications.md +425 -0
  177. package/skills/ios/hig-components-system/references/home-screen-quick-actions.md +42 -0
  178. package/skills/ios/hig-components-system/references/live-activities.md +442 -0
  179. package/skills/ios/hig-components-system/references/notifications.md +153 -0
  180. package/skills/ios/hig-components-system/references/top-shelf.md +135 -0
  181. package/skills/ios/hig-components-system/references/watch-faces.md +40 -0
  182. package/skills/ios/hig-components-system/references/widgets.md +517 -0
  183. package/skills/ios/hig-foundations/SKILL.md +98 -0
  184. package/skills/ios/hig-foundations/references/accessibility.md +291 -0
  185. package/skills/ios/hig-foundations/references/app-icons.md +210 -0
  186. package/skills/ios/hig-foundations/references/branding.md +44 -0
  187. package/skills/ios/hig-foundations/references/color.md +274 -0
  188. package/skills/ios/hig-foundations/references/dark-mode.md +116 -0
  189. package/skills/ios/hig-foundations/references/icons.md +263 -0
  190. package/skills/ios/hig-foundations/references/images.md +176 -0
  191. package/skills/ios/hig-foundations/references/immersive-experiences.md +174 -0
  192. package/skills/ios/hig-foundations/references/inclusion.md +189 -0
  193. package/skills/ios/hig-foundations/references/layout.md +425 -0
  194. package/skills/ios/hig-foundations/references/materials.md +238 -0
  195. package/skills/ios/hig-foundations/references/motion.md +103 -0
  196. package/skills/ios/hig-foundations/references/privacy.md +231 -0
  197. package/skills/ios/hig-foundations/references/right-to-left.md +206 -0
  198. package/skills/ios/hig-foundations/references/sf-symbols.md +310 -0
  199. package/skills/ios/hig-foundations/references/spatial-layout.md +142 -0
  200. package/skills/ios/hig-foundations/references/typography.md +1146 -0
  201. package/skills/ios/hig-foundations/references/writing.md +91 -0
  202. package/skills/ios/hig-inputs/SKILL.md +94 -0
  203. package/skills/ios/hig-inputs/references/apple-pencil-and-scribble.md +148 -0
  204. package/skills/ios/hig-inputs/references/camera-control.md +107 -0
  205. package/skills/ios/hig-inputs/references/digital-crown.md +83 -0
  206. package/skills/ios/hig-inputs/references/eyes.md +120 -0
  207. package/skills/ios/hig-inputs/references/focus-and-selection.md +120 -0
  208. package/skills/ios/hig-inputs/references/game-controls.md +156 -0
  209. package/skills/ios/hig-inputs/references/gestures.md +208 -0
  210. package/skills/ios/hig-inputs/references/gyro-and-accelerometer.md +40 -0
  211. package/skills/ios/hig-inputs/references/keyboards.md +234 -0
  212. package/skills/ios/hig-inputs/references/nearby-interactions.md +70 -0
  213. package/skills/ios/hig-inputs/references/pointing-devices.md +237 -0
  214. package/skills/ios/hig-inputs/references/remotes.md +67 -0
  215. package/skills/ios/hig-inputs/references/spatial-interactions.md +70 -0
  216. package/skills/ios/hig-patterns/SKILL.md +104 -0
  217. package/skills/ios/hig-patterns/references/charting-data.md +81 -0
  218. package/skills/ios/hig-patterns/references/collaboration-and-sharing.md +86 -0
  219. package/skills/ios/hig-patterns/references/drag-and-drop.md +134 -0
  220. package/skills/ios/hig-patterns/references/entering-data.md +69 -0
  221. package/skills/ios/hig-patterns/references/feedback.md +67 -0
  222. package/skills/ios/hig-patterns/references/file-management.md +135 -0
  223. package/skills/ios/hig-patterns/references/going-full-screen.md +79 -0
  224. package/skills/ios/hig-patterns/references/launching.md +81 -0
  225. package/skills/ios/hig-patterns/references/live-viewing-apps.md +79 -0
  226. package/skills/ios/hig-patterns/references/loading.md +59 -0
  227. package/skills/ios/hig-patterns/references/managing-accounts.md +107 -0
  228. package/skills/ios/hig-patterns/references/managing-notifications.md +99 -0
  229. package/skills/ios/hig-patterns/references/modality.md +82 -0
  230. package/skills/ios/hig-patterns/references/multitasking.md +131 -0
  231. package/skills/ios/hig-patterns/references/offering-help.md +117 -0
  232. package/skills/ios/hig-patterns/references/onboarding.md +69 -0
  233. package/skills/ios/hig-patterns/references/playing-audio.md +124 -0
  234. package/skills/ios/hig-patterns/references/playing-haptics.md +280 -0
  235. package/skills/ios/hig-patterns/references/playing-video.md +180 -0
  236. package/skills/ios/hig-patterns/references/printing.md +50 -0
  237. package/skills/ios/hig-patterns/references/ratings-and-reviews.md +48 -0
  238. package/skills/ios/hig-patterns/references/searching.md +70 -0
  239. package/skills/ios/hig-patterns/references/settings.md +84 -0
  240. package/skills/ios/hig-patterns/references/undo-and-redo.md +58 -0
  241. package/skills/ios/hig-patterns/references/workouts.md +76 -0
  242. package/skills/ios/hig-platforms/SKILL.md +84 -0
  243. package/skills/ios/hig-platforms/references/designing-for-games.md +159 -0
  244. package/skills/ios/hig-platforms/references/designing-for-ios.md +66 -0
  245. package/skills/ios/hig-platforms/references/designing-for-ipados.md +64 -0
  246. package/skills/ios/hig-platforms/references/designing-for-macos.md +70 -0
  247. package/skills/ios/hig-platforms/references/designing-for-tvos.md +68 -0
  248. package/skills/ios/hig-platforms/references/designing-for-visionos.md +85 -0
  249. package/skills/ios/hig-platforms/references/designing-for-watchos.md +74 -0
  250. package/skills/ios/hig-project-context/SKILL.md +133 -0
  251. package/skills/ios/hig-technologies/SKILL.md +107 -0
  252. package/skills/ios/hig-technologies/references/airplay.md +125 -0
  253. package/skills/ios/hig-technologies/references/always-on.md +62 -0
  254. package/skills/ios/hig-technologies/references/apple-pay.md +441 -0
  255. package/skills/ios/hig-technologies/references/augmented-reality.md +247 -0
  256. package/skills/ios/hig-technologies/references/carekit.md +224 -0
  257. package/skills/ios/hig-technologies/references/carplay.md +119 -0
  258. package/skills/ios/hig-technologies/references/game-center.md +343 -0
  259. package/skills/ios/hig-technologies/references/generative-ai.md +110 -0
  260. package/skills/ios/hig-technologies/references/healthkit.md +120 -0
  261. package/skills/ios/hig-technologies/references/homekit.md +343 -0
  262. package/skills/ios/hig-technologies/references/icloud.md +52 -0
  263. package/skills/ios/hig-technologies/references/id-verifier.md +73 -0
  264. package/skills/ios/hig-technologies/references/imessage-apps-and-stickers.md +105 -0
  265. package/skills/ios/hig-technologies/references/in-app-purchase.md +263 -0
  266. package/skills/ios/hig-technologies/references/live-photos.md +54 -0
  267. package/skills/ios/hig-technologies/references/mac-catalyst.md +216 -0
  268. package/skills/ios/hig-technologies/references/machine-learning.md +394 -0
  269. package/skills/ios/hig-technologies/references/maps.md +221 -0
  270. package/skills/ios/hig-technologies/references/nfc.md +51 -0
  271. package/skills/ios/hig-technologies/references/photo-editing.md +40 -0
  272. package/skills/ios/hig-technologies/references/researchkit.md +134 -0
  273. package/skills/ios/hig-technologies/references/shareplay.md +142 -0
  274. package/skills/ios/hig-technologies/references/shazamkit.md +47 -0
  275. package/skills/ios/hig-technologies/references/sign-in-with-apple.md +288 -0
  276. package/skills/ios/hig-technologies/references/siri.md +523 -0
  277. package/skills/ios/hig-technologies/references/tap-to-pay-on-iphone.md +208 -0
  278. package/skills/ios/hig-technologies/references/voiceover.md +90 -0
  279. package/skills/ios/hig-technologies/references/wallet.md +420 -0
  280. package/skills/ios/ios-26-platform/SKILL.md +53 -0
  281. package/skills/ios/ios-26-platform/references/automatic-adoption.md +161 -0
  282. package/skills/ios/ios-26-platform/references/backward-compat.md +238 -0
  283. package/skills/ios/ios-26-platform/references/liquid-glass.md +255 -0
  284. package/skills/ios/ios-26-platform/references/swiftui-apis.md +277 -0
  285. package/skills/ios/ios-26-platform/references/toolbar-navigation.md +250 -0
  286. package/skills/ios/ios-bootstrap/SKILL.md +107 -0
  287. package/skills/ios/ios-bootstrap/references/apple-docs-mcp-config.md +28 -0
  288. package/skills/ios/ios-bootstrap/references/new-project-dialog.md +41 -0
  289. package/skills/ios/ios-bootstrap/references/xcode-mcp-config.md +29 -0
  290. package/skills/ios/ios-debugger-agent/LICENSE +21 -0
  291. package/skills/ios/ios-debugger-agent/SKILL.md +58 -0
  292. package/skills/ios/ios-debugger-agent/agents/openai.yaml +4 -0
  293. package/skills/ios/ios-entitlements-generator/SKILL.md +47 -0
  294. package/skills/ios/ios-info-plist-hardening/SKILL.md +130 -0
  295. package/skills/ios/ios-maestro-flow-author/SKILL.md +68 -0
  296. package/skills/ios/ios-maestro-flow-author/references/input-and-scroll.yaml +17 -0
  297. package/skills/ios/ios-maestro-flow-author/references/modal-and-dismiss.yaml +14 -0
  298. package/skills/ios/ios-maestro-flow-author/references/onboarding-flow.yaml +16 -0
  299. package/skills/ios/ios-maestro-flow-author/references/tab-navigation.yaml +13 -0
  300. package/skills/ios/ios-maestro-flow-author/references/tap-and-assert.yaml +9 -0
  301. package/skills/ios/swift-accessibility/LICENSE +21 -0
  302. package/skills/ios/swift-accessibility/SKILL.md +371 -0
  303. package/skills/ios/swift-accessibility/examples/before-after-appkit.md +446 -0
  304. package/skills/ios/swift-accessibility/examples/before-after-swiftui.md +441 -0
  305. package/skills/ios/swift-accessibility/examples/before-after-uikit.md +464 -0
  306. package/skills/ios/swift-accessibility/references/assistive-access.md +441 -0
  307. package/skills/ios/swift-accessibility/references/display-settings.md +491 -0
  308. package/skills/ios/swift-accessibility/references/dynamic-type.md +420 -0
  309. package/skills/ios/swift-accessibility/references/media-accessibility.md +421 -0
  310. package/skills/ios/swift-accessibility/references/motor-input.md +393 -0
  311. package/skills/ios/swift-accessibility/references/nutrition-labels.md +362 -0
  312. package/skills/ios/swift-accessibility/references/platform-specifics.md +515 -0
  313. package/skills/ios/swift-accessibility/references/semantic-structure.md +585 -0
  314. package/skills/ios/swift-accessibility/references/testing-auditing.md +507 -0
  315. package/skills/ios/swift-accessibility/references/voice-control.md +317 -0
  316. package/skills/ios/swift-accessibility/references/voiceover-swiftui.md +584 -0
  317. package/skills/ios/swift-accessibility/references/voiceover-uikit.md +519 -0
  318. package/skills/ios/swift-accessibility/references/wcag-mapping.md +167 -0
  319. package/skills/ios/swift-accessibility/resources/audit-template.swift +128 -0
  320. package/skills/ios/swift-accessibility/resources/qa-checklist.md +258 -0
  321. package/skills/ios/swift-actor-persistence/SKILL.md +143 -0
  322. package/skills/ios/swift-concurrency/LICENSE +21 -0
  323. package/skills/ios/swift-concurrency/SKILL.md +171 -0
  324. package/skills/ios/swift-concurrency/references/_index.md +50 -0
  325. package/skills/ios/swift-concurrency/references/actors.md +660 -0
  326. package/skills/ios/swift-concurrency/references/async-algorithms.md +847 -0
  327. package/skills/ios/swift-concurrency/references/async-await-basics.md +266 -0
  328. package/skills/ios/swift-concurrency/references/async-sequences.md +710 -0
  329. package/skills/ios/swift-concurrency/references/core-data.md +560 -0
  330. package/skills/ios/swift-concurrency/references/glossary.md +135 -0
  331. package/skills/ios/swift-concurrency/references/linting.md +155 -0
  332. package/skills/ios/swift-concurrency/references/memory-management.md +569 -0
  333. package/skills/ios/swift-concurrency/references/migration.md +1104 -0
  334. package/skills/ios/swift-concurrency/references/performance.md +593 -0
  335. package/skills/ios/swift-concurrency/references/sendable.md +598 -0
  336. package/skills/ios/swift-concurrency/references/tasks.md +636 -0
  337. package/skills/ios/swift-concurrency/references/testing.md +592 -0
  338. package/skills/ios/swift-concurrency/references/threading.md +495 -0
  339. package/skills/ios/swift-concurrency-6-2/SKILL.md +216 -0
  340. package/skills/ios/swift-protocol-di-testing/SKILL.md +190 -0
  341. package/skills/ios/swift-security-expert/LICENSE +21 -0
  342. package/skills/ios/swift-security-expert/SKILL.md +470 -0
  343. package/skills/ios/swift-security-expert/references/biometric-authentication.md +565 -0
  344. package/skills/ios/swift-security-expert/references/certificate-trust.md +592 -0
  345. package/skills/ios/swift-security-expert/references/common-anti-patterns.md +690 -0
  346. package/skills/ios/swift-security-expert/references/compliance-owasp-mapping.md +537 -0
  347. package/skills/ios/swift-security-expert/references/credential-storage-patterns.md +721 -0
  348. package/skills/ios/swift-security-expert/references/cryptokit-public-key.md +505 -0
  349. package/skills/ios/swift-security-expert/references/cryptokit-symmetric.md +497 -0
  350. package/skills/ios/swift-security-expert/references/keychain-access-control.md +508 -0
  351. package/skills/ios/swift-security-expert/references/keychain-fundamentals.md +596 -0
  352. package/skills/ios/swift-security-expert/references/keychain-item-classes.md +476 -0
  353. package/skills/ios/swift-security-expert/references/keychain-sharing.md +458 -0
  354. package/skills/ios/swift-security-expert/references/migration-legacy-stores.md +727 -0
  355. package/skills/ios/swift-security-expert/references/secure-enclave.md +539 -0
  356. package/skills/ios/swift-security-expert/references/testing-security-code.md +781 -0
  357. package/skills/ios/swift-testing-expert/LICENSE +21 -0
  358. package/skills/ios/swift-testing-expert/SKILL.md +79 -0
  359. package/skills/ios/swift-testing-expert/references/_index.md +12 -0
  360. package/skills/ios/swift-testing-expert/references/async-testing-and-waiting.md +127 -0
  361. package/skills/ios/swift-testing-expert/references/expectations.md +145 -0
  362. package/skills/ios/swift-testing-expert/references/fundamentals.md +141 -0
  363. package/skills/ios/swift-testing-expert/references/migration-from-xctest.md +127 -0
  364. package/skills/ios/swift-testing-expert/references/parallelization-and-isolation.md +95 -0
  365. package/skills/ios/swift-testing-expert/references/parameterized-testing.md +284 -0
  366. package/skills/ios/swift-testing-expert/references/performance-and-best-practices.md +187 -0
  367. package/skills/ios/swift-testing-expert/references/traits-and-tags.md +114 -0
  368. package/skills/ios/swift-testing-expert/references/xcode-workflows.md +70 -0
  369. package/skills/ios/swiftdata-pro/LICENSE +21 -0
  370. package/skills/ios/swiftdata-pro/SKILL.md +102 -0
  371. package/skills/ios/swiftdata-pro/agents/openai.yaml +10 -0
  372. package/skills/ios/swiftdata-pro/assets/swiftdata-pro-icon.png +0 -0
  373. package/skills/ios/swiftdata-pro/assets/swiftdata-pro-icon.svg +29 -0
  374. package/skills/ios/swiftdata-pro/references/class-inheritance.md +104 -0
  375. package/skills/ios/swiftdata-pro/references/cloudkit.md +10 -0
  376. package/skills/ios/swiftdata-pro/references/core-rules.md +20 -0
  377. package/skills/ios/swiftdata-pro/references/indexing.md +27 -0
  378. package/skills/ios/swiftdata-pro/references/predicates.md +73 -0
  379. package/skills/ios/swiftui-design-principles/AGENTS.md +21 -0
  380. package/skills/ios/swiftui-design-principles/LICENSE +21 -0
  381. package/skills/ios/swiftui-design-principles/README.md +41 -0
  382. package/skills/ios/swiftui-design-principles/SKILL.md +605 -0
  383. package/skills/ios/swiftui-design-principles/metadata.json +10 -0
  384. package/skills/ios/swiftui-design-tokens/SKILL.md +475 -0
  385. package/skills/ios/swiftui-liquid-glass/LICENSE +21 -0
  386. package/skills/ios/swiftui-liquid-glass/SKILL.md +95 -0
  387. package/skills/ios/swiftui-liquid-glass/agents/openai.yaml +4 -0
  388. package/skills/ios/swiftui-liquid-glass/references/liquid-glass.md +280 -0
  389. package/skills/ios/swiftui-performance-audit/LICENSE +21 -0
  390. package/skills/ios/swiftui-performance-audit/SKILL.md +111 -0
  391. package/skills/ios/swiftui-performance-audit/agents/openai.yaml +4 -0
  392. package/skills/ios/swiftui-performance-audit/references/code-smells.md +150 -0
  393. package/skills/ios/swiftui-performance-audit/references/demystify-swiftui-performance-wwdc23.md +46 -0
  394. package/skills/ios/swiftui-performance-audit/references/optimizing-swiftui-performance-instruments.md +29 -0
  395. package/skills/ios/swiftui-performance-audit/references/profiling-intake.md +44 -0
  396. package/skills/ios/swiftui-performance-audit/references/report-template.md +47 -0
  397. package/skills/ios/swiftui-performance-audit/references/understanding-hangs-in-your-app.md +33 -0
  398. package/skills/ios/swiftui-performance-audit/references/understanding-improving-swiftui-performance.md +52 -0
  399. package/skills/ios/swiftui-pro/LICENSE +21 -0
  400. package/skills/ios/swiftui-pro/SKILL.md +108 -0
  401. package/skills/ios/swiftui-pro/agents/openai.yaml +10 -0
  402. package/skills/ios/swiftui-pro/assets/swiftui-pro-icon.png +0 -0
  403. package/skills/ios/swiftui-pro/assets/swiftui-pro-icon.svg +29 -0
  404. package/skills/ios/swiftui-pro/references/accessibility.md +13 -0
  405. package/skills/ios/swiftui-pro/references/api.md +39 -0
  406. package/skills/ios/swiftui-pro/references/data.md +43 -0
  407. package/skills/ios/swiftui-pro/references/design.md +31 -0
  408. package/skills/ios/swiftui-pro/references/hygiene.md +9 -0
  409. package/skills/ios/swiftui-pro/references/navigation.md +14 -0
  410. package/skills/ios/swiftui-pro/references/performance.md +46 -0
  411. package/skills/ios/swiftui-pro/references/swift.md +56 -0
  412. package/skills/ios/swiftui-pro/references/views.md +35 -0
  413. package/skills/ios/swiftui-ui-patterns/LICENSE +21 -0
  414. package/skills/ios/swiftui-ui-patterns/SKILL.md +100 -0
  415. package/skills/ios/swiftui-ui-patterns/agents/openai.yaml +4 -0
  416. package/skills/ios/swiftui-ui-patterns/references/app-wiring.md +201 -0
  417. package/skills/ios/swiftui-ui-patterns/references/async-state.md +96 -0
  418. package/skills/ios/swiftui-ui-patterns/references/components-index.md +50 -0
  419. package/skills/ios/swiftui-ui-patterns/references/controls.md +57 -0
  420. package/skills/ios/swiftui-ui-patterns/references/deeplinks.md +66 -0
  421. package/skills/ios/swiftui-ui-patterns/references/focus.md +90 -0
  422. package/skills/ios/swiftui-ui-patterns/references/form.md +97 -0
  423. package/skills/ios/swiftui-ui-patterns/references/grids.md +71 -0
  424. package/skills/ios/swiftui-ui-patterns/references/haptics.md +71 -0
  425. package/skills/ios/swiftui-ui-patterns/references/input-toolbar.md +51 -0
  426. package/skills/ios/swiftui-ui-patterns/references/lightweight-clients.md +93 -0
  427. package/skills/ios/swiftui-ui-patterns/references/list.md +86 -0
  428. package/skills/ios/swiftui-ui-patterns/references/loading-placeholders.md +38 -0
  429. package/skills/ios/swiftui-ui-patterns/references/macos-settings.md +71 -0
  430. package/skills/ios/swiftui-ui-patterns/references/matched-transitions.md +59 -0
  431. package/skills/ios/swiftui-ui-patterns/references/media.md +73 -0
  432. package/skills/ios/swiftui-ui-patterns/references/menu-bar.md +101 -0
  433. package/skills/ios/swiftui-ui-patterns/references/navigationstack.md +159 -0
  434. package/skills/ios/swiftui-ui-patterns/references/overlay.md +45 -0
  435. package/skills/ios/swiftui-ui-patterns/references/performance.md +62 -0
  436. package/skills/ios/swiftui-ui-patterns/references/previews.md +48 -0
  437. package/skills/ios/swiftui-ui-patterns/references/scroll-reveal.md +133 -0
  438. package/skills/ios/swiftui-ui-patterns/references/scrollview.md +87 -0
  439. package/skills/ios/swiftui-ui-patterns/references/searchable.md +71 -0
  440. package/skills/ios/swiftui-ui-patterns/references/sheets.md +155 -0
  441. package/skills/ios/swiftui-ui-patterns/references/split-views.md +72 -0
  442. package/skills/ios/swiftui-ui-patterns/references/tabview.md +114 -0
  443. package/skills/ios/swiftui-ui-patterns/references/theming.md +71 -0
  444. package/skills/ios/swiftui-ui-patterns/references/title-menus.md +93 -0
  445. package/skills/ios/swiftui-ui-patterns/references/top-bar.md +49 -0
  446. package/skills/ios/swiftui-view-refactor/LICENSE +21 -0
  447. package/skills/ios/swiftui-view-refactor/SKILL.md +207 -0
  448. package/skills/ios/swiftui-view-refactor/agents/openai.yaml +4 -0
  449. package/skills/ios/swiftui-view-refactor/references/mv-patterns.md +161 -0
  450. package/skills/ios/widgetkit/LICENSE +131 -0
  451. package/skills/ios/widgetkit/SKILL.md +502 -0
  452. package/skills/ios/widgetkit/references/widgetkit-advanced.md +871 -0
  453. package/skills/ios/writing-for-interfaces/SKILL.md +75 -0
  454. package/skills/web/accessibility/SKILL.md +146 -0
  455. package/skills/web/aceternity-ui/SKILL.md +719 -0
  456. package/skills/web/aceternity-ui/metadata.json +10 -0
  457. package/skills/web/api-design/SKILL.md +523 -0
  458. package/skills/web/chart-accessibility/SKILL.md +332 -0
  459. package/skills/web/composition-patterns/AGENTS.md +946 -0
  460. package/skills/web/composition-patterns/README.md +60 -0
  461. package/skills/web/composition-patterns/SKILL.md +89 -0
  462. package/skills/web/composition-patterns/metadata.json +11 -0
  463. package/skills/web/composition-patterns/rules/_sections.md +29 -0
  464. package/skills/web/composition-patterns/rules/_template.md +24 -0
  465. package/skills/web/composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
  466. package/skills/web/composition-patterns/rules/architecture-compound-components.md +112 -0
  467. package/skills/web/composition-patterns/rules/patterns-children-over-render-props.md +87 -0
  468. package/skills/web/composition-patterns/rules/patterns-explicit-variants.md +100 -0
  469. package/skills/web/composition-patterns/rules/react19-no-forwardref.md +42 -0
  470. package/skills/web/composition-patterns/rules/state-context-interface.md +191 -0
  471. package/skills/web/composition-patterns/rules/state-decouple-implementation.md +113 -0
  472. package/skills/web/composition-patterns/rules/state-lift-state.md +125 -0
  473. package/skills/web/cost-aware-llm-pipeline/SKILL.md +183 -0
  474. package/skills/web/database-migrations/SKILL.md +429 -0
  475. package/skills/web/deployment-patterns/SKILL.md +427 -0
  476. package/skills/web/docker-patterns/SKILL.md +364 -0
  477. package/skills/web/e2e-testing/SKILL.md +326 -0
  478. package/skills/web/lighthouse-ci/SKILL.md +361 -0
  479. package/skills/web/mcp-server-patterns/SKILL.md +69 -0
  480. package/skills/web/next-best-practices/SKILL.md +153 -0
  481. package/skills/web/next-best-practices/async-patterns.md +87 -0
  482. package/skills/web/next-best-practices/bundling.md +180 -0
  483. package/skills/web/next-best-practices/data-patterns.md +297 -0
  484. package/skills/web/next-best-practices/debug-tricks.md +105 -0
  485. package/skills/web/next-best-practices/directives.md +73 -0
  486. package/skills/web/next-best-practices/error-handling.md +227 -0
  487. package/skills/web/next-best-practices/file-conventions.md +140 -0
  488. package/skills/web/next-best-practices/font.md +245 -0
  489. package/skills/web/next-best-practices/functions.md +108 -0
  490. package/skills/web/next-best-practices/hydration-error.md +91 -0
  491. package/skills/web/next-best-practices/image.md +173 -0
  492. package/skills/web/next-best-practices/metadata.md +301 -0
  493. package/skills/web/next-best-practices/parallel-routes.md +287 -0
  494. package/skills/web/next-best-practices/route-handlers.md +146 -0
  495. package/skills/web/next-best-practices/rsc-boundaries.md +159 -0
  496. package/skills/web/next-best-practices/runtime-selection.md +39 -0
  497. package/skills/web/next-best-practices/scripts.md +141 -0
  498. package/skills/web/next-best-practices/self-hosting.md +371 -0
  499. package/skills/web/next-best-practices/suspense-boundaries.md +67 -0
  500. package/skills/web/next-cache-components/SKILL.md +411 -0
  501. package/skills/web/postgres-best-practices/SKILL.md +14 -0
  502. package/skills/web/postgres-best-practices/references/schema-design.md +9 -0
  503. package/skills/web/react-best-practices/AGENTS.md +3810 -0
  504. package/skills/web/react-best-practices/README.md +123 -0
  505. package/skills/web/react-best-practices/SKILL.md +149 -0
  506. package/skills/web/react-best-practices/metadata.json +15 -0
  507. package/skills/web/react-best-practices/rules/_sections.md +46 -0
  508. package/skills/web/react-best-practices/rules/_template.md +28 -0
  509. package/skills/web/react-best-practices/rules/advanced-effect-event-deps.md +56 -0
  510. package/skills/web/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  511. package/skills/web/react-best-practices/rules/advanced-init-once.md +42 -0
  512. package/skills/web/react-best-practices/rules/advanced-use-latest.md +39 -0
  513. package/skills/web/react-best-practices/rules/async-api-routes.md +38 -0
  514. package/skills/web/react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
  515. package/skills/web/react-best-practices/rules/async-defer-await.md +82 -0
  516. package/skills/web/react-best-practices/rules/async-dependencies.md +51 -0
  517. package/skills/web/react-best-practices/rules/async-parallel.md +28 -0
  518. package/skills/web/react-best-practices/rules/async-suspense-boundaries.md +99 -0
  519. package/skills/web/react-best-practices/rules/bundle-analyzable-paths.md +63 -0
  520. package/skills/web/react-best-practices/rules/bundle-barrel-imports.md +60 -0
  521. package/skills/web/react-best-practices/rules/bundle-conditional.md +31 -0
  522. package/skills/web/react-best-practices/rules/bundle-defer-third-party.md +49 -0
  523. package/skills/web/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  524. package/skills/web/react-best-practices/rules/bundle-preload.md +50 -0
  525. package/skills/web/react-best-practices/rules/client-event-listeners.md +74 -0
  526. package/skills/web/react-best-practices/rules/client-localstorage-schema.md +71 -0
  527. package/skills/web/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  528. package/skills/web/react-best-practices/rules/client-swr-dedup.md +56 -0
  529. package/skills/web/react-best-practices/rules/js-batch-dom-css.md +107 -0
  530. package/skills/web/react-best-practices/rules/js-cache-function-results.md +80 -0
  531. package/skills/web/react-best-practices/rules/js-cache-property-access.md +28 -0
  532. package/skills/web/react-best-practices/rules/js-cache-storage.md +70 -0
  533. package/skills/web/react-best-practices/rules/js-combine-iterations.md +32 -0
  534. package/skills/web/react-best-practices/rules/js-early-exit.md +50 -0
  535. package/skills/web/react-best-practices/rules/js-flatmap-filter.md +60 -0
  536. package/skills/web/react-best-practices/rules/js-hoist-regexp.md +45 -0
  537. package/skills/web/react-best-practices/rules/js-index-maps.md +37 -0
  538. package/skills/web/react-best-practices/rules/js-length-check-first.md +49 -0
  539. package/skills/web/react-best-practices/rules/js-min-max-loop.md +82 -0
  540. package/skills/web/react-best-practices/rules/js-request-idle-callback.md +105 -0
  541. package/skills/web/react-best-practices/rules/js-set-map-lookups.md +24 -0
  542. package/skills/web/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  543. package/skills/web/react-best-practices/rules/rendering-activity.md +26 -0
  544. package/skills/web/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  545. package/skills/web/react-best-practices/rules/rendering-conditional-render.md +40 -0
  546. package/skills/web/react-best-practices/rules/rendering-content-visibility.md +38 -0
  547. package/skills/web/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  548. package/skills/web/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  549. package/skills/web/react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
  550. package/skills/web/react-best-practices/rules/rendering-resource-hints.md +85 -0
  551. package/skills/web/react-best-practices/rules/rendering-script-defer-async.md +68 -0
  552. package/skills/web/react-best-practices/rules/rendering-svg-precision.md +28 -0
  553. package/skills/web/react-best-practices/rules/rendering-usetransition-loading.md +75 -0
  554. package/skills/web/react-best-practices/rules/rerender-defer-reads.md +39 -0
  555. package/skills/web/react-best-practices/rules/rerender-dependencies.md +45 -0
  556. package/skills/web/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  557. package/skills/web/react-best-practices/rules/rerender-derived-state.md +29 -0
  558. package/skills/web/react-best-practices/rules/rerender-functional-setstate.md +74 -0
  559. package/skills/web/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  560. package/skills/web/react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
  561. package/skills/web/react-best-practices/rules/rerender-memo.md +44 -0
  562. package/skills/web/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  563. package/skills/web/react-best-practices/rules/rerender-no-inline-components.md +82 -0
  564. package/skills/web/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  565. package/skills/web/react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
  566. package/skills/web/react-best-practices/rules/rerender-transitions.md +40 -0
  567. package/skills/web/react-best-practices/rules/rerender-use-deferred-value.md +59 -0
  568. package/skills/web/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  569. package/skills/web/react-best-practices/rules/server-after-nonblocking.md +73 -0
  570. package/skills/web/react-best-practices/rules/server-auth-actions.md +96 -0
  571. package/skills/web/react-best-practices/rules/server-cache-lru.md +41 -0
  572. package/skills/web/react-best-practices/rules/server-cache-react.md +76 -0
  573. package/skills/web/react-best-practices/rules/server-dedup-props.md +65 -0
  574. package/skills/web/react-best-practices/rules/server-hoist-static-io.md +149 -0
  575. package/skills/web/react-best-practices/rules/server-no-shared-module-state.md +50 -0
  576. package/skills/web/react-best-practices/rules/server-parallel-fetching.md +83 -0
  577. package/skills/web/react-best-practices/rules/server-parallel-nested-fetching.md +34 -0
  578. package/skills/web/react-best-practices/rules/server-serialization.md +38 -0
  579. package/skills/web/seo/SKILL.md +154 -0
  580. package/skills/web/web-design-guidelines/SKILL.md +39 -0
  581. package/skills/web/zap-scan-config/SKILL.md +444 -0
  582. package/skills/web/zap-scan-config/assets/.gitkeep +9 -0
  583. package/skills/web/zap-scan-config/assets/github_action.yml +207 -0
  584. package/skills/web/zap-scan-config/assets/gitlab_ci.yml +226 -0
  585. package/skills/web/zap-scan-config/assets/zap_automation.yaml +196 -0
  586. package/skills/web/zap-scan-config/assets/zap_context.xml +192 -0
  587. package/skills/web/zap-scan-config/references/EXAMPLE.md +40 -0
  588. package/skills/web/zap-scan-config/references/api_testing_guide.md +475 -0
  589. package/skills/web/zap-scan-config/references/authentication_guide.md +431 -0
  590. package/skills/web/zap-scan-config/references/false_positive_handling.md +427 -0
  591. package/skills/web/zap-scan-config/references/owasp_mapping.md +255 -0
  592. package/src/lrr/aggregator.ts +80 -0
  593. package/src/orchestrator/hooks/context-header.ts +95 -0
  594. package/src/orchestrator/hooks/token-accounting-emitter.ts +77 -0
  595. package/src/orchestrator/hooks/token-accounting.ts +101 -0
  596. package/src/orchestrator/mcp/cycle-counter.ts +129 -0
  597. package/src/orchestrator/mcp/scribe.ts +283 -0
  598. package/src/orchestrator/mcp/state-save.ts +149 -0
  599. package/src/orchestrator/mcp/write-lease.ts +167 -0
  600. package/src/orchestrator/phase4-shared-context.ts +41 -0
  601. package/src/orchestrator/schemas/backward-edge.ts +46 -0
  602. package/agents/agentic-identity-trust.md +0 -121
  603. package/agents/data-consolidation-agent.md +0 -39
  604. package/agents/design-image-prompt-engineer.md +0 -105
  605. package/agents/design-visual-storyteller.md +0 -147
  606. package/agents/design-whimsy-injector.md +0 -89
  607. package/agents/engineering-autonomous-optimization-architect.md +0 -105
  608. package/agents/market-intel.md +0 -35
  609. package/agents/marketing-instagram-curator.md +0 -111
  610. package/agents/marketing-reddit-community-builder.md +0 -121
  611. package/agents/marketing-social-media-strategist.md +0 -74
  612. package/agents/marketing-tiktok-strategist.md +0 -123
  613. package/agents/marketing-twitter-engager.md +0 -124
  614. package/agents/marketing-wechat-official-account.md +0 -143
  615. package/agents/marketing-xiaohongshu-specialist.md +0 -136
  616. package/agents/marketing-zhihu-strategist.md +0 -160
  617. package/agents/product-behavioral-nudge-engine.md +0 -78
  618. package/agents/project-management-experiment-tracker.md +0 -102
  619. package/agents/report-distribution-agent.md +0 -43
  620. package/agents/risk-analysis.md +0 -45
  621. package/agents/sales-data-extraction-agent.md +0 -46
  622. package/agents/specialized-cultural-intelligence-strategist.md +0 -65
  623. package/agents/specialized-developer-advocate.md +0 -146
  624. package/agents/support-analytics-reporter.md +0 -133
  625. package/agents/support-executive-summary-generator.md +0 -64
  626. package/agents/support-finance-tracker.md +0 -145
  627. package/agents/support-legal-compliance-checker.md +0 -129
  628. package/agents/support-support-responder.md +0 -91
  629. package/agents/testing-accessibility-auditor.md +0 -110
  630. package/agents/testing-test-results-analyzer.md +0 -97
  631. package/agents/testing-tool-evaluator.md +0 -76
  632. package/agents/testing-workflow-optimizer.md +0 -99
  633. package/agents/user-research.md +0 -40
@@ -0,0 +1,721 @@
1
+ # Credential Storage Patterns
2
+
3
+ > Scope: Secure lifecycle patterns for client-side credentials on Apple platforms, including storage, refresh, rotation, migration, and logout cleanup.
4
+
5
+ The iOS Keychain is the only Apple-sanctioned storage mechanism for OAuth tokens, API keys, passwords, and other credentials. Cybernews found in 2025 that 71% of iOS apps leak at least one hardcoded secret — primarily through `UserDefaults`, `Info.plist`, or `.xcconfig` files that produce plaintext artifacts trivially extractable from device backups or IPA bundles. This reference covers the complete credential lifecycle: secure storage via Keychain Services, OAuth2/OIDC authentication flows, atomic token refresh with rotation, runtime secret fetching, key rotation strategies, and comprehensive logout cleanup.
6
+
7
+ Authoritative sources: Apple Developer Documentation (Keychain Services, Authentication Services), Apple Platform Security Guide (December 2024), WWDC 2019 Session 516 "What's New in Authentication", WWDC 2021 Session 10105 "Secure login with iCloud Keychain verification codes", WWDC 2024 Session 10125 "Streamline sign-in with passkey upgrades and credential managers", OWASP Mobile Top 10 2024, MASVS v2.1.0 (January 2024), MASTG v2, CISA/FBI "Product Security Bad Practices" advisory v2.0 (January 2025), and the Cybernews iOS app security research (March 2025).
8
+
9
+ ---
10
+
11
+ ## The Six Anti-Patterns AI Code Generators Reproduce
12
+
13
+ AI coding assistants routinely generate insecure credential handling. Each anti-pattern below is documented with evidence, an incorrect code sample, and the correct alternative.
14
+
15
+ ### Anti-Pattern 1 — Tokens in UserDefaults
16
+
17
+ `UserDefaults` writes an unencrypted XML plist at `/var/mobile/Containers/Data/Application/{APP_ID}/Library/Preferences/{BUNDLE_ID}.plist`. This file is included in iTunes/Finder device backups, readable with iMazing or iExplorer on non-jailbroken devices, and trivially extractable on jailbroken devices via `objection`'s `ios nsuserdefaults get` command. Apple's documentation is explicit: the defaults system stores information on disk in an unencrypted format and must not be used for personal or sensitive information.
18
+
19
+ ```swift
20
+ // ❌ INCORRECT — AI-generated token storage in UserDefaults
21
+ // Tokens are written as plaintext XML plist, readable from device backups
22
+ func saveTokens(accessToken: String, refreshToken: String) {
23
+ UserDefaults.standard.set(accessToken, forKey: "access_token")
24
+ UserDefaults.standard.set(refreshToken, forKey: "refresh_token")
25
+ }
26
+ ```
27
+
28
+ **OWASP mapping:** Violates M9 (Insecure Data Storage), MASVS-STORAGE-1, MASWE-0002, and fails MASTG-TEST-0300/0301.
29
+
30
+ > For the canonical ❌/✅ code samples, objection detection commands, and full remediation checklist for this pattern, see `common-anti-patterns.md` § Anti-Pattern #1 — Storing Secrets in UserDefaults.
31
+
32
+ ### Anti-Pattern 2 — Hardcoded API Keys in Source Code
33
+
34
+ CISA and the FBI classify hardcoded credentials as a formal "bad security practice" (CWE-798, ranked in 2024 CWE Top 25). The Cybernews research team found 815,000+ hardcoded secrets across 156,080 iOS apps simply by unzipping IPA files and scanning plaintext — no decompilation required.
35
+
36
+ ```swift
37
+ // ❌ INCORRECT — Hardcoded API key discoverable via `strings` on the Mach-O binary
38
+ struct APIConfig {
39
+ static let stripeSecretKey = "sk_live_51ABC123DEF456..."
40
+ static let firebaseAPIKey = "AIzaSyB1234567890abcdefg"
41
+ }
42
+ // Attacker runs: strings MyApp.app/MyApp | grep "sk_live"
43
+ ```
44
+
45
+ **OWASP mapping:** Violates M1 (Improper Credential Usage), MASWE-0005, and CISA/FBI advisory item #8.
46
+
47
+ ### Anti-Pattern 3 — Production Secrets in .xcconfig
48
+
49
+ The `.xcconfig` pattern solves only the git-commit problem. When you reference `$(MY_API_KEY)` in Info.plist, Xcode resolves the variable at build time and embeds the literal plaintext value in the compiled Info.plist inside the `.app` bundle. Extraction takes seconds: rename `.ipa` to `.zip`, unzip, open Info.plist.
50
+
51
+ ```swift
52
+ // ❌ INCORRECT — .xcconfig value compiled into Info.plist as plaintext
53
+ // In Secrets.xcconfig: MAPS_API_KEY = gm_pk_a1b2c3d4e5f6g7h8i9
54
+ // In Info.plist: <key>MapsAPIKey</key><string>$(MAPS_API_KEY)</string>
55
+
56
+ let apiKey = Bundle.main.infoDictionary?["MapsAPIKey"] as? String
57
+ // Attacker: unzip App.ipa && plutil -p Payload/App.app/Info.plist | grep Maps
58
+ ```
59
+
60
+ ### Anti-Pattern 4 — Missing kSecAttrAccessible Specification
61
+
62
+ When you add a Keychain item without specifying `kSecAttrAccessible`, the system applies the default: `kSecAttrAccessibleWhenUnlocked` (iOS 4.0+). While reasonable, this default allows Keychain items to migrate to new devices via encrypted backups and treats devices without a passcode as "always unlocked." Explicitly setting `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` prevents backup migration and confines the credential to the original hardware.
63
+
64
+ ### Anti-Pattern 5 — Non-Atomic Token Refresh
65
+
66
+ When an access token expires, the app must delete the old token and store the new one. If the app crashes between these operations, the Keychain enters an inconsistent state. Concurrent refresh attempts compound the problem: two threads can both detect expiry, both call the refresh endpoint, and one writes a stale or already-rotated refresh token. With Refresh Token Rotation (RTR), this race can invalidate the entire token family.
67
+
68
+ ### Anti-Pattern 6 — Incomplete Credential Clearing on Logout
69
+
70
+ The most common partial-cleanup bug is deleting the access token while leaving the refresh token in the Keychain. A refresh token is often longer-lived and more powerful — it can silently generate new access tokens.
71
+
72
+ ```swift
73
+ // ❌ INCORRECT — Partial cleanup leaves refresh token behind
74
+ func logout() {
75
+ let query: [String: Any] = [
76
+ kSecClass as String: kSecClassGenericPassword,
77
+ kSecAttrService as String: "com.myapp.auth",
78
+ kSecAttrAccount as String: "access_token"
79
+ ]
80
+ SecItemDelete(query as CFDictionary)
81
+ // BUG: refresh_token, user_profile, cached API keys all remain
82
+ }
83
+ ```
84
+
85
+ ### Correct Baseline for Credential Storage
86
+
87
+ ✅ Store credentials in Keychain, not `UserDefaults`/plist/source literals.
88
+ ✅ Set `kSecAttrAccessible` explicitly for each item based on access pattern.
89
+ ✅ Use add-or-update semantics and handle all `OSStatus` outcomes.
90
+ ✅ Delete all credential artifacts (access token, refresh token, derived caches) on logout.
91
+
92
+ ---
93
+
94
+ ## Data Protection Class Selection for Credentials
95
+
96
+ Choosing the correct `kSecAttrAccessible` value is the highest-ROI decision for credential confidentiality. The Keychain encrypts items using dual AES-256-GCM keys: a metadata key (cached for fast searches) and a per-row secret key that always requires a Secure Enclave round trip (Apple Platform Security Guide, December 2024; full architecture: `keychain-fundamentals.md` § Two-Tier Encryption and Query Cost).
97
+
98
+ | Accessibility Class | Device-Bound | Background Access | Primary Use Case | Risk Note |
99
+ | -------------------------------------------------- | ------------ | ----------------------- | ----------------------------- | ------------------------------------------------------------------- |
100
+ | `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` | Yes | No | OAuth tokens, API keys | **Recommended default** — strongest for credentials |
101
+ | `kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly` | Yes | No | Highest-assurance secrets | Item permanently destroyed if user removes passcode |
102
+ | `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` | Yes | Yes (post-first unlock) | Background token refresh | Larger exposure window; use only when background access is required |
103
+ | `kSecAttrAccessibleAfterFirstUnlock` | No | Yes | Background + backup migration | Transfers via encrypted backup; avoid for sensitive tokens |
104
+
105
+ **Rule of thumb:** Default to `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` for all OAuth tokens and API keys. Use `AfterFirstUnlockThisDeviceOnly` only when background refresh is required (e.g., silent push notification handling). Never use `kSecAttrSynchronizable` for app tokens — iCloud Keychain sync is designed for website passwords, not application secrets.
106
+
107
+ > For complete accessibility constant selection criteria, data protection tier explanations, and `SecAccessControl` interaction rules, see `keychain-access-control.md` § The "When" Layer: Seven Accessibility Constants.
108
+
109
+ ---
110
+
111
+ ## Actor-Based KeychainManager — Thread-Safe Credential Storage
112
+
113
+ The `SecItemAdd`, `SecItemCopyMatching`, `SecItemUpdate`, and `SecItemDelete` functions (all iOS 2.0+) are synchronous C functions performing IPC to the `securityd` daemon. They are thread-safe for independent items, but concurrent modifications to the same item produce race conditions — notably `errSecDuplicateItem` (-25299) when two threads both try to add a missing item simultaneously. A Swift actor (iOS 13+, idiomatic from iOS 17+ with mature concurrency) provides a serial executor that eliminates these races.
114
+
115
+ ```swift
116
+ // ✅ CORRECT — Actor-based KeychainManager with proper kSecAttrAccessible
117
+ // Requires: iOS 13+ (actors), recommended iOS 17+ for mature concurrency
118
+ import Foundation
119
+ import Security
120
+
121
+ public actor KeychainManager {
122
+
123
+ public enum KeychainError: Error {
124
+ case unexpectedStatus(OSStatus), itemNotFound, encodingFailed, decodingFailed
125
+ }
126
+
127
+ let service: String
128
+ private let accessGroup: String?
129
+ private let accessibility: CFString
130
+
131
+ public init(service: String, accessGroup: String? = nil,
132
+ accessibility: CFString = kSecAttrAccessibleWhenUnlockedThisDeviceOnly) {
133
+ self.service = service; self.accessGroup = accessGroup; self.accessibility = accessibility
134
+ }
135
+
136
+ func baseQuery(account: String) -> [CFString: Any] {
137
+ var q: [CFString: Any] = [
138
+ kSecClass: kSecClassGenericPassword, kSecAttrService: service,
139
+ kSecAttrAccount: account, kSecAttrAccessible: accessibility
140
+ ]
141
+ if let accessGroup { q[kSecAttrAccessGroup] = accessGroup }
142
+ #if os(macOS)
143
+ q[kSecUseDataProtectionKeychain] = true // iOS-style data protection on macOS
144
+ #endif
145
+ return q
146
+ }
147
+
148
+ /// Add-or-update semantics: try update first, fall back to add.
149
+ public func save(account: String, data: Data) throws {
150
+ var searchQ = baseQuery(account: account)
151
+ searchQ.removeValue(forKey: kSecAttrAccessible)
152
+ let attrs: [CFString: Any] = [kSecValueData: data, kSecAttrAccessible: accessibility]
153
+ var status = SecItemUpdate(searchQ as CFDictionary, attrs as CFDictionary)
154
+ if status == errSecItemNotFound {
155
+ var addQ = baseQuery(account: account); addQ[kSecValueData] = data
156
+ status = SecItemAdd(addQ as CFDictionary, nil)
157
+ }
158
+ guard status == errSecSuccess else { throw KeychainError.unexpectedStatus(status) }
159
+ }
160
+
161
+ public func load(account: String) throws -> Data {
162
+ var q = baseQuery(account: account)
163
+ q.removeValue(forKey: kSecAttrAccessible)
164
+ q[kSecReturnData] = kCFBooleanTrue; q[kSecMatchLimit] = kSecMatchLimitOne
165
+ var result: AnyObject?
166
+ let status = SecItemCopyMatching(q as CFDictionary, &result)
167
+ switch status {
168
+ case errSecSuccess: guard let d = result as? Data else { throw KeychainError.decodingFailed }; return d
169
+ case errSecItemNotFound: throw KeychainError.itemNotFound
170
+ default: throw KeychainError.unexpectedStatus(status)
171
+ }
172
+ }
173
+
174
+ public func delete(account: String) throws {
175
+ var q = baseQuery(account: account); q.removeValue(forKey: kSecAttrAccessible)
176
+ let s = SecItemDelete(q as CFDictionary)
177
+ guard s == errSecSuccess || s == errSecItemNotFound else { throw KeychainError.unexpectedStatus(s) }
178
+ }
179
+
180
+ /// Delete ALL items for this service — used during logout.
181
+ public func deleteAll() throws {
182
+ var q: [CFString: Any] = [kSecClass: kSecClassGenericPassword, kSecAttrService: service as CFString]
183
+ if let accessGroup { q[kSecAttrAccessGroup] = accessGroup as CFString }
184
+ #if os(macOS)
185
+ q[kSecUseDataProtectionKeychain] = true
186
+ #endif
187
+ let s = SecItemDelete(q as CFDictionary)
188
+ guard s == errSecSuccess || s == errSecItemNotFound else { throw KeychainError.unexpectedStatus(s) }
189
+ }
190
+ }
191
+ ```
192
+
193
+ **Why an actor?** The actor's serial executor guarantees that `save`, `load`, `delete`, and `deleteAll` never interleave. Two concurrent callers hitting `save` for the same account queue instead of racing. The synchronous `SecItem*` C calls execute safely within the actor — callers `await` access, suspending rather than blocking the cooperative thread pool.
194
+
195
+ **Global actor alternative** — when Keychain serialization must span multiple modules:
196
+
197
+ ```swift
198
+ // ✅ Pattern: Global actor for cross-module Keychain serialization
199
+ // Requires: iOS 13.0+ (global actors via Swift 5.5+)
200
+ @globalActor
201
+ actor KeychainActor {
202
+ static let shared = KeychainActor()
203
+ }
204
+
205
+ @KeychainActor
206
+ func saveCredential(_ data: Data, account: String) throws {
207
+ let query: [CFString: Any] = [
208
+ kSecClass: kSecClassGenericPassword,
209
+ kSecAttrService: "com.myapp.auth" as CFString,
210
+ kSecAttrAccount: account as CFString,
211
+ kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
212
+ kSecValueData: data
213
+ ]
214
+ let status = SecItemAdd(query as CFDictionary, nil)
215
+ guard status == errSecSuccess else {
216
+ throw NSError(domain: NSOSStatusErrorDomain, code: Int(status))
217
+ }
218
+ }
219
+ ```
220
+
221
+ ---
222
+
223
+ ## OAuth2 Token Storage and Retrieval Cycle
224
+
225
+ `ASWebAuthenticationSession` (iOS 12.0+) is the mandatory standard for secure web-based login flows. Using legacy web views like `WKWebView` or `SFSafariViewController` for OAuth is a significant anti-pattern — they allow the host app to inspect web content or steal credentials. WWDC 2019 "What's New in Authentication" formally recommended migrating from the deprecated `SFAuthenticationSession` to `ASWebAuthenticationSession`.
226
+
227
+ ### Token Model
228
+
229
+ ```swift
230
+ // ✅ CORRECT — Codable token model with expiry tracking
231
+ struct OAuthTokens: Codable {
232
+ let accessToken: String
233
+ let refreshToken: String
234
+ let expiresAt: Date
235
+ let tokenType: String
236
+
237
+ var isExpired: Bool {
238
+ Date() >= expiresAt
239
+ }
240
+
241
+ /// Proactive refresh before expiry.
242
+ /// Both providers agree: refresh at 75–90% of lifetime or with a fixed
243
+ /// buffer (e.g., 60 seconds) to account for network latency and clock skew.
244
+ var shouldRefresh: Bool {
245
+ let buffer: TimeInterval = 60
246
+ return Date() >= expiresAt.addingTimeInterval(-buffer)
247
+ }
248
+ }
249
+ ```
250
+
251
+ ### ASWebAuthenticationSession + PKCE Flow
252
+
253
+ ```swift
254
+ // ✅ CORRECT — ASWebAuthenticationSession + PKCE + Keychain storage
255
+ // Requires: iOS 13.0+ (for prefersEphemeralWebBrowserSession)
256
+ import AuthenticationServices
257
+ import CryptoKit
258
+
259
+ final class OAuthManager: NSObject, ASWebAuthenticationPresentationContextProviding {
260
+
261
+ private let keychain = KeychainManager(
262
+ service: "com.myapp.auth",
263
+ accessibility: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
264
+ )
265
+ private let clientID = "mobile-app-client" // public client, no secret needed
266
+ private let redirectScheme = "com.myapp.auth"
267
+
268
+ func startAuthentication() async throws -> OAuthTokens {
269
+ let codeVerifier = generateCodeVerifier() // RFC 7636 PKCE
270
+ let codeChallenge = generateCodeChallenge(from: codeVerifier)
271
+
272
+ var components = URLComponents(string: "https://auth.example.com/authorize")!
273
+ components.queryItems = [
274
+ URLQueryItem(name: "response_type", value: "code"),
275
+ URLQueryItem(name: "client_id", value: clientID),
276
+ URLQueryItem(name: "redirect_uri", value: "\(redirectScheme)://callback"),
277
+ URLQueryItem(name: "scope", value: "openid profile offline_access"),
278
+ URLQueryItem(name: "code_challenge", value: codeChallenge),
279
+ URLQueryItem(name: "code_challenge_method", value: "S256"),
280
+ URLQueryItem(name: "state", value: UUID().uuidString)
281
+ ]
282
+
283
+ let callbackURL: URL = try await withCheckedThrowingContinuation { continuation in
284
+ let session = ASWebAuthenticationSession(
285
+ url: components.url!, callbackURLScheme: redirectScheme
286
+ ) { url, error in
287
+ if let error { continuation.resume(throwing: error) }
288
+ else if let url { continuation.resume(returning: url) }
289
+ }
290
+ session.prefersEphemeralWebBrowserSession = true // iOS 13+: no cookie sharing
291
+ session.presentationContextProvider = self
292
+ session.start()
293
+ }
294
+
295
+ guard let code = URLComponents(url: callbackURL, resolvingAgainstBaseURL: false)?
296
+ .queryItems?.first(where: { $0.name == "code" })?.value else {
297
+ throw OAuthError.missingAuthorizationCode
298
+ }
299
+
300
+ let tokens = try await exchangeCodeForTokens(code: code, codeVerifier: codeVerifier)
301
+ try await keychain.save(account: "oauth_tokens", data: JSONEncoder().encode(tokens))
302
+ return tokens
303
+ }
304
+
305
+ // MARK: - PKCE helpers (RFC 7636)
306
+
307
+ private func generateCodeVerifier() -> String {
308
+ var buffer = [UInt8](repeating: 0, count: 32)
309
+ _ = SecRandomCopyBytes(kSecRandomDefault, buffer.count, &buffer)
310
+ return Data(buffer).base64EncodedString()
311
+ .replacingOccurrences(of: "+", with: "-")
312
+ .replacingOccurrences(of: "/", with: "_")
313
+ .replacingOccurrences(of: "=", with: "")
314
+ }
315
+
316
+ private func generateCodeChallenge(from verifier: String) -> String {
317
+ // CryptoKit SHA256 (iOS 13.0+) — replaces legacy CC_SHA256
318
+ let hash = SHA256.hash(data: Data(verifier.utf8))
319
+ return Data(hash).base64EncodedString()
320
+ .replacingOccurrences(of: "+", with: "-")
321
+ .replacingOccurrences(of: "/", with: "_")
322
+ .replacingOccurrences(of: "=", with: "")
323
+ }
324
+
325
+ private func exchangeCodeForTokens(code: String, codeVerifier: String) async throws -> OAuthTokens {
326
+ // Standard OAuth2 token exchange — implement with your authorization server
327
+ fatalError("Implement token exchange")
328
+ }
329
+
330
+ func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor { ASPresentationAnchor() }
331
+ enum OAuthError: Error { case missingAuthorizationCode }
332
+ }
333
+ ```
334
+
335
+ **iOS 17.4+ improvement:** `ASWebAuthenticationSession.Callback` enables HTTPS universal link callbacks instead of custom URL schemes. Universal links provide a cryptographic guarantee of domain ownership, making them significantly less susceptible to interception (RFC 8252, OAuth 2.0 for Native Apps).
336
+
337
+ **Privacy vs SSO trade-off:** Setting `prefersEphemeralWebBrowserSession = true` maximizes privacy and session isolation but breaks Single Sign-On. Toggle based on whether your app prioritizes strict isolation or seamless SSO.
338
+
339
+ ---
340
+
341
+ ## Atomic Token Refresh with Rotation Support
342
+
343
+ When a server implements Refresh Token Rotation (RTR) — as Okta, Auth0, and others do — each refresh response includes a new refresh token and the old one is immediately invalidated. If the app stores the new access token but crashes before persisting the new refresh token, the user is locked out. The solution: update both tokens atomically within the actor's serial execution context.
344
+
345
+ Servers typically provide a short grace period (e.g., 30 seconds per Okta's configuration) during which the previous refresh token remains valid to handle network retries. If a previously invalidated token is reused outside the grace period, the server invalidates the entire token family — a strong signal of credential compromise.
346
+
347
+ ```swift
348
+ // ✅ CORRECT — Atomic token refresh with rotation support
349
+ // Requires: iOS 13.0+ (actor serialization guarantees no interleaving)
350
+ extension KeychainManager {
351
+ func atomicTokenUpdate(oldAccount: String = "oauth_tokens", newTokens: OAuthTokens) throws {
352
+ let newData = try JSONEncoder().encode(newTokens) // Encode BEFORE mutation
353
+
354
+ var delQ: [CFString: Any] = [kSecClass: kSecClassGenericPassword,
355
+ kSecAttrService: self.service as CFString,
356
+ kSecAttrAccount: oldAccount as CFString]
357
+ #if os(macOS)
358
+ delQ[kSecUseDataProtectionKeychain] = true
359
+ #endif
360
+ let delStatus = SecItemDelete(delQ as CFDictionary)
361
+ guard delStatus == errSecSuccess || delStatus == errSecItemNotFound else {
362
+ throw KeychainError.unexpectedStatus(delStatus)
363
+ }
364
+
365
+ var addQ = baseQuery(account: oldAccount); addQ[kSecValueData] = newData
366
+ let addStatus = SecItemAdd(addQ as CFDictionary, nil)
367
+ guard addStatus == errSecSuccess else { throw KeychainError.unexpectedStatus(addStatus) }
368
+ }
369
+ }
370
+ ```
371
+
372
+ ### Refresh Coordinator with Promise Coalescing
373
+
374
+ If multiple concurrent callers detect an expired token, only one refresh request should fire and all callers share the result:
375
+
376
+ ```swift
377
+ // ✅ CORRECT — Single-flight refresh coordinator
378
+ // Requires: iOS 13.0+
379
+ actor TokenRefreshCoordinator {
380
+
381
+ private let keychain: KeychainManager
382
+ private let tokenEndpoint: URL
383
+ private var refreshTask: Task<OAuthTokens, Error>?
384
+
385
+ init(keychain: KeychainManager, tokenEndpoint: URL) {
386
+ self.keychain = keychain; self.tokenEndpoint = tokenEndpoint
387
+ }
388
+
389
+ /// Returns a valid access token, refreshing if necessary.
390
+ func validAccessToken() async throws -> String {
391
+ guard let data = try? await keychain.load(account: "oauth_tokens"),
392
+ let tokens = try? JSONDecoder().decode(OAuthTokens.self, from: data) else {
393
+ throw TokenError.notAuthenticated
394
+ }
395
+ guard tokens.shouldRefresh else { return tokens.accessToken }
396
+
397
+ // Coalesce: reuse in-flight refresh if one exists
398
+ if let existing = refreshTask { return try await existing.value.accessToken }
399
+
400
+ let task = Task<OAuthTokens, Error> {
401
+ defer { refreshTask = nil }
402
+ return try await performRefresh(currentRefreshToken: tokens.refreshToken)
403
+ }
404
+ refreshTask = task
405
+ return try await task.value.accessToken
406
+ }
407
+
408
+ private func performRefresh(currentRefreshToken: String) async throws -> OAuthTokens {
409
+ var request = URLRequest(url: tokenEndpoint)
410
+ request.httpMethod = "POST"
411
+ request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
412
+ request.httpBody = "grant_type=refresh_token&refresh_token=\(currentRefreshToken)".data(using: .utf8)
413
+
414
+ let (data, response) = try await URLSession.shared.data(for: request)
415
+ guard let http = response as? HTTPURLResponse else { throw TokenError.networkError }
416
+
417
+ switch http.statusCode {
418
+ case 200:
419
+ // Decode server response (access_token, refresh_token?, expires_in, token_type)
420
+ let json = try JSONSerialization.jsonObject(with: data) as! [String: Any]
421
+ let newTokens = OAuthTokens(
422
+ accessToken: json["access_token"] as! String,
423
+ refreshToken: (json["refresh_token"] as? String) ?? currentRefreshToken,
424
+ expiresAt: Date().addingTimeInterval(json["expires_in"] as! TimeInterval),
425
+ tokenType: json["token_type"] as! String
426
+ )
427
+ try await keychain.atomicTokenUpdate(newTokens: newTokens)
428
+ return newTokens
429
+ case 400, 401:
430
+ try? await keychain.deleteAll() // Refresh token revoked — force re-auth
431
+ throw TokenError.refreshTokenExpired
432
+ default:
433
+ throw TokenError.serverError(http.statusCode)
434
+ }
435
+ }
436
+
437
+ enum TokenError: Error {
438
+ case notAuthenticated, refreshTokenExpired, networkError, serverError(Int)
439
+ }
440
+ }
441
+ ```
442
+
443
+ ---
444
+
445
+ ## Runtime API Key Fetching with Keychain Cache and TTL
446
+
447
+ The most secure pattern for API keys is a backend proxy — the key never reaches the device. When that is not feasible, fetch the key from a secure backend at runtime and cache it in the Keychain with a time-to-live. The Keychain has no native TTL mechanism, so store expiry metadata alongside the secret.
448
+
449
+ Use App Attest (`DCAppAttestService`, iOS 14.0+) to prove app integrity before the backend issues secrets. The app generates a hardware-backed key pair in the Secure Enclave and requests an attestation object from Apple. The backend validates this object, ensuring the app is untampered and running on a genuine device, before delivering short-lived API keys.
450
+
451
+ ```swift
452
+ // ✅ CORRECT — Runtime secret fetching with TTL-based Keychain cache
453
+ // Requires: iOS 13.0+
454
+ actor RuntimeSecretManager {
455
+
456
+ private struct CachedSecret: Codable {
457
+ let value: String; let fetchedAt: Date; let ttlSeconds: TimeInterval
458
+ var isExpired: Bool { Date().timeIntervalSince(fetchedAt) >= ttlSeconds }
459
+ }
460
+
461
+ private let keychain: KeychainManager
462
+ private let secretsEndpoint: URL
463
+ private let defaultTTL: TimeInterval
464
+ private var memoryCache: [String: CachedSecret] = [:]
465
+
466
+ init(keychain: KeychainManager, secretsEndpoint: URL, defaultTTL: TimeInterval = 3600) {
467
+ self.keychain = keychain; self.secretsEndpoint = secretsEndpoint; self.defaultTTL = defaultTTL
468
+ }
469
+
470
+ /// Three-tier lookup: memory → Keychain → network
471
+ func secret(forKey key: String) async throws -> String {
472
+ if let c = memoryCache[key], !c.isExpired { return c.value }
473
+
474
+ if let data = try? await keychain.load(account: "secret_\(key)"),
475
+ let c = try? JSONDecoder().decode(CachedSecret.self, from: data), !c.isExpired {
476
+ memoryCache[key] = c; return c.value
477
+ }
478
+
479
+ let freshValue = try await fetchFromBackend(key: key)
480
+ let cached = CachedSecret(value: freshValue, fetchedAt: Date(), ttlSeconds: defaultTTL)
481
+ try await keychain.save(account: "secret_\(key)", data: JSONEncoder().encode(cached))
482
+ memoryCache[key] = cached
483
+ return freshValue
484
+ }
485
+
486
+ private func fetchFromBackend(key: String) async throws -> String {
487
+ var request = URLRequest(url: secretsEndpoint.appendingPathComponent(key))
488
+ // Authenticate with App Attest (iOS 14.0+) before backend issues secret
489
+ let (data, response) = try await URLSession.shared.data(for: request)
490
+ guard let http = response as? HTTPURLResponse, http.statusCode == 200,
491
+ let json = try? JSONDecoder().decode([String: String].self, from: data),
492
+ let value = json["value"] else { throw SecretFetchError.serverError }
493
+ return value
494
+ }
495
+
496
+ enum SecretFetchError: Error { case serverError }
497
+ }
498
+ ```
499
+
500
+ ---
501
+
502
+ ## Comprehensive Credential Clearing on Logout
503
+
504
+ A secure logout must clear every credential artifact: access token, refresh token, cached secrets, user profile data, and in-memory caches. It must also revoke tokens server-side when possible. Group all auth-related Keychain items under a single `kSecAttrService` value so `SecItemDelete` can wipe them in one call — no forgotten refresh tokens, no orphaned API keys.
505
+
506
+ ```swift
507
+ // ✅ CORRECT — Complete credential clearing on logout
508
+ // OWASP MASVS-STORAGE-1, MASVS-STORAGE-2 compliant | iOS 13.0+
509
+ actor SessionManager {
510
+
511
+ private let keychain = KeychainManager(service: "com.myapp.auth",
512
+ accessibility: kSecAttrAccessibleWhenUnlockedThisDeviceOnly)
513
+
514
+ func logout() async {
515
+ // 1. Server-side revocation (best-effort)
516
+ if let data = try? await keychain.load(account: "oauth_tokens"),
517
+ let tokens = try? JSONDecoder().decode(OAuthTokens.self, from: data) {
518
+ try? await revoke(token: tokens.refreshToken)
519
+ try? await revoke(token: tokens.accessToken)
520
+ }
521
+ // 2. Nuclear Keychain cleanup — ALL items for this service
522
+ try? await keychain.deleteAll()
523
+ // 3. Clear cookies for auth domain
524
+ HTTPCookieStorage.shared.cookies(for: URL(string: "https://auth.example.com")!)?
525
+ .forEach { HTTPCookieStorage.shared.deleteCookie($0) }
526
+ // 4. Clear URL cache
527
+ URLCache.shared.removeAllCachedResponses()
528
+ }
529
+
530
+ private func revoke(token: String) async throws {
531
+ var req = URLRequest(url: URL(string: "https://auth.example.com/oauth/revoke")!)
532
+ req.httpMethod = "POST"
533
+ req.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
534
+ req.httpBody = "token=\(token)".data(using: .utf8)
535
+ _ = try await URLSession.shared.data(for: req)
536
+ }
537
+ }
538
+ ```
539
+
540
+ **Server-driven revocation signals:** Backends can signal revocation via HTTP 401/403 with custom reason codes (e.g., `token_revoked`) or via silent push notifications (APNs) to trigger background logout and Keychain clearing.
541
+
542
+ ---
543
+
544
+ ## Key Rotation and Versioned Migration
545
+
546
+ ### Rotation Strategies by Secret Type
547
+
548
+ **OAuth refresh tokens** — rely on server-driven RTR. Okta's model issues a new refresh token on every use with a configurable grace period (0–60 seconds). If a previously invalidated token is reused outside the grace period, the server invalidates the entire token family.
549
+
550
+ **Long-lived API keys** — rotation is a planned event: generate a new least-privilege key, deploy it, verify operation, then revoke the old one. Maintain emergency playbooks for compromise scenarios.
551
+
552
+ ### Versioned Keychain Items for Migration
553
+
554
+ Version Keychain items using the `kSecAttrAccount` key to enable backward-compatible migration during rotation:
555
+
556
+ ```swift
557
+ // ✅ CORRECT — Versioned Keychain migration during rotation
558
+ // Requires: iOS 13.0+
559
+ actor TokenMigrationManager {
560
+
561
+ private let keychain: KeychainManager
562
+ private static let currentVersion = 2
563
+
564
+ init(keychain: KeychainManager) { self.keychain = keychain }
565
+
566
+ /// Call on app launch to migrate old token formats.
567
+ func migrateIfNeeded() async throws {
568
+ if let _ = try? await keychain.load(account: "oauth_tokens_v2") {
569
+ return // Already current
570
+ }
571
+ if let oldData = try? await keychain.load(account: "oauth_tokens") {
572
+ let migrated = try migrateV1ToV2(oldData)
573
+ try await keychain.save(account: "oauth_tokens_v2", data: migrated)
574
+ try await keychain.delete(account: "oauth_tokens") // Clean up old
575
+ }
576
+ }
577
+
578
+ private func migrateV1ToV2(_ data: Data) throws -> Data {
579
+ // Implement format conversion between versions
580
+ return data
581
+ }
582
+ }
583
+ ```
584
+
585
+ ### Detecting Compromised Credentials
586
+
587
+ Four strategies: (1) **Token reuse detection** — server invalidates the entire token family when an already-rotated refresh token is presented. (2) **Anomaly monitoring** — geographic or temporal anomalies in token usage patterns. (3) **Proactive refresh** — refresh tokens at 75–90% of their lifetime rather than waiting for expiry. (4) **Breach database checks** — services like AWS Cognito check credentials against known breach databases during authentication.
588
+
589
+ ---
590
+
591
+ ## Device Binding and Backup Implications
592
+
593
+ Using `ThisDeviceOnly` variants prevents credential cloning but introduces friction during device upgrades. Because `ThisDeviceOnly` secrets are non-migratory, they will not transfer when a user restores an iCloud backup to a new device. The application must detect missing credentials on first launch and gracefully route the user through re-authentication.
594
+
595
+ ```swift
596
+ // ✅ Pattern: Detect missing credentials after device restore
597
+ func handleAppLaunch() async {
598
+ do {
599
+ let _ = try await keychain.load(account: "oauth_tokens_v2")
600
+ // Tokens present — proceed normally
601
+ } catch KeychainManager.KeychainError.itemNotFound {
602
+ // Likely a fresh install or device restore
603
+ // Route to authentication flow
604
+ await presentLoginScreen()
605
+ } catch {
606
+ // Unexpected error — log and route to auth
607
+ logger.error("Keychain load failed: \(error)")
608
+ await presentLoginScreen()
609
+ }
610
+ }
611
+ ```
612
+
613
+ **Why not `kSecAttrSynchronizable` for app tokens?** Setting it to `true` syncs the item across all trusted Apple devices via iCloud Keychain. While appropriate for website passwords managed by the Passwords app, this significantly increases the attack surface for OAuth tokens and API keys. Omit this attribute to keep secrets local.
614
+
615
+ ---
616
+
617
+ ## Biometric Protection for High-Value Credentials
618
+
619
+ For user-initiated, high-value operations (e.g., payment authorization, viewing sensitive data), add `SecAccessControl` with biometric gating. Avoid biometric protection for refresh tokens that require headless background renewal.
620
+
621
+ ```swift
622
+ // ✅ CORRECT — Maximum OWASP MASTG L2 compliance configuration
623
+ // Requires: iOS 11.3+ (for .or compound constraint)
624
+ func createHighSecurityKeychainItem(account: String, secret: Data) throws {
625
+ var error: Unmanaged<CFError>?
626
+ guard let accessControl = SecAccessControlCreateWithFlags(
627
+ kCFAllocatorDefault,
628
+ kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
629
+ [.biometryCurrentSet, .or, .devicePasscode],
630
+ &error
631
+ ) else {
632
+ throw error!.takeRetainedValue() as Error
633
+ }
634
+
635
+ let query: [CFString: Any] = [
636
+ kSecClass: kSecClassGenericPassword,
637
+ kSecAttrService: "com.myapp.auth" as CFString,
638
+ kSecAttrAccount: account as CFString,
639
+ kSecValueData: secret,
640
+ kSecAttrAccessControl: accessControl,
641
+ kSecUseDataProtectionKeychain: true
642
+ ]
643
+
644
+ let status = SecItemAdd(query as CFDictionary, nil)
645
+ guard status == errSecSuccess else {
646
+ throw NSError(domain: NSOSStatusErrorDomain, code: Int(status))
647
+ }
648
+ }
649
+ ```
650
+
651
+ **Cross-reference:** See `biometric-authentication.md` for detailed `LAContext` integration patterns and the LAContext-only bypass vulnerability. See `keychain-access-control.md` for the full accessibility class decision tree.
652
+
653
+ ---
654
+
655
+ ## iOS 17+ and 18+ Modernizations
656
+
657
+ **iOS 17** introduced `ASWebAuthenticationSession.Callback` (iOS 17.4+), enabling HTTPS universal link callbacks instead of custom URL schemes — more secure redirect handling that verifies domain ownership. Shared password groups let teams share credentials via end-to-end encrypted iCloud Keychain. Third-party credential provider extensions can now supply passkeys alongside passwords.
658
+
659
+ **iOS 18** brought the standalone Passwords app (replacing Keychain Access for end users), automatic passkey upgrades via `.conditional` registration style, and expanded credential provider extensions to support verification codes. No new `SecItem*` APIs were introduced, but the ecosystem shift toward passkeys means the Keychain's role is evolving from storing passwords to storing cryptographic keys for WebAuthn-based authentication.
660
+
661
+ **WWDC 2024** Session 10125 "Streamline sign-in with passkey upgrades and credential managers" detailed the automatic passkey upgrade flow. WWDC 2021 Session 10105 introduced on-device TOTP verification code generation synced via iCloud Keychain, reducing dependence on SMS-based 2FA.
662
+
663
+ **Swift 6 strict concurrency direction:** The community `swift-keychain-kit` library introduces `SecretData` as a non-copyable type (`~Copyable`) that uses `mlock` to prevent swapping to disk and zeroes memory on deallocation. While not yet an Apple framework, this pattern points toward where Keychain APIs are heading: consumed secrets that cannot accidentally be copied into insecure memory.
664
+
665
+ ---
666
+
667
+ ## Static Analysis and CI/CD Guardrails
668
+
669
+ Catch credential anti-patterns before they reach production:
670
+
671
+ | Tool | Purpose | Integration Point |
672
+ | ---------------------------- | ------------------------------------------------------ | ------------------------ |
673
+ | **truffleHog / gitleaks** | Scan for hardcoded secrets in source code | PR/commit hooks |
674
+ | **strings / class-dump** | Verify no secrets in compiled binary | Post-build CI step |
675
+ | **SwiftLint** (custom rules) | Flag `UserDefaults` usage for token-like keys | Local + CI |
676
+ | **Frida / Objection** | Verify `kSecAttrAccessible` values at runtime | QA / penetration testing |
677
+ | **MobSF** | Automated network traffic and storage leakage analysis | Dynamic regression gate |
678
+
679
+ **Rule:** Fail the build if static analysis detects secrets in the codebase or compiled binary.
680
+
681
+ ---
682
+
683
+ ## OWASP MASTG Compliance Mapping
684
+
685
+ The OWASP Mobile Top 10 (2024) places M1 (Improper Credential Usage) as the number-one mobile security risk. The MASVS v2.1.0 restructured requirements with MASWE (Mobile App Security Weakness Enumeration) bridging controls to specific tests.
686
+
687
+ | Pattern | OWASP Controls | MASWE Weaknesses | MASTG Tests |
688
+ | ------------------------------------------ | ----------------------- | ---------------------------------- | ------------------------------------- |
689
+ | Keychain with `WhenUnlockedThisDeviceOnly` | M1, M9, MASVS-STORAGE-1 | MASWE-0002, MASWE-0004, MASWE-0036 | MASTG-TEST-0299, 0300, 0301, 0302 |
690
+ | Actor-based thread-safe access | M9, MASVS-STORAGE-1 | MASWE-0002 | MASTG-TEST-0300 |
691
+ | ASWebAuthenticationSession (ephemeral) | M1, MASVS-AUTH-1 | MASWE-0032 | MASTG-TEST-0064 |
692
+ | Atomic token refresh | M1, MASVS-AUTH-1 | MASWE-0038 | — |
693
+ | Runtime secret fetching | M1, MASVS-STORAGE-1 | MASWE-0005 | — |
694
+ | Comprehensive logout cleanup | M9, MASVS-STORAGE-2 | MASWE-0004 | MASTG-TEST-0298 |
695
+ | Biometric + `ThisDeviceOnly` | M9, MASVS-STORAGE-2 | MASWE-0046 | MASTG-TEST-0298, MASTG-DEMO-0043–0047 |
696
+
697
+ The legacy test identifiers MSTG-STORAGE-1 and MSTG-STORAGE-2 map to the deprecated MASTG-TEST-0052 and MASTG-TEST-0053, now replaced by the granular suite MASTG-TEST-0296 through MASTG-TEST-0314.
698
+
699
+ ---
700
+
701
+ ## Conclusion
702
+
703
+ The Keychain is not optional — it is the only mechanism Apple provides that encrypts credentials via the Secure Enclave and enforces data protection classes tied to device lock state. Three architectural decisions eliminate the majority of credential vulnerabilities: (1) use a Swift actor as the single Keychain access point to eliminate race conditions in token refresh; (2) fetch secrets at runtime from a backend proxy using App Attest for app attestation rather than embedding them in the binary; (3) group all auth-related Keychain items under a single `kSecAttrService` so logout can clear everything in one call.
704
+
705
+ The future trajectory — passkeys, non-copyable secret types, HTTPS callbacks — reinforces rather than replaces these fundamentals.
706
+
707
+ ---
708
+
709
+ ## Summary Checklist
710
+
711
+ 1. **Keychain-only storage** — all tokens, API keys, and credentials stored exclusively in the Keychain with `kSecAttrAccessibleWhenUnlockedThisDeviceOnly`; never in `UserDefaults`, `Info.plist`, `.xcconfig`, or hardcoded in source
712
+ 2. **Actor-serialized access** — all Keychain operations routed through a Swift `actor` (or `@globalActor`) to prevent race conditions and `errSecDuplicateItem` errors from concurrent access
713
+ 3. **ASWebAuthenticationSession + PKCE** — OAuth2 flows use `ASWebAuthenticationSession` with `prefersEphemeralWebBrowserSession = true` and PKCE (RFC 7636); never `WKWebView` or `SFSafariViewController`
714
+ 4. **Atomic token refresh** — refresh token rotation handled atomically within the actor: encode new tokens before any mutation, delete old, store new; promise coalescing prevents duplicate refresh requests
715
+ 5. **Runtime secret fetching** — API keys fetched from an attested backend (App Attest / DeviceCheck, iOS 14.0+) and cached in Keychain with application-layer TTL; three-tier lookup: memory → Keychain → network
716
+ 6. **Comprehensive logout** — `deleteAll()` by `kSecAttrService` clears all credential items in one call; also revokes tokens server-side, clears cookies, and clears `URLCache`
717
+ 7. **No `kSecAttrSynchronizable` for app tokens** — iCloud Keychain sync is for website passwords, not application secrets; `ThisDeviceOnly` variants prevent backup exfiltration
718
+ 8. **Device restore detection** — app detects missing `ThisDeviceOnly` credentials after device restore and gracefully routes to re-authentication
719
+ 9. **Versioned migration** — Keychain items versioned via `kSecAttrAccount` naming (e.g., `oauth_tokens_v2`) to support format changes and rollback during rotation
720
+ 10. **CI/CD secret scanning** — static analysis (truffleHog, gitleaks, `strings`) integrated into build pipeline to catch hardcoded secrets before deployment; fail the build on detection
721
+ 11. **OWASP MASTG compliance** — patterns satisfy M1, M9, MASVS-STORAGE-1, MASVS-AUTH-1 controls; validate with MASTG-TEST-0298 through 0302 and dynamic analysis (Frida/Objection) confirming protection classes at runtime