@mmerterden/multi-agent-pipeline 20.6.0 → 20.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (326) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/LICENSE +0 -10
  3. package/docs/facts.json +1 -1
  4. package/manifest.json +328 -329
  5. package/package.json +2 -2
  6. package/pipeline/multi-agent-refs/features/design-conformance.md +62 -64
  7. package/pipeline/scripts/_notices.mjs +12 -1
  8. package/pipeline/scripts/gen-skills-index.mjs +1 -1
  9. package/pipeline/skills/.skill-manifest.json +106 -106
  10. package/pipeline/skills/shared/README.md +71 -71
  11. package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +1 -0
  12. package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
  13. package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
  14. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
  15. package/pipeline/skills/shared/external/android-architecture/SKILL.md +2 -0
  16. package/pipeline/skills/shared/external/android-performance/SKILL.md +2 -0
  17. package/pipeline/skills/shared/external/android-security/SKILL.md +2 -0
  18. package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
  19. package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
  20. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
  21. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
  22. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
  23. package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
  24. package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
  25. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
  26. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
  27. package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
  28. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
  29. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
  30. package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
  31. package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
  32. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
  33. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
  34. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
  35. package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
  36. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
  37. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
  38. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
  39. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
  40. package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
  41. package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
  42. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
  43. package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
  44. package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
  45. package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
  46. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
  47. package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
  48. package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
  49. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
  50. package/pipeline/skills/shared/external/backlog/BACKLOG.md +1 -1
  51. package/pipeline/skills/shared/external/backlog/SKILL.md +56 -33
  52. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
  53. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
  54. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
  55. package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +1 -0
  56. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
  57. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
  58. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
  59. package/pipeline/skills/shared/external/compose-components/SKILL.md +2 -0
  60. package/pipeline/skills/shared/external/compose-navigation/SKILL.md +3 -2
  61. package/pipeline/skills/shared/external/compose-testing/SKILL.md +2 -0
  62. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
  63. package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
  64. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
  65. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
  66. package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
  67. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
  68. package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
  69. package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
  70. package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
  71. package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
  72. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
  73. package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
  74. package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
  75. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
  76. package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
  77. package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
  78. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
  79. package/pipeline/skills/shared/external/council/SKILL.md +1 -0
  80. package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
  81. package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
  82. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
  83. package/pipeline/skills/shared/external/css-modern/SKILL.md +1 -0
  84. package/pipeline/skills/shared/external/database-patterns/SKILL.md +1 -0
  85. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
  86. package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
  87. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
  88. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
  89. package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
  90. package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
  91. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
  92. package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
  93. package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
  94. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
  95. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
  96. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
  97. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
  98. package/pipeline/skills/shared/external/evidence-github/SKILL.md +2 -0
  99. package/pipeline/skills/shared/external/evidence-registry/SKILL.md +2 -0
  100. package/pipeline/skills/shared/external/gradle-kotlin-dsl/SKILL.md +2 -0
  101. package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
  102. package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
  103. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
  104. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
  105. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
  106. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
  107. package/pipeline/skills/shared/external/html-semantic/SKILL.md +1 -0
  108. package/pipeline/skills/shared/external/humanizer/SKILL.md +1 -0
  109. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
  110. package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
  111. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
  112. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
  113. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
  114. package/pipeline/skills/shared/external/ios-coding-standard/SKILL.md +1 -0
  115. package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
  116. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
  117. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
  118. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
  119. package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +1 -0
  120. package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
  121. package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
  122. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
  123. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
  124. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
  125. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
  126. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
  127. package/pipeline/skills/shared/external/ios-security/SKILL.md +2 -0
  128. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
  129. package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
  130. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
  131. package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
  132. package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
  133. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
  134. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +91 -283
  135. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +119 -151
  136. package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +60 -90
  137. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +119 -156
  138. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +726 -787
  139. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +253 -288
  140. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +243 -304
  141. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +88 -104
  142. package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +181 -235
  143. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-key-shots.py +198 -263
  144. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +461 -466
  145. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +145 -151
  146. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +123 -141
  147. package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +146 -157
  148. package/pipeline/skills/shared/external/localization-reuse-map/scripts/snapshot-resources.sh +22 -19
  149. package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +156 -140
  150. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
  151. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
  152. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
  153. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
  154. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
  155. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
  156. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
  157. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
  158. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
  159. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
  160. package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
  161. package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
  162. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
  163. package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +1 -0
  164. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
  165. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
  166. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
  167. package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
  168. package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
  169. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
  170. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
  171. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
  172. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
  173. package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
  174. package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
  175. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
  176. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
  177. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
  178. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
  179. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
  180. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
  181. package/pipeline/skills/shared/external/play-store-review/SKILL.md +2 -0
  182. package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
  183. package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
  184. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
  185. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
  186. package/pipeline/skills/shared/external/python-patterns/SKILL.md +1 -0
  187. package/pipeline/skills/shared/external/react-best-practices/SKILL.md +1 -0
  188. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
  189. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
  190. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
  191. package/pipeline/skills/shared/external/rest-api-design/SKILL.md +1 -0
  192. package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +2 -0
  193. package/pipeline/skills/shared/external/room-database/SKILL.md +2 -0
  194. package/pipeline/skills/shared/external/search-first/SKILL.md +1 -0
  195. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
  196. package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
  197. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
  198. package/pipeline/skills/shared/external/signal-community/SKILL.md +2 -0
  199. package/pipeline/skills/shared/external/skill-creator/SKILL.md +80 -41
  200. package/pipeline/skills/shared/external/skill-creator/audit.md +63 -59
  201. package/pipeline/skills/shared/external/skill-creator/checklist.md +28 -20
  202. package/pipeline/skills/shared/external/skill-creator/examples.md +40 -40
  203. package/pipeline/skills/shared/external/skill-creator/label-check.md +48 -36
  204. package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +91 -100
  205. package/pipeline/skills/shared/external/skill-creator/template.md +51 -39
  206. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
  207. package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
  208. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
  209. package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
  210. package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
  211. package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
  212. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
  213. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
  214. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
  215. package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
  216. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
  217. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
  218. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
  219. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
  220. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
  221. package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
  222. package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
  223. package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
  224. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
  225. package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
  226. package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
  227. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
  228. package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
  229. package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
  230. package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
  231. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
  232. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
  233. package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
  234. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
  235. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
  236. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
  237. package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
  238. package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
  239. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
  240. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
  241. package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
  242. package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
  243. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
  244. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
  245. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
  246. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
  247. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
  248. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
  249. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
  250. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
  251. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
  252. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
  253. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
  254. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
  255. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
  256. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
  257. package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
  258. package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
  259. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
  260. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
  261. package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
  262. package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
  263. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
  264. package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
  265. package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
  266. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
  267. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
  268. package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
  269. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
  270. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
  271. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
  272. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
  273. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
  274. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
  275. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
  276. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
  277. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
  278. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
  279. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
  280. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
  281. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
  282. package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
  283. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
  284. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
  285. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
  286. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
  287. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
  288. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
  289. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
  290. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
  291. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
  292. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
  293. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
  294. package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
  295. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
  296. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
  297. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
  298. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
  299. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
  300. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
  301. package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
  302. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
  303. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
  304. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
  305. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
  306. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
  307. package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
  308. package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
  309. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
  310. package/pipeline/skills/shared/external/tailwind-css/SKILL.md +1 -0
  311. package/pipeline/skills/shared/external/testing-backend/SKILL.md +1 -0
  312. package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
  313. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
  314. package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +1 -0
  315. package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
  316. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
  317. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
  318. package/pipeline/skills/shared/external/vue-composition/SKILL.md +1 -0
  319. package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
  320. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
  321. package/pipeline/skills/shared/external/web-accessibility/SKILL.md +1 -0
  322. package/pipeline/skills/shared/external/web-performance/SKILL.md +1 -0
  323. package/pipeline/skills/shared/external/web-testing/SKILL.md +1 -0
  324. package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
  325. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
  326. package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
@@ -3,41 +3,45 @@
3
3
  "evals": [
4
4
  {
5
5
  "id": 0,
6
- "prompt": "I'm building a SwiftUI drawing screen for iPad with Apple Pencil and optional finger drawing. Sketch the PencilKit wrapper, tool picker setup, save/load path, and the lifecycle details that prevent stale tools or update loops.",
7
- "expected_output": "A SwiftUI PencilKit implementation plan that wraps PKCanvasView, retains and observes PKToolPicker, chooses an appropriate drawingPolicy, serializes PKDrawing with dataRepresentation()/PKDrawing(data:), and avoids binding update loops.",
6
+ "name": "swiftui-sketch-pad",
7
+ "prompt": "I need a SwiftUI sketch pad for our iPad note app: a PencilKit canvas bound to a PKDrawing in our model, the tool palette toggled from a toolbar button, and the drawing saved to disk and restored on launch. Which input policy should we use, and how do we avoid update loops?",
8
+ "expected_output": "Builds a UIViewRepresentable with a Coordinator acting as PKCanvasViewDelegate that writes changes back to the binding. Keeps one PKToolPicker retained in the coordinator, adds the canvas as observer before showing it and makes the canvas first responder. Explains .default, .anyInput and .pencilOnly and picks one with a reason. Persists with dataRepresentation() and PKDrawing(data:), and only assigns the drawing in updateUIView when it differs.",
8
9
  "files": [],
9
10
  "expectations": [
10
- "Wraps PKCanvasView with UIViewRepresentable and uses a Coordinator as PKCanvasViewDelegate.",
11
- "Retains PKToolPicker outside a local scope and adds the canvas as an observer before showing the picker or becoming first responder.",
12
- "Explains the difference between .default, .anyInput, and .pencilOnly enough to choose one for Pencil-primary versus finger-drawing modes.",
13
- "Persists drawings using PKDrawing.dataRepresentation() and reloads with PKDrawing(data:).",
14
- "Avoids SwiftUI update loops by checking whether the canvas drawing already matches the binding before assigning."
11
+ "Uses UIViewRepresentable with a Coordinator that implements canvasViewDrawingDidChange(_:)",
12
+ "Retains a single PKToolPicker and adds the canvas as observer before setVisible and becomeFirstResponder",
13
+ "Explains all three drawing policies, describing .default as following the system Pencil setting while the picker is visible",
14
+ "Saves with dataRepresentation() and loads with PKDrawing(data:)",
15
+ "Guards updateUIView with an inequality check to stop update loops"
15
16
  ]
16
17
  },
17
18
  {
18
19
  "id": 1,
19
- "prompt": "Review this PencilKit compatibility plan: let users pick watercolor, crayon, and reed, save the PKDrawing bytes to CloudKit, and set canvasView.maximumSupportedContentVersion = .version2 so iPadOS 16 clients can open every drawing. What should change?",
20
- "expected_output": "A correction-focused review that explains PencilKit content versions, unsupported-ink load failures, the need to set maximumSupportedContentVersion on both canvas and tool picker, and the version boundary for iPadOS 17 and iOS 26 inks.",
20
+ "name": "older-ipad-sync",
21
+ "prompt": "Teachers annotate worksheets on new iPads and students open them on iPads still running iPadOS 16. Students report that some worksheets fail to open. Our plan is to set maximumSupportedContentVersion to .version2 on the canvas. Review the plan.",
22
+ "expected_output": "Explains that inks newer than the recipient supports make loading fail, gives the full content version map (version1 pen/pencil/marker, version2 the iPadOS 17 inks, version3 barrel-roll data, version4 reed pen), and says .version2 does not help iPadOS 16, which needs .version1 or a verified fallback drawing. The cap must be set on both canvas and tool picker. Notes .reed needs 26 and curated picker items need iOS 18 or visionOS 2.",
21
23
  "files": [],
22
24
  "expectations": [
23
- "States that unsupported newer inks cause older systems to fail when loading PKDrawing data rather than guaranteeing compatibility.",
24
- "Correctly maps version1 to marker/pen/pencil-era compatibility, version2 to iPadOS 17 inks, version3 to barrel-roll angle data, and version4 to reed.",
25
- "Explains that .version2 does not make drawings loadable by iPadOS 16 clients; use .version1 or a fallback for pre-iPadOS 17 compatibility.",
26
- "Sets maximumSupportedContentVersion on both PKCanvasView and PKToolPicker when restricting new content.",
27
- "Calls out .reed as iOS/iPadOS/macOS/visionOS 26+ and custom tool picker items as iOS/iPadOS 18+ or visionOS 2+."
25
+ "States that unsupported inks make older systems fail to load the drawing",
26
+ "Lists the four content versions with what each adds",
27
+ "Rejects .version2 for iPadOS 16 recipients and asks for .version1 or a checked fallback",
28
+ "Requires the cap on both PKCanvasView and PKToolPicker",
29
+ "Mentions .reed at 26 and PKToolPicker(toolItems:) at iOS 18 or visionOS 2"
28
30
  ]
29
31
  },
30
32
  {
31
33
  "id": 2,
32
- "prompt": "I need an annotation experience with freehand Apple Pencil signatures, movable text boxes, arrows, image stickers, and a standard markup toolbar. Which parts should stay in PencilKit and when should I switch to PaperKit?",
33
- "expected_output": "A boundary-aware answer that keeps freeform drawing, tool picker, stroke inspection, image export, and PKDrawing serialization in PencilKit while routing structured markup and toolbar needs to PaperKit.",
34
+ "name": "pencilkit-paperkit-split",
35
+ "prompt": "Product wants our inspection app to gain arrows, text callouts, photo stickers and a magnifier on top of the existing PencilKit signature and sketch features. What stays in PencilKit, what moves to PaperKit, and how do they connect?",
36
+ "expected_output": "PencilKit keeps the canvas, tool picker, PKDrawing persistence and export, stroke inspection and signatures. PaperKit takes shapes and arrows, text boxes, images and stickers, loupes and the markup toolbar. Connects them through PaperMarkupViewController.drawingTool accepting a PKTool and PaperMarkup appending a PKDrawing. Gives a step-by-step handoff plan without claiming PencilKit does structured markup.",
34
37
  "files": [],
35
38
  "expectations": [
36
- "Keeps PencilKit scope to PKCanvasView, PKToolPicker, PKDrawing serialization/export, stroke inspection, and signature/freehand drawing.",
37
- "Routes shapes, arrows, text boxes, images/stickers, loupes, and system-standard markup toolbar work to PaperKit.",
38
- "Mentions the verified bridge points: PaperMarkupViewController.drawingTool accepts a PKTool and PaperMarkup can append PKDrawing.",
39
- "Does not claim PencilKit directly owns structured markup elements such as movable text boxes or stickers.",
40
- "Preserves a practical handoff plan between saved PKDrawing content and a PaperKit markup workflow."
39
+ "Keeps canvas, picker, serialization, export, stroke inspection and signatures in PencilKit",
40
+ "Moves arrows, text boxes, images or stickers, loupes and the markup toolbar to PaperKit",
41
+ "Mentions PaperMarkupViewController.drawingTool taking a PKTool",
42
+ "Mentions PaperMarkup accepting an appended PKDrawing",
43
+ "Does not claim PencilKit provides structured markup",
44
+ "Gives a concrete handoff plan"
41
45
  ]
42
46
  }
43
47
  ]
@@ -1,333 +1,268 @@
1
- # PencilKit Extended Patterns
1
+ # PencilKit patterns
2
2
 
3
- Overflow reference for the `pencilkit` skill. Contains advanced patterns
4
- that exceed the main skill file's scope.
3
+ Longer recipes for the `pencilkit-drawing` skill ([../SKILL.md](../SKILL.md)).
5
4
 
6
- ## Contents
5
+ ## Listening to the tool picker
7
6
 
8
- - [Tool Picker Observer Pattern](#tool-picker-observer-pattern)
9
- - [Custom Tool Picker Items](#custom-tool-picker-items)
10
- - [Canvas View Delegate Lifecycle](#canvas-view-delegate-lifecycle)
11
- - [Undo/Redo Support](#undoredo-support)
12
- - [Thumbnail Generation](#thumbnail-generation)
13
- - [Drawing Comparison and Scoring](#drawing-comparison-and-scoring)
14
- - [Content Version Management](#content-version-management)
15
- - [Advanced SwiftUI Wrapper](#advanced-swiftui-wrapper)
16
-
17
- ## Tool Picker Observer Pattern
18
-
19
- Observe tool picker changes to update custom UI or track tool usage.
7
+ Register your controller next to the canvas so both hear about changes.
20
8
 
21
9
  ```swift
22
10
  import PencilKit
23
-
24
- class DrawingController: UIViewController, PKToolPickerObserver {
25
- let canvasView = PKCanvasView()
26
- let toolPicker = PKToolPicker()
27
-
28
- override func viewDidLoad() {
29
- super.viewDidLoad()
30
- toolPicker.addObserver(self)
31
- toolPicker.addObserver(canvasView)
11
+ import UIKit
12
+
13
+ final class StudioViewController: UIViewController, PKToolPickerObserver {
14
+ private let board = PKCanvasView()
15
+ private let palette = PKToolPicker()
16
+
17
+ override func viewDidAppear(_ isAnimated: Bool) {
18
+ super.viewDidAppear(isAnimated)
19
+ palette.addObserver(board)
20
+ palette.addObserver(self)
21
+ palette.setVisible(true, forFirstResponder: board)
22
+ board.becomeFirstResponder()
32
23
  }
33
24
 
34
25
  func toolPickerSelectedToolItemDidChange(_ toolPicker: PKToolPicker) {
35
- let item = toolPicker.selectedToolItem
36
- print("Selected tool: \(item.identifier)")
26
+ let chosen = toolPicker.selectedToolItem.identifier
27
+ title = "Tool: \(chosen)"
37
28
  }
38
29
 
39
30
  func toolPickerVisibilityDidChange(_ toolPicker: PKToolPicker) {
40
- print("Picker visible: \(toolPicker.isVisible)")
31
+ navigationController?.setToolbarHidden(toolPicker.isVisible, animated: true)
41
32
  }
42
33
 
43
34
  func toolPickerFramesObscuredDidChange(_ toolPicker: PKToolPicker) {
44
- let obscured = toolPicker.frameObscured(in: view)
45
- // Adjust content insets to avoid overlap
46
- canvasView.contentInset.bottom = obscured.height
35
+ let covered = toolPicker.frameObscured(in: view)
36
+ board.contentInset.bottom = covered.isNull ? 0 : view.bounds.maxY - covered.minY
47
37
  }
48
38
  }
49
39
  ```
50
40
 
51
- ## Custom Tool Picker Items
41
+ The last callback keeps the bottom of the drawing reachable when the docked
42
+ palette covers part of the screen.
52
43
 
53
- Create custom tools with unique behaviors and icons. Custom tool picker items
54
- require iOS/iPadOS 18+, Mac Catalyst 18+, or visionOS 2+.
44
+ ## A custom picker item
55
45
 
56
- ```swift
57
- var customConfig = PKToolPickerCustomItem.Configuration(
58
- identifier: "com.app.highlighter",
59
- name: "Highlighter"
60
- )
61
- customConfig.defaultColor = .yellow
62
- customConfig.allowsColorSelection = true
63
- customConfig.defaultWidth = 20
64
- customConfig.widthVariants = [
65
- 10: UIImage(systemName: "line.diagonal")!,
66
- 20: UIImage(systemName: "line.3.horizontal")!,
67
- 40: UIImage(systemName: "rectangle.fill")!
68
- ]
69
- customConfig.imageProvider = { item in
70
- // Return a custom image based on current color/width
71
- let config = UIImage.SymbolConfiguration(pointSize: 24)
72
- return UIImage(systemName: "highlighter", withConfiguration: config)!
73
- }
46
+ Requires iOS or iPadOS 18, visionOS 2, or Mac Catalyst 18.
74
47
 
75
- let customItem = PKToolPickerCustomItem(configuration: customConfig)
48
+ ```swift
49
+ @available(iOS 18.0, visionOS 2.0, *)
50
+ func makeStampPicker() -> PKToolPicker {
51
+ var setup = PKToolPickerCustomItem.Configuration(identifier: "com.example.stamp",
52
+ name: "Stamp")
53
+ setup.defaultColor = .systemOrange
54
+ setup.allowsColorSelection = true
55
+ setup.defaultWidth = 24
56
+ setup.widthVariants = [
57
+ 16: UIImage(systemName: "circle.fill") ?? UIImage(),
58
+ 24: UIImage(systemName: "circle.circle.fill") ?? UIImage()
59
+ ]
60
+ setup.imageProvider = { item in
61
+ (UIImage(systemName: "seal.fill") ?? UIImage()).withTintColor(item.color)
62
+ }
76
63
 
77
- let toolPicker = PKToolPicker(toolItems: [
78
- PKToolPickerInkingItem(type: .pen, color: .black, width: 5),
79
- customItem,
80
- PKToolPickerEraserItem(type: .vector)
81
- ])
64
+ let stamp = PKToolPickerCustomItem(configuration: setup)
65
+ return PKToolPicker(toolItems: [
66
+ PKToolPickerInkingItem(type: .pen),
67
+ stamp,
68
+ PKToolPickerEraserItem(type: .bitmap)
69
+ ])
70
+ }
82
71
  ```
83
72
 
84
- ## Canvas View Delegate Lifecycle
85
-
86
- Track the complete drawing lifecycle.
73
+ ## Delegate lifecycle
87
74
 
88
75
  ```swift
89
- class DrawingManager: NSObject, PKCanvasViewDelegate {
90
- var hasUnsavedChanges = false
91
- var isCurrentlyDrawing = false
92
-
76
+ extension StudioViewController: PKCanvasViewDelegate {
93
77
  func canvasViewDidBeginUsingTool(_ canvasView: PKCanvasView) {
94
- isCurrentlyDrawing = true
78
+ autosaveTimer?.invalidate() // user is mid-stroke
95
79
  }
96
80
 
97
81
  func canvasViewDidEndUsingTool(_ canvasView: PKCanvasView) {
98
- isCurrentlyDrawing = false
82
+ scheduleAutosave()
99
83
  }
100
84
 
101
85
  func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) {
102
- hasUnsavedChanges = true
86
+ hasPendingChanges = true
103
87
  }
104
88
 
105
89
  func canvasViewDidFinishRendering(_ canvasView: PKCanvasView) {
106
- // Safe to capture a snapshot for thumbnails
90
+ refreshPreviewTile() // rendering has settled; safe to snapshot
107
91
  }
108
92
  }
109
93
  ```
110
94
 
111
- ## Undo/Redo Support
95
+ `autosaveTimer`, `hasPendingChanges`, `scheduleAutosave()` and
96
+ `refreshPreviewTile()` stand for the app's own state.
112
97
 
113
- `PKCanvasView` automatically integrates with `UndoManager`.
98
+ ## Undo and redo
99
+
100
+ The canvas registers its edits with the responder chain's `UndoManager`, so
101
+ buttons only need to forward (for example from `viewDidLoad()`):
114
102
 
115
103
  ```swift
116
- class DrawingViewController: UIViewController {
117
- let canvasView = PKCanvasView()
118
-
119
- override func viewDidLoad() {
120
- super.viewDidLoad()
121
- view.addSubview(canvasView)
122
-
123
- navigationItem.leftBarButtonItem = UIBarButtonItem(
124
- systemItem: .undo,
125
- primaryAction: UIAction { [weak self] _ in
126
- self?.canvasView.undoManager?.undo()
127
- }
128
- )
129
- navigationItem.rightBarButtonItem = UIBarButtonItem(
130
- systemItem: .redo,
131
- primaryAction: UIAction { [weak self] _ in
132
- self?.canvasView.undoManager?.redo()
133
- }
134
- )
135
- }
104
+ let stepBack = UIAction(title: "Undo", image: UIImage(systemName: "arrow.uturn.backward")) { [weak self] _ in
105
+ self?.board.undoManager?.undo()
106
+ }
107
+ let stepForward = UIAction(title: "Redo", image: UIImage(systemName: "arrow.uturn.forward")) { [weak self] _ in
108
+ self?.board.undoManager?.redo()
136
109
  }
110
+ navigationItem.rightBarButtonItems = [
111
+ UIBarButtonItem(primaryAction: stepForward),
112
+ UIBarButtonItem(primaryAction: stepBack)
113
+ ]
137
114
  ```
138
115
 
139
- ## Thumbnail Generation
140
-
141
- Generate thumbnails for document browsers or galleries.
116
+ ## Thumbnails with a fixed aspect ratio
142
117
 
143
118
  ```swift
144
- func generateThumbnail(
145
- for drawing: PKDrawing,
146
- size: CGSize,
147
- scale: CGFloat = 2.0
148
- ) -> UIImage? {
149
- let bounds = drawing.bounds
150
- guard !bounds.isEmpty else { return nil }
151
-
152
- let aspectRatio = bounds.width / bounds.height
153
- let targetAspect = size.width / size.height
154
- var renderRect = bounds
155
-
156
- if aspectRatio > targetAspect {
157
- let scaleFactor = size.width / bounds.width
158
- renderRect = CGRect(
159
- x: bounds.minX,
160
- y: bounds.midY - (size.height / scaleFactor) / 2,
161
- width: bounds.width,
162
- height: size.height / scaleFactor
163
- )
119
+ func tile(for sketch: PKDrawing, size: CGSize, scale: CGFloat) -> UIImage? {
120
+ let content = sketch.bounds
121
+ guard !content.isEmpty else { return nil }
122
+
123
+ let wanted = size.width / size.height
124
+ var frame = content
125
+ if content.width / content.height > wanted {
126
+ let width = content.height * wanted
127
+ frame = CGRect(x: content.midX - width / 2, y: content.minY,
128
+ width: width, height: content.height)
164
129
  } else {
165
- let scaleFactor = size.height / bounds.height
166
- renderRect = CGRect(
167
- x: bounds.midX - (size.width / scaleFactor) / 2,
168
- y: bounds.minY,
169
- width: size.width / scaleFactor,
170
- height: bounds.height
171
- )
130
+ let height = content.width / wanted
131
+ frame = CGRect(x: content.minX, y: content.midY - height / 2,
132
+ width: content.width, height: height)
172
133
  }
173
-
174
- return drawing.image(from: renderRect, scale: scale)
134
+ return sketch.image(from: frame, scale: scale)
175
135
  }
176
136
  ```
177
137
 
178
- ## Drawing Comparison and Scoring
138
+ ## Scoring a tracing exercise
179
139
 
180
- Compare two drawings by analyzing their strokes and points.
140
+ A simple similarity score: sample both drawings evenly, then count how many
141
+ reference samples have an attempt sample nearby.
181
142
 
182
143
  ```swift
183
- func strokeSimilarity(
184
- reference: PKDrawing,
185
- candidate: PKDrawing,
186
- tolerance: CGFloat = 20
187
- ) -> Double {
188
- let refPoints = reference.strokes.flatMap { stroke in
189
- stroke.path.interpolatedPoints(by: .distance(5)).map(\.location)
190
- }
191
-
192
- let candPoints = candidate.strokes.flatMap { stroke in
193
- stroke.path.interpolatedPoints(by: .distance(5)).map(\.location)
194
- }
195
-
196
- guard !refPoints.isEmpty else { return 0 }
197
-
198
- var matchCount = 0
199
- for refPoint in refPoints {
200
- let minDist = candPoints.map { point in
201
- hypot(refPoint.x - point.x, refPoint.y - point.y)
202
- }.min() ?? .infinity
203
-
204
- if minDist <= tolerance { matchCount += 1 }
144
+ func tracingScore(target: PKDrawing, attempt: PKDrawing, radius: CGFloat = 20) -> Double {
145
+ func samples(_ drawing: PKDrawing) -> [CGPoint] {
146
+ drawing.strokes.flatMap { stroke in
147
+ let evenly = stroke.path.interpolatedPoints(by: .distance(5))
148
+ return evenly.map { $0.location.applying(stroke.transform) }
149
+ }
205
150
  }
206
-
207
- return Double(matchCount) / Double(refPoints.count)
151
+ let wanted = samples(target)
152
+ let drawn = samples(attempt)
153
+ guard !wanted.isEmpty else { return 0 }
154
+
155
+ let hits = wanted.filter { goal in
156
+ drawn.contains { hypot($0.x - goal.x, $0.y - goal.y) <= radius }
157
+ }.count
158
+ return Double(hits) / Double(wanted.count)
208
159
  }
209
160
  ```
210
161
 
211
- ## Content Version Management
162
+ ## Managing content versions
212
163
 
213
- Handle backward compatibility when sharing drawings across OS versions.
164
+ The version map and the cap from the main skill, in one place:
214
165
 
215
166
  ```swift
216
- // Check if a drawing uses features beyond a version
217
- let drawing = canvasView.drawing
218
- let version = drawing.requiredContentVersion
219
-
220
- switch version {
221
- case .version1:
222
- // iPadOS 14-era inks: marker, pen, pencil
223
- break
224
- case .version2:
225
- // iPadOS 17 inks: monoline, fountain pen, watercolor, crayon
226
- break
227
- case .version3:
228
- // Barrel-roll angle data
229
- break
230
- case .version4:
231
- // Reed pen
232
- break
233
- @unknown default:
234
- break
167
+ func describe(_ sketch: PKDrawing) -> String {
168
+ switch sketch.requiredContentVersion {
169
+ case .version1: "pen, pencil, marker"
170
+ case .version2: "adds monoline, fountain pen, watercolor, crayon"
171
+ case .version3: "adds barrel-roll angles"
172
+ case .version4: "adds reed pen"
173
+ @unknown default: "unknown future content"
174
+ }
235
175
  }
236
176
 
237
- // Limit both canvas and picker to a specific version.
238
- // Use .version1 when saved drawings must load on pre-iPadOS 17 systems.
239
- if #available(iOS 17.0, *) {
240
- canvasView.maximumSupportedContentVersion = .version1
241
- toolPicker.maximumSupportedContentVersion = .version1
177
+ @MainActor
178
+ func limitToOldestClients(_ board: PKCanvasView, _ palette: PKToolPicker) {
179
+ if #available(iOS 17.0, *) { // drawings must open before iPadOS 17
180
+ board.maximumSupportedContentVersion = .version1
181
+ palette.maximumSupportedContentVersion = .version1
182
+ }
242
183
  }
243
184
  ```
244
185
 
245
- When you allow newer inks, branch before CloudKit or cross-device sync and
246
- upload either the original drawing or a verified fallback drawing.
186
+ ### Deciding what to upload
187
+
188
+ Before sending a drawing through CloudKit or any cross-device channel, decide
189
+ what the oldest device will receive.
247
190
 
248
191
  ```swift
249
- func drawingForPreiPadOS17Sync(_ drawing: PKDrawing) -> PKDrawing? {
250
- switch drawing.requiredContentVersion {
192
+ func payloadForOldestDevice(_ sketch: PKDrawing) -> PKDrawing? {
193
+ switch sketch.requiredContentVersion {
251
194
  case .version1:
252
- return drawing
195
+ return sketch
253
196
  case .version2, .version3, .version4:
254
- let fallback = version1Fallback(from: drawing)
255
- guard fallback.requiredContentVersion == .version1 else {
256
- // Reusing paths can preserve newer metadata, such as barrel-roll data.
257
- // Sync a thumbnail/message instead of incompatible drawing data.
258
- return nil
197
+ let simplified = downgradedCopy(of: sketch)
198
+ guard simplified.requiredContentVersion == .version1 else {
199
+ return nil // send a thumbnail or an explanation instead
259
200
  }
260
- return fallback
201
+ return simplified
261
202
  @unknown default:
262
203
  return nil
263
204
  }
264
205
  }
265
206
 
266
- func version1Fallback(from drawing: PKDrawing) -> PKDrawing {
267
- let strokes = drawing.strokes.map { stroke -> PKStroke in
268
- var fallback = stroke
269
- fallback.ink = PKInkingTool(.pen, color: .black, width: 2).ink
270
- return fallback
207
+ func downgradedCopy(of sketch: PKDrawing) -> PKDrawing {
208
+ let basicInk = PKInkingTool(.pen, color: .black, width: 4).ink
209
+ let strokes = sketch.strokes.map { original -> PKStroke in
210
+ var copy = original
211
+ copy.ink = PKInk(basicInk.inkType, color: original.ink.color)
212
+ return copy
271
213
  }
272
214
  return PKDrawing(strokes: strokes)
273
215
  }
274
216
  ```
275
217
 
276
- ## Advanced SwiftUI Wrapper
218
+ Swapping the ink is not always enough. The copied paths can still carry newer
219
+ data such as barrel-roll angles, which is why the result is checked again
220
+ before upload.
277
221
 
278
- A full-featured SwiftUI wrapper with tool picker, undo, and save support.
222
+ ## A configurable SwiftUI wrapper
279
223
 
280
224
  ```swift
281
- import SwiftUI
282
225
  import PencilKit
226
+ import SwiftUI
283
227
 
284
- struct DrawingCanvas: UIViewRepresentable {
228
+ struct SketchSurface: UIViewRepresentable {
285
229
  @Binding var drawing: PKDrawing
286
- var drawingPolicy: PKCanvasViewDrawingPolicy = .anyInput
287
- var showToolPicker: Bool = true
230
+ var policy: PKCanvasViewDrawingPolicy = .anyInput
231
+ var showsPalette = true
232
+
233
+ func makeCoordinator() -> Coordinator { Coordinator(drawing: $drawing) }
288
234
 
289
235
  func makeUIView(context: Context) -> PKCanvasView {
290
236
  let canvas = PKCanvasView()
291
- canvas.delegate = context.coordinator
292
- canvas.drawingPolicy = drawingPolicy
293
- canvas.drawing = drawing
294
237
  canvas.backgroundColor = .clear
295
238
  canvas.isOpaque = false
296
-
297
- let coordinator = context.coordinator
298
- coordinator.toolPicker.addObserver(canvas)
299
-
239
+ canvas.drawingPolicy = policy
240
+ canvas.drawing = drawing
241
+ canvas.delegate = context.coordinator
242
+ context.coordinator.palette.addObserver(canvas)
300
243
  return canvas
301
244
  }
302
245
 
303
- func updateUIView(_ canvas: PKCanvasView, context: Context) {
304
- let coordinator = context.coordinator
305
-
306
- if canvas.drawing != drawing {
307
- canvas.drawing = drawing
308
- }
309
-
310
- coordinator.toolPicker.setVisible(showToolPicker, forFirstResponder: canvas)
311
- if showToolPicker {
312
- canvas.becomeFirstResponder()
313
- }
246
+ func updateUIView(_ uiView: PKCanvasView, context: Context) {
247
+ uiView.drawingPolicy = policy
248
+ if uiView.drawing != drawing { uiView.drawing = drawing }
249
+ let palette = context.coordinator.palette
250
+ palette.setVisible(showsPalette, forFirstResponder: uiView)
251
+ if showsPalette { uiView.becomeFirstResponder() }
314
252
  }
315
253
 
316
- func makeCoordinator() -> Coordinator {
317
- Coordinator(parent: self)
318
- }
254
+ final class Coordinator: NSObject, PKCanvasViewDelegate {
255
+ let palette = PKToolPicker()
256
+ private let drawing: Binding<PKDrawing>
319
257
 
320
- class Coordinator: NSObject, PKCanvasViewDelegate {
321
- let parent: DrawingCanvas
322
- let toolPicker = PKToolPicker()
323
-
324
- init(parent: DrawingCanvas) {
325
- self.parent = parent
326
- }
258
+ init(drawing: Binding<PKDrawing>) { self.drawing = drawing }
327
259
 
328
260
  func canvasViewDrawingDidChange(_ canvasView: PKCanvasView) {
329
- parent.drawing = canvasView.drawing
261
+ drawing.wrappedValue = canvasView.drawing
330
262
  }
331
263
  }
332
264
  }
333
265
  ```
266
+
267
+ The clear background lets the surface sit over a photo, a PDF page or a
268
+ paper texture.