@mmerterden/multi-agent-pipeline 20.7.0 → 20.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/LICENSE +0 -10
- package/docs/facts.json +1 -1
- package/manifest.json +266 -267
- package/package.json +2 -2
- package/pipeline/scripts/_notices.mjs +1 -1
- package/pipeline/skills/.skill-manifest.json +68 -68
- package/pipeline/skills/shared/README.md +70 -70
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
- package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
- package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
- package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
- package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
- package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
- package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
- package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
- package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
- package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
- package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
- package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
- package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
- package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
- package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
- package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
- package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
- package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
- package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
- package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
- package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
- package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
- package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
- package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
- package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
- package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
- package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
- package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
- package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
- package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
- package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
- package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
- package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
- package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
- package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
- package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
- package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
- package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
- package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
- package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
- package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
- package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
- package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
- package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
- package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
- package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
- package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
- package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
- package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
- package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
- package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
- package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
- package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
- package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
- package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
- package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
- package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
- package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
- package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
- package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
- package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
- package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
- package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
- package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
- package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
- package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
- package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
- package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
- package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
- package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
- package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
- package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
- package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
- package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
- package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
- package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
- package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
- package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
- package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
- package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
- package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
- package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
- package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
- package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
- package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
- package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
- package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
- package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
- package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
- package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
- package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
- package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
- package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
- package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
- package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
- package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
- package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
- package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
- package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
- package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
- package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
- package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
- package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
- package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
- package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
- package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
- package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
- package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
- package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
- package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
- package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
- package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
- package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
- package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
- package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
- package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
- package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
- package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
- package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
- package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
- package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
- package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
- package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
- package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
- package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
- package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
- package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
- package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
- package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
- package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
- package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
- package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
- package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
- package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
- package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
- package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
- package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
- package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
- package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
- package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
- package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
- package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
- package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
- package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
- package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
- package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
- package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
- package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
- package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
- package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
- package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
- package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
- package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
- package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
- package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
- package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
- package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
- package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
- package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
- package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
- package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
- package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
- package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
- package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
- package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
- package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
- package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
- package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
- package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
- package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
- package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
- package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
- package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
- package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
- package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
- package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
- package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
- package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
- package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
- package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
- package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
- package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
- package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
- package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
- package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
- package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
- package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
- package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
- package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
- package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
- package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
- package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
- package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
- package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
- package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
- package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
- package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
- package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
- package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
- package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
- package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
- package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
- package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
|
@@ -1,430 +1,438 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: swiftui-uikit-interop
|
|
3
|
-
description: "
|
|
3
|
+
description: "UIKit and SwiftUI interop: UIViewRepresentable, UIViewControllerRepresentable, UIHostingController, UIHostingConfiguration, Coordinator delegate bridging. Use when SwiftUI needs a camera preview, map view, mail compose, document scanner, PDF renderer, attributed text view or another UIKit-only or third-party UIKit SDK surface, or when migrating a UIKit app to SwiftUI step by step. Not for web content (swiftui-webkit)."
|
|
4
4
|
metadata:
|
|
5
|
-
source:
|
|
5
|
+
source: multi-agent-pipeline
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
# SwiftUI
|
|
8
|
+
# SwiftUI and UIKit Interop
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
SwiftUI and UIKit meet at three seams: a UIKit view or controller shown inside
|
|
11
|
+
SwiftUI (the representable protocols), SwiftUI shown inside UIKit (the hosting
|
|
12
|
+
controller and the hosting cell configuration), and the object that carries
|
|
13
|
+
UIKit callbacks back into SwiftUI state (the Coordinator). This skill covers all
|
|
14
|
+
three. Patterns target iOS 26 and Swift 6.3 and stay usable back to iOS 16
|
|
15
|
+
unless a section says otherwise.
|
|
11
16
|
|
|
12
|
-
|
|
17
|
+
Detailed material lives in two references:
|
|
13
18
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
- [
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
- [Sizing and Layout](#sizing-and-layout)
|
|
21
|
-
- [State Synchronization Patterns](#state-synchronization-patterns)
|
|
22
|
-
- [Sendable Considerations](#sendable-considerations)
|
|
23
|
-
- [Common Mistakes](#common-mistakes)
|
|
24
|
-
- [Review Checklist](#review-checklist)
|
|
25
|
-
- [References](#references)
|
|
19
|
+
- [wrapper recipes](references/representable-recipes.md):
|
|
20
|
+
nine complete wrappers (map, attributed text view, camera preview, photo
|
|
21
|
+
picker, mail, share sheet, search bar, PDF view, SMS) with usage and pitfalls.
|
|
22
|
+
- [migration guide](references/hosting-migration.md): moving a
|
|
23
|
+
UIKit app to SwiftUI one piece at a time, including navigation, shared data
|
|
24
|
+
and environment bridging.
|
|
26
25
|
|
|
27
|
-
|
|
26
|
+
Embedded web content is out of scope: on iOS 26 use the native SwiftUI WebKit
|
|
27
|
+
types and the `swiftui-webkit` skill.
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
## Contents
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
1. [Wrapping a view](#1-uiviewrepresentable)
|
|
32
|
+
2. [Wrapping a controller](#2-uiviewcontrollerrepresentable)
|
|
33
|
+
3. [Coordinators](#3-the-coordinator-pattern)
|
|
34
|
+
4. [Hosting SwiftUI in a controller](#4-uihostingcontroller)
|
|
35
|
+
5. [Hosting SwiftUI in cells](#5-uihostingconfiguration-ios-16)
|
|
36
|
+
6. [Sizing](#6-sizing-and-layout)
|
|
37
|
+
7. [Keeping state in sync](#7-state-synchronization)
|
|
38
|
+
8. [Concurrency](#8-swift-concurrency-and-sendable)
|
|
39
|
+
9. [Mistakes](#9-common-mistakes)
|
|
40
|
+
10. [Checklist](#10-review-checklist)
|
|
41
|
+
|
|
42
|
+
## 1. UIViewRepresentable
|
|
43
|
+
|
|
44
|
+
`UIViewRepresentable` lets SwiftUI display any `UIView` subclass. Two methods
|
|
45
|
+
are required:
|
|
46
|
+
|
|
47
|
+
- `makeUIView(context:)` builds and returns the UIKit view. SwiftUI calls it a
|
|
48
|
+
single time, when the representable is inserted. Put one-time setup here:
|
|
49
|
+
delegate assignment, fonts, static configuration.
|
|
50
|
+
- `updateUIView(_:context:)` pushes SwiftUI state into the view. It runs again
|
|
51
|
+
for every relevant state change, so compare before you assign. Writing a value
|
|
52
|
+
the view already holds can trigger a delegate callback that writes the binding
|
|
53
|
+
that calls update again.
|
|
32
54
|
|
|
33
55
|
```swift
|
|
34
|
-
|
|
35
|
-
|
|
56
|
+
import UIKit
|
|
57
|
+
import SwiftUI
|
|
58
|
+
|
|
59
|
+
struct PlainTextEditor: UIViewRepresentable {
|
|
60
|
+
@Binding var draft: String
|
|
61
|
+
|
|
62
|
+
func makeCoordinator() -> Coordinator { Coordinator(parent: self) }
|
|
36
63
|
|
|
37
64
|
func makeUIView(context: Context) -> UITextView {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
textView.font = .preferredFont(forTextStyle: .body)
|
|
43
|
-
return textView
|
|
65
|
+
let editor = UITextView(frame: .zero)
|
|
66
|
+
editor.delegate = context.coordinator
|
|
67
|
+
editor.font = .preferredFont(forTextStyle: .body)
|
|
68
|
+
return editor
|
|
44
69
|
}
|
|
45
70
|
|
|
46
|
-
func updateUIView(_
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
71
|
+
func updateUIView(_ editor: UITextView, context: Context) {
|
|
72
|
+
context.coordinator.parent = self
|
|
73
|
+
guard editor.text != draft else { return }
|
|
74
|
+
editor.text = draft
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@MainActor
|
|
78
|
+
final class Coordinator: NSObject, UITextViewDelegate {
|
|
79
|
+
init(parent: PlainTextEditor) { self.parent = parent }
|
|
80
|
+
var parent: PlainTextEditor
|
|
81
|
+
|
|
82
|
+
func textViewDidChange(_ editor: UITextView) {
|
|
83
|
+
parent.draft = editor.text
|
|
52
84
|
}
|
|
53
85
|
}
|
|
54
86
|
}
|
|
55
87
|
```
|
|
56
88
|
|
|
57
|
-
### Lifecycle
|
|
89
|
+
### Lifecycle order
|
|
58
90
|
|
|
59
|
-
| Method | When
|
|
60
|
-
|
|
61
|
-
| `makeCoordinator()` | Before `makeUIView
|
|
62
|
-
| `makeUIView(context:)` | Once, when the
|
|
63
|
-
| `updateUIView(_:context:)` | Immediately after
|
|
64
|
-
| `dismantleUIView(_:coordinator:)` | When the
|
|
65
|
-
| `sizeThatFits(_:uiView:context:)` |
|
|
91
|
+
| Step | Method | When it runs | Job |
|
|
92
|
+
|------|--------|--------------|-----|
|
|
93
|
+
| 1 | `makeCoordinator()` | Before `makeUIView`, once for the representable's lifetime | Create the reference object that acts as delegate or data source |
|
|
94
|
+
| 2 | `makeUIView(context:)` | Once, when the view joins the tree | Build the UIKit object and set it up |
|
|
95
|
+
| 3 | `updateUIView(_:context:)` | Immediately after make, then on each relevant change | Copy SwiftUI state into UIKit |
|
|
96
|
+
| 4 | `dismantleUIView(_:coordinator:)` | When the view leaves the tree | Tear down: observers removed, timers invalidated, subscriptions cancelled |
|
|
97
|
+
| - | `sizeThatFits(_:uiView:context:)` | While laying out, if SwiftUI wants an ideal size (iOS 16+) | Report a custom size |
|
|
66
98
|
|
|
67
|
-
|
|
99
|
+
SwiftUI calls `updateUIView` whenever anything the representable reads changes:
|
|
100
|
+
a `@Binding`, `@State`, `@Environment` value or a property of an `@Observable`
|
|
101
|
+
model. A property you forget to apply in `updateUIView` silently goes stale.
|
|
68
102
|
|
|
69
103
|
### Optional: dismantleUIView
|
|
70
104
|
|
|
105
|
+
`dismantleUIView` is a `static` function. SwiftUI hands it both the UIKit view
|
|
106
|
+
and the Coordinator, so anything the coordinator stores can be released there.
|
|
107
|
+
|
|
108
|
+
For a coordinator that keeps a list of cancellable observation tokens:
|
|
109
|
+
|
|
71
110
|
```swift
|
|
72
|
-
static func dismantleUIView(_
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
coordinator.cancellables.removeAll()
|
|
111
|
+
static func dismantleUIView(_ editor: UITextView, coordinator: Coordinator) {
|
|
112
|
+
coordinator.observationTokens.forEach { $0.cancel() }
|
|
113
|
+
coordinator.observationTokens.removeAll()
|
|
76
114
|
}
|
|
77
115
|
```
|
|
78
116
|
|
|
79
117
|
### Optional: sizeThatFits (iOS 16+)
|
|
80
118
|
|
|
119
|
+
A `nil` result means SwiftUI falls back to the view's `intrinsicContentSize`. Return a
|
|
120
|
+
`CGSize` and SwiftUI uses that size instead of its own choice.
|
|
121
|
+
|
|
81
122
|
```swift
|
|
82
123
|
@available(iOS 16.0, *)
|
|
83
|
-
func sizeThatFits(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
let width = proposal.width ?? UIView.layoutFittingExpandedSize.width
|
|
91
|
-
let size = uiView.sizeThatFits(CGSize(width: width, height: .greatestFiniteMagnitude))
|
|
92
|
-
return size
|
|
124
|
+
func sizeThatFits(_ proposal: ProposedViewSize,
|
|
125
|
+
uiView editor: UITextView,
|
|
126
|
+
context: Context) -> CGSize? {
|
|
127
|
+
let targetWidth = proposal.width ?? UIView.layoutFittingExpandedSize.width
|
|
128
|
+
return editor.sizeThatFits(
|
|
129
|
+
CGSize(width: targetWidth, height: .greatestFiniteMagnitude)
|
|
130
|
+
)
|
|
93
131
|
}
|
|
94
132
|
```
|
|
95
133
|
|
|
96
|
-
## UIViewControllerRepresentable
|
|
134
|
+
## 2. UIViewControllerRepresentable
|
|
97
135
|
|
|
98
|
-
|
|
136
|
+
Wrap a `UIViewController` subclass with `UIViewControllerRepresentable`. It is
|
|
137
|
+
the right choice for pickers the system provides, a scanner for documents, the
|
|
138
|
+
mail composer, and other controllers that are normally presented modally.
|
|
139
|
+
The methods mirror the view version: `makeUIViewController(context:)`, `updateUIViewController(_:context:)`
|
|
140
|
+
and `makeCoordinator()`. For a modal controller the update method usually
|
|
141
|
+
has nothing to push after presentation; it only refreshes the coordinator's
|
|
142
|
+
`parent`.
|
|
99
143
|
|
|
100
144
|
```swift
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
145
|
+
import VisionKit
|
|
146
|
+
import SwiftUI
|
|
147
|
+
|
|
148
|
+
struct PageScannerView: UIViewControllerRepresentable {
|
|
149
|
+
@Environment(\.dismiss) var close
|
|
150
|
+
@Binding var pages: [UIImage]
|
|
151
|
+
|
|
152
|
+
func makeCoordinator() -> Coordinator { Coordinator(parent: self) }
|
|
104
153
|
|
|
105
154
|
func makeUIViewController(context: Context) -> VNDocumentCameraViewController {
|
|
106
|
-
let
|
|
107
|
-
|
|
108
|
-
return
|
|
155
|
+
let camera = VNDocumentCameraViewController()
|
|
156
|
+
camera.delegate = context.coordinator
|
|
157
|
+
return camera
|
|
109
158
|
}
|
|
110
159
|
|
|
111
|
-
func updateUIViewController(_
|
|
112
|
-
|
|
160
|
+
func updateUIViewController(_ camera: VNDocumentCameraViewController,
|
|
161
|
+
context: Context) {
|
|
162
|
+
context.coordinator.parent = self
|
|
113
163
|
}
|
|
114
|
-
|
|
115
|
-
func makeCoordinator() -> Coordinator { Coordinator(self) }
|
|
116
164
|
}
|
|
117
165
|
```
|
|
118
166
|
|
|
119
|
-
###
|
|
167
|
+
### Returning results from a presented controller
|
|
120
168
|
|
|
121
|
-
|
|
169
|
+
Delegate callbacks land in the Coordinator, which passes results on by writing
|
|
170
|
+
a `@Binding` or calling a closure stored on `parent`. Every exit path, success, cancel
|
|
171
|
+
and failure, ends by dismissing.
|
|
122
172
|
|
|
123
173
|
```swift
|
|
124
|
-
extension
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
func documentCameraViewController(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
parent.scannedImages = (0..<scan.pageCount).map { scan.imageOfPage(at: $0) }
|
|
135
|
-
parent.dismiss()
|
|
174
|
+
extension PageScannerView {
|
|
175
|
+
@MainActor
|
|
176
|
+
final class Coordinator: NSObject, @preconcurrency VNDocumentCameraViewControllerDelegate {
|
|
177
|
+
init(parent: PageScannerView) { self.parent = parent }
|
|
178
|
+
var parent: PageScannerView
|
|
179
|
+
|
|
180
|
+
func documentCameraViewController(_ camera: VNDocumentCameraViewController,
|
|
181
|
+
didFinishWith result: VNDocumentCameraScan) {
|
|
182
|
+
parent.pages = (0..<result.pageCount).map { index in result.imageOfPage(at: index) }
|
|
183
|
+
parent.close()
|
|
136
184
|
}
|
|
137
185
|
|
|
138
|
-
func documentCameraViewControllerDidCancel(_
|
|
139
|
-
parent.
|
|
186
|
+
func documentCameraViewControllerDidCancel(_ camera: VNDocumentCameraViewController) {
|
|
187
|
+
parent.close()
|
|
140
188
|
}
|
|
141
189
|
|
|
142
|
-
func documentCameraViewController(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
) {
|
|
146
|
-
parent.dismiss()
|
|
190
|
+
func documentCameraViewController(_ camera: VNDocumentCameraViewController,
|
|
191
|
+
didFailWithError failure: Error) {
|
|
192
|
+
parent.close()
|
|
147
193
|
}
|
|
148
194
|
}
|
|
149
195
|
}
|
|
150
196
|
```
|
|
151
197
|
|
|
152
|
-
|
|
198
|
+
The coordinator is a `final class` that subclasses `NSObject`, adopts the
|
|
199
|
+
delegate protocol, stores `parent`, and is built from `makeCoordinator()` by
|
|
200
|
+
passing `self`.
|
|
201
|
+
|
|
202
|
+
## 3. The Coordinator Pattern
|
|
153
203
|
|
|
154
|
-
### Why
|
|
204
|
+
### Why it exists
|
|
155
205
|
|
|
156
|
-
|
|
206
|
+
Delegates, data sources and target-action receivers in UIKit have to be class
|
|
207
|
+
instances. A representable is a struct, so it cannot play that role. The
|
|
208
|
+
Coordinator is the class that does. SwiftUI creates it, owns it, and keeps it
|
|
209
|
+
alive exactly as long as the representable.
|
|
157
210
|
|
|
158
|
-
###
|
|
211
|
+
### Shape
|
|
159
212
|
|
|
160
|
-
|
|
213
|
+
Declare the Coordinator as a nested type of the representable, directly or
|
|
214
|
+
through an extension. Give it a `parent` property holding the representable
|
|
215
|
+
struct; that is how it writes to `@Binding` properties and calls closures.
|
|
161
216
|
|
|
162
217
|
```swift
|
|
163
|
-
struct
|
|
164
|
-
@Binding var
|
|
165
|
-
var
|
|
218
|
+
struct LegacySearchField: UIViewRepresentable {
|
|
219
|
+
@Binding var query: String
|
|
220
|
+
var onSubmit: (String) -> Void
|
|
166
221
|
|
|
167
|
-
func makeCoordinator() -> Coordinator {
|
|
222
|
+
func makeCoordinator() -> Coordinator { .init(parent: self) }
|
|
168
223
|
|
|
169
224
|
func makeUIView(context: Context) -> UISearchBar {
|
|
170
|
-
let bar = UISearchBar()
|
|
171
|
-
bar.delegate = context.coordinator
|
|
225
|
+
let bar = UISearchBar(frame: .zero)
|
|
226
|
+
bar.delegate = context.coordinator
|
|
172
227
|
return bar
|
|
173
228
|
}
|
|
174
229
|
|
|
175
|
-
func updateUIView(_
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
230
|
+
func updateUIView(_ bar: UISearchBar, context: Context) {
|
|
231
|
+
context.coordinator.parent = self
|
|
232
|
+
if bar.text != query { bar.text = query }
|
|
179
233
|
}
|
|
180
234
|
|
|
235
|
+
@MainActor
|
|
181
236
|
final class Coordinator: NSObject, UISearchBarDelegate {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
init(_ parent: SearchBarView) { self.parent = parent }
|
|
237
|
+
init(parent: LegacySearchField) { self.parent = parent }
|
|
238
|
+
var parent: LegacySearchField
|
|
185
239
|
|
|
186
|
-
func searchBar(_
|
|
187
|
-
parent.
|
|
240
|
+
func searchBar(_ bar: UISearchBar, textDidChange newText: String) {
|
|
241
|
+
parent.query = newText
|
|
188
242
|
}
|
|
189
243
|
|
|
190
|
-
func searchBarSearchButtonClicked(_
|
|
191
|
-
parent.
|
|
192
|
-
|
|
244
|
+
func searchBarSearchButtonClicked(_ bar: UISearchBar) {
|
|
245
|
+
parent.onSubmit(parent.query)
|
|
246
|
+
bar.resignFirstResponder()
|
|
193
247
|
}
|
|
194
248
|
}
|
|
195
249
|
}
|
|
196
250
|
```
|
|
197
251
|
|
|
198
|
-
###
|
|
252
|
+
### Rules
|
|
199
253
|
|
|
200
|
-
|
|
254
|
+
- Assign the coordinator as delegate in `makeUIView` or
|
|
255
|
+
`makeUIViewController`. Doing it in the update method repeats the assignment
|
|
256
|
+
on every state change and can cause side effects inside the UIKit object.
|
|
257
|
+
- SwiftUI does not refresh `coordinator.parent` for you. The coordinator is
|
|
258
|
+
created once with the first struct value, while SwiftUI builds a new struct
|
|
259
|
+
on every update. Start `updateUIView` (or `updateUIViewController`) with
|
|
260
|
+
`context.coordinator.parent = self`, so the coordinator always reads the
|
|
261
|
+
current bindings and closures before the next delegate callback arrives.
|
|
262
|
+
- When a UIKit object stores a closure that refers to the coordinator, capture
|
|
263
|
+
it as `[weak coordinator]` to avoid a retain cycle.
|
|
201
264
|
|
|
202
|
-
|
|
265
|
+
## 4. UIHostingController
|
|
203
266
|
|
|
204
|
-
|
|
267
|
+
### Basic embedding
|
|
205
268
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
Embed SwiftUI views inside UIKit view controllers using `UIHostingController`.
|
|
209
|
-
|
|
210
|
-
### Basic Embedding
|
|
269
|
+
`UIHostingController(rootView:)` puts a SwiftUI view inside a UIKit
|
|
270
|
+
controller. Containment has three steps and the order is fixed:
|
|
211
271
|
|
|
212
272
|
```swift
|
|
213
|
-
final class
|
|
214
|
-
|
|
273
|
+
final class ProfileScreenController: UIViewController {
|
|
274
|
+
let summaryHost = UIHostingController(rootView: ProfileSummary(profile: .empty))
|
|
215
275
|
|
|
216
276
|
override func viewDidLoad() {
|
|
217
277
|
super.viewDidLoad()
|
|
278
|
+
addChild(summaryHost) // 1
|
|
218
279
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
// 2. Add and constrain the view
|
|
223
|
-
hostingController.view.translatesAutoresizingMaskIntoConstraints = false
|
|
224
|
-
view.addSubview(hostingController.view)
|
|
280
|
+
let hosted: UIView = summaryHost.view
|
|
281
|
+
hosted.translatesAutoresizingMaskIntoConstraints = false // 2
|
|
282
|
+
view.addSubview(hosted)
|
|
225
283
|
NSLayoutConstraint.activate([
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
284
|
+
hosted.topAnchor.constraint(equalTo: view.topAnchor),
|
|
285
|
+
hosted.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
|
286
|
+
hosted.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
|
287
|
+
hosted.trailingAnchor.constraint(equalTo: view.trailingAnchor)
|
|
230
288
|
])
|
|
231
289
|
|
|
232
|
-
// 3
|
|
233
|
-
hostingController.didMove(toParent: self)
|
|
290
|
+
summaryHost.didMove(toParent: self) // 3
|
|
234
291
|
}
|
|
235
292
|
}
|
|
236
293
|
```
|
|
237
294
|
|
|
238
|
-
|
|
295
|
+
Leave out any step and containment callbacks, appearance transitions and trait
|
|
296
|
+
propagation stop working correctly.
|
|
239
297
|
|
|
240
|
-
### Sizing
|
|
298
|
+
### Sizing options (iOS 16+)
|
|
241
299
|
|
|
242
|
-
|
|
243
|
-
@available(iOS 16.0, *)
|
|
244
|
-
hostingController.sizingOptions = [.intrinsicContentSize]
|
|
245
|
-
```
|
|
300
|
+
`sizingOptions` is an option set on the hosting controller:
|
|
246
301
|
|
|
247
|
-
| Option | Effect |
|
|
248
|
-
|
|
249
|
-
| `.intrinsicContentSize` | The hosting
|
|
250
|
-
| `.preferredContentSize` |
|
|
302
|
+
| Option | Effect | Use for |
|
|
303
|
+
|--------|--------|---------|
|
|
304
|
+
| `.intrinsicContentSize` | The hosting view reports the SwiftUI content size as its `intrinsicContentSize` | Self-sizing under Auto Layout |
|
|
305
|
+
| `.preferredContentSize` | `preferredContentSize` follows the SwiftUI content size | Popovers and form sheets |
|
|
251
306
|
|
|
252
|
-
### Updating the
|
|
307
|
+
### Updating the root view
|
|
253
308
|
|
|
254
|
-
|
|
309
|
+
To push new data from UIKit, assign a new value: `summaryHost.rootView =
|
|
310
|
+
ProfileSummary(profile: updated)`. If the SwiftUI view instead receives an
|
|
311
|
+
`@Observable` model, SwiftUI tracks its changes and no reassignment is needed.
|
|
255
312
|
|
|
256
|
-
|
|
257
|
-
func updateProfile(_ profile: Profile) {
|
|
258
|
-
hostingController.rootView = ProfileView(profile: profile)
|
|
259
|
-
}
|
|
260
|
-
```
|
|
313
|
+
## 5. UIHostingConfiguration (iOS 16+)
|
|
261
314
|
|
|
262
|
-
For
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
Render SwiftUI content directly inside `UICollectionViewCell` or `UITableViewCell` without managing a child hosting controller:
|
|
315
|
+
For cells, set `contentConfiguration` to a `UIHostingConfiguration`. SwiftUI
|
|
316
|
+
content renders directly in a `UICollectionViewCell` or `UITableViewCell`
|
|
317
|
+
without creating a child hosting controller yourself.
|
|
267
318
|
|
|
268
319
|
```swift
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
) -> UICollectionViewCell {
|
|
274
|
-
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath)
|
|
320
|
+
func collectionView(_ grid: UICollectionView,
|
|
321
|
+
cellForItemAt position: IndexPath) -> UICollectionViewCell {
|
|
322
|
+
let contact = contacts[position.item]
|
|
323
|
+
let cell = grid.dequeueReusableCell(withReuseIdentifier: "contact", for: position)
|
|
275
324
|
cell.contentConfiguration = UIHostingConfiguration {
|
|
276
|
-
|
|
325
|
+
ContactRow(contact: contact)
|
|
277
326
|
}
|
|
278
327
|
return cell
|
|
279
328
|
}
|
|
280
329
|
```
|
|
281
330
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
### intrinsicContentSize Bridging
|
|
331
|
+
Margins, backgrounds, self-sizing and reuse rules are in
|
|
332
|
+
[the migration guide, pattern 5](references/hosting-migration.md#5-uihostingconfiguration-ios-16).
|
|
285
333
|
|
|
286
|
-
|
|
334
|
+
## 6. Sizing and Layout
|
|
287
335
|
|
|
288
|
-
|
|
336
|
+
A wrapped UIKit view tells SwiftUI its natural size through
|
|
337
|
+
`intrinsicContentSize`. SwiftUI respects it unless `frame()` or `fixedSize()`
|
|
338
|
+
says otherwise.
|
|
289
339
|
|
|
290
|
-
|
|
|
291
|
-
|
|
292
|
-
|
|
|
293
|
-
| `.
|
|
294
|
-
| `.fixedSize(
|
|
295
|
-
| `.
|
|
340
|
+
| Modifier on the representable | Result |
|
|
341
|
+
|-------------------------------|--------|
|
|
342
|
+
| none | Intrinsic size is the ideal size; the view can still grow or shrink |
|
|
343
|
+
| `.frame(width:height:)` | Replaces the proposed size; the UIKit view gets exactly that size |
|
|
344
|
+
| `.fixedSize()` | Locked to the intrinsic size in width and height |
|
|
345
|
+
| `.fixedSize(horizontal: true, vertical: false)` | Width locked to intrinsic, height flexible |
|
|
296
346
|
|
|
297
|
-
|
|
347
|
+
When SwiftUI is the child inside UIKit, pin the hosting view with constraints
|
|
348
|
+
and set `sizingOptions = [.intrinsicContentSize]`; that is how Auto Layout
|
|
349
|
+
learns the natural size. Self-sizing cells and sections of variable height
|
|
350
|
+
depend on this.
|
|
298
351
|
|
|
299
|
-
|
|
352
|
+
## 7. State Synchronization
|
|
300
353
|
|
|
301
|
-
|
|
354
|
+
**Two-way with `@Binding`.** The coordinator writes `parent.value` from a
|
|
355
|
+
delegate method; `updateUIView` reads the binding and applies it to the view.
|
|
356
|
+
The `PlainTextEditor` above does both halves (`textViewDidChange(_:)` writes,
|
|
357
|
+
the update method applies).
|
|
302
358
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
359
|
+
**One-way with closures.** For events that flow only from UIKit to SwiftUI
|
|
360
|
+
(tap, submit, scan finished), give the representable an optional closure
|
|
361
|
+
property such as `var onScanComplete: ((String) -> Void)?`. The coordinator
|
|
362
|
+
calls `parent.onScanComplete?(code)` when UIKit reports the event, and SwiftUI
|
|
363
|
+
supplies the closure at the call site:
|
|
306
364
|
|
|
307
365
|
```swift
|
|
308
|
-
|
|
309
|
-
if uiView.text != text { uiView.text = text }
|
|
310
|
-
|
|
311
|
-
// UIKit -> SwiftUI: in Coordinator delegate method
|
|
312
|
-
func textViewDidChange(_ textView: UITextView) {
|
|
313
|
-
parent.text = textView.text
|
|
314
|
-
}
|
|
366
|
+
TicketBarcodeView(onScanComplete: { code in checkIn.validate(code) })
|
|
315
367
|
```
|
|
316
368
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
For fire-and-forget events (button tapped, search submitted, scan completed), pass a closure instead of a binding:
|
|
369
|
+
**Environment.** Inside the representable methods read SwiftUI environment
|
|
370
|
+
values from `context.environment`:
|
|
320
371
|
|
|
321
372
|
```swift
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
373
|
+
func updateUIView(_ editor: UITextView, context: Context) {
|
|
374
|
+
context.coordinator.parent = self
|
|
375
|
+
let environment = context.environment
|
|
376
|
+
editor.isEditable = environment.isEnabled
|
|
377
|
+
editor.backgroundColor = environment.colorScheme == .dark
|
|
378
|
+
? .secondarySystemBackground : .systemBackground
|
|
325
379
|
}
|
|
326
380
|
```
|
|
327
381
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
**DON'T:** Create the UIKit view in `updateUIView`.
|
|
378
|
-
**DO:** Create the view once in `makeUIView`; only configure/update it in `updateUIView`.
|
|
379
|
-
*Why:* `updateUIView` runs on every state change. Creating a new view each time destroys all UIKit state (selection, scroll position, first responder) and leaks memory.
|
|
380
|
-
|
|
381
|
-
**DON'T:** Set delegates in `updateUIView`.
|
|
382
|
-
**DO:** Set delegates in `makeUIView`/`makeUIViewController` only.
|
|
383
|
-
*Why:* Redundant delegate assignment on every update can reset internal delegate state in UIKit views like `WKWebView` or `MKMapView`.
|
|
384
|
-
|
|
385
|
-
**DON'T:** Hold strong references to the Coordinator from closures.
|
|
386
|
-
**DO:** Use `[weak coordinator]` in closures.
|
|
387
|
-
*Why:* UIKit objects often store closures (completion handlers, action blocks). A strong reference to the coordinator that holds a reference to the UIKit view creates a retain cycle.
|
|
388
|
-
|
|
389
|
-
**DON'T:** Forget to call `parent.dismiss()` or completion handlers.
|
|
390
|
-
**DO:** Use the coordinator to track dismissal and invoke `parent.dismiss()` in all delegate exit paths.
|
|
391
|
-
*Why:* Modal controllers presented by SwiftUI (via `.sheet`) need their dismiss binding toggled, or the sheet state becomes inconsistent.
|
|
392
|
-
|
|
393
|
-
**DON'T:** Ignore `dismantleUIView` for views that hold observers or timers.
|
|
394
|
-
**DO:** Clean up `NotificationCenter` observers, `Combine` subscriptions, and `Timer` instances in `dismantleUIView`.
|
|
395
|
-
*Why:* Without cleanup, observers and timers continue firing after the view is removed, causing crashes or stale state updates.
|
|
396
|
-
|
|
397
|
-
**DON'T:** Force `UIHostingController`'s view to fill the parent without proper constraints.
|
|
398
|
-
**DO:** Use Auto Layout constraints or `sizingOptions` for proper embedding.
|
|
399
|
-
*Why:* Setting `frame` manually breaks adaptive layout, trait propagation, and safe area handling.
|
|
400
|
-
|
|
401
|
-
**DON'T:** Try to use `@State` in the Coordinator -- it is not a `View`.
|
|
402
|
-
**DO:** Use regular stored properties on the Coordinator and communicate to SwiftUI via `parent`'s `@Binding` properties.
|
|
403
|
-
*Why:* `@State` only works inside `View` conformances. Using it on a class has no effect.
|
|
404
|
-
|
|
405
|
-
**DON'T:** Skip the `addChild`/`didMove(toParent:)` dance when embedding `UIHostingController`.
|
|
406
|
-
**DO:** Always call `addChild(_:)`, add the view to the hierarchy, then call `didMove(toParent:)`.
|
|
407
|
-
*Why:* Skipping containment causes viewWillAppear/viewDidAppear to never fire, breaks trait collection propagation, and causes visual glitches.
|
|
408
|
-
|
|
409
|
-
## Review Checklist
|
|
410
|
-
|
|
411
|
-
- [ ] View/controller created in `make*`, not `update*`
|
|
412
|
-
- [ ] Coordinator set as delegate in `make*`, not `update*`
|
|
413
|
-
- [ ] `@Binding` used for two-way state sync
|
|
414
|
-
- [ ] `updateUIView` handles all SwiftUI state changes with redundancy guards
|
|
415
|
-
- [ ] `dismantleUIView` cleans up observers/timers if needed
|
|
416
|
-
- [ ] No retain cycles between coordinator and closures (`[weak coordinator]`)
|
|
417
|
-
- [ ] `UIHostingController` properly added as child (`addChild` + `didMove(toParent:)`)
|
|
418
|
-
- [ ] Sizing strategy chosen (`intrinsicContentSize` vs fixed `frame` vs `sizeThatFits`)
|
|
419
|
-
- [ ] Environment values read in `updateUIView` via `context.environment` where needed
|
|
420
|
-
- [ ] Coordinator marked `@MainActor` for strict concurrency
|
|
421
|
-
- [ ] Modal controllers dismiss in all delegate exit paths (success, cancel, error)
|
|
422
|
-
- [ ] `UIHostingConfiguration` used for collection/table view cells instead of manual hosting (iOS 16+)
|
|
382
|
+
**Why the equality guard matters.** Assigning to the UIKit view may fire its
|
|
383
|
+
delegate, the delegate writes the binding, the binding change calls
|
|
384
|
+
`updateUIView`, which assigns again. Checking for a difference before assigning
|
|
385
|
+
is what ends that cycle.
|
|
386
|
+
|
|
387
|
+
## 8. Swift Concurrency and Sendable
|
|
388
|
+
|
|
389
|
+
- Delegate protocols in UIKit are main-actor protocols, not `Sendable` ones. A
|
|
390
|
+
coordinator adopting one is isolated to the main actor through that
|
|
391
|
+
conformance.
|
|
392
|
+
- Some framework delegates carry no isolation at all, for example
|
|
393
|
+
`VNDocumentCameraViewControllerDelegate`, `MFMailComposeViewControllerDelegate`
|
|
394
|
+
and `MFMessageComposeViewControllerDelegate`. A `@MainActor` coordinator that
|
|
395
|
+
adopts one plainly fails in Swift 6 with "conformance ... crosses into main
|
|
396
|
+
actor-isolated code". Write `@preconcurrency` before the protocol name: the
|
|
397
|
+
conformance compiles, and the callbacks are checked at run time to arrive on
|
|
398
|
+
the main thread, which is where these controllers deliver them.
|
|
399
|
+
- Mark coordinators `@MainActor`. Use `nonisolated` only on methods that never
|
|
400
|
+
touch UIKit state.
|
|
401
|
+
- A closure that crosses an isolation boundary must be `@Sendable` or captured
|
|
402
|
+
on the actor it runs on.
|
|
403
|
+
|
|
404
|
+
## 9. Common Mistakes
|
|
405
|
+
|
|
406
|
+
| Mistake | Consequence | Fix |
|
|
407
|
+
|---------|-------------|-----|
|
|
408
|
+
| Creating the UIKit view inside `updateUIView` | Selection, scroll offset and first responder are lost, and memory leaks | Create once in `makeUIView` |
|
|
409
|
+
| Setting delegates inside `updateUIView` | Objects such as `WKWebView` or `MKMapView` may reset their own delegate bookkeeping | Assign in `make*` |
|
|
410
|
+
| Strong capture of the coordinator in a closure a UIKit object keeps | Retain cycle coordinator to view and back | `[weak coordinator]` |
|
|
411
|
+
| Forgetting `parent.dismiss()` or the completion on one delegate path | A `.sheet` presenting the controller is left in an inconsistent state | Dismiss on every exit path |
|
|
412
|
+
| Leaving `NotificationCenter` observers, Combine subscriptions or `Timer`s alive | They keep firing after removal: crashes and stale updates | Tear down in `dismantleUIView` |
|
|
413
|
+
| Sizing the hosting view by setting `frame` by hand | Safe areas, trait propagation and adaptive layout stop working | Constraints or `sizingOptions` |
|
|
414
|
+
| Using `@State` in a Coordinator | Does nothing, the coordinator is not a `View` | Plain stored properties, talk to SwiftUI through `parent` bindings |
|
|
415
|
+
| Skipping `addChild` or `didMove(toParent:)` | `viewWillAppear` and `viewDidAppear` never run, traits do not propagate, visual glitches | Full containment sequence |
|
|
416
|
+
|
|
417
|
+
## 10. Review Checklist
|
|
418
|
+
|
|
419
|
+
- [ ] View or controller created in `make*`, never in `update*`
|
|
420
|
+
- [ ] Delegate assignment to the coordinator happens in `make*`, never in `update*`
|
|
421
|
+
- [ ] `@Binding` used where data flows both ways
|
|
422
|
+
- [ ] `updateUIView` applies every piece of state it depends on, each behind a difference check
|
|
423
|
+
- [ ] `dismantleUIView` removes observers and timers where any exist
|
|
424
|
+
- [ ] No retain cycle between coordinator and stored closures (`[weak coordinator]`)
|
|
425
|
+
- [ ] Hosting controller added as a child with `addChild` and `didMove(toParent:)`
|
|
426
|
+
- [ ] Sizing chosen deliberately: `intrinsicContentSize`, fixed `frame`, or `sizeThatFits`
|
|
427
|
+
- [ ] Environment values read from `context.environment` in `updateUIView` where relevant
|
|
428
|
+
- [ ] Coordinators carry `@MainActor` so strict concurrency checking passes
|
|
429
|
+
- [ ] Modal controllers dismiss on success, cancel and error
|
|
430
|
+
- [ ] Cells use `UIHostingConfiguration` rather than hand-managed hosting controllers (iOS 16+)
|
|
423
431
|
|
|
424
432
|
## References
|
|
425
433
|
|
|
426
|
-
-
|
|
427
|
-
-
|
|
428
|
-
- Apple
|
|
429
|
-
|
|
430
|
-
|
|
434
|
+
- [wrapper recipes](references/representable-recipes.md): complete wrapper recipes
|
|
435
|
+
- [migration guide](references/hosting-migration.md): incremental UIKit to SwiftUI migration
|
|
436
|
+
- Apple: [UIViewRepresentable](https://developer.apple.com/documentation/swiftui/uiviewrepresentable),
|
|
437
|
+
[UIViewControllerRepresentable](https://developer.apple.com/documentation/swiftui/uiviewcontrollerrepresentable),
|
|
438
|
+
[UIHostingController](https://developer.apple.com/documentation/swiftui/uihostingcontroller)
|