@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,87 +1,109 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: mapkit-location
|
|
3
|
-
description: "
|
|
3
|
+
description: "MapKit and CoreLocation on iOS and macOS: SwiftUI Map, markers, annotations, polylines and overlays, camera control, user location tracking, CLLocationUpdate streams, location authorization, geocoding and reverse geocoding, search and autocomplete, directions, routes and ETA, CLMonitor geofencing and region monitoring, background location, distance math. Use when implementing, reviewing or improving anything built on maps, coordinates, addresses, places, directions or distances."
|
|
4
4
|
metadata:
|
|
5
|
-
source:
|
|
5
|
+
source: multi-agent-pipeline
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
# MapKit
|
|
8
|
+
# MapKit and CoreLocation
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
`
|
|
10
|
+
Two frameworks do the work here. MapKit draws the map and answers questions
|
|
11
|
+
about places (search, geocoding, routes). CoreLocation tells you where the
|
|
12
|
+
device is and when it crosses a boundary. Since iOS 17 both have a Swift-first
|
|
13
|
+
surface: a SwiftUI `Map` whose content is written in a `MapContentBuilder`
|
|
14
|
+
closure, the async sequence `CLLocationUpdate.liveUpdates()` for position, and
|
|
15
|
+
the `CLMonitor` actor for geofences. Write new code against those; reach for
|
|
16
|
+
`MKMapView` or `CLLocationManager` only for the gaps named below.
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
[references/mapkit-corelocation-patterns.md](references/mapkit-corelocation-patterns.md) when the task involves
|
|
18
|
-
location update lifecycle, geofencing, background location, testing, or privacy keys.
|
|
18
|
+
Baseline: iOS 17. Sections that need a later release say so, and code that
|
|
19
|
+
needs it carries `@available`.
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
Longer material lives in two references:
|
|
21
22
|
|
|
22
|
-
- [
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
- [
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
- [Review Checklist](#review-checklist)
|
|
31
|
-
- [References](#references)
|
|
23
|
+
- [Map patterns](references/mapkit-patterns.md): a complete
|
|
24
|
+
map screen, custom annotations, camera control, search with autocomplete,
|
|
25
|
+
routes, Look Around, snapshots, dense annotations, iOS 26 place APIs,
|
|
26
|
+
coordinate helpers, accessibility.
|
|
27
|
+
- [Location patterns](references/mapkit-corelocation-patterns.md):
|
|
28
|
+
the location update lifecycle, diagnostics, `CLServiceSession`, `CLMonitor`
|
|
29
|
+
geofencing, background location, legacy `CLLocationManager` cases, accuracy,
|
|
30
|
+
simulator testing, Info.plist privacy keys.
|
|
32
31
|
|
|
33
|
-
##
|
|
32
|
+
## Scope
|
|
34
33
|
|
|
35
|
-
|
|
34
|
+
In scope: maps, markers and annotations, overlays, routes, search and
|
|
35
|
+
geocoding, the user's position, `CLMonitor` geofencing and the rules for
|
|
36
|
+
location in the background.
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
4. Configure map style with `.mapStyle()`.
|
|
42
|
-
5. Add map controls with `.mapControls { }`.
|
|
43
|
-
6. Handle selection with a `selection:` binding.
|
|
38
|
+
Neighbouring work belongs elsewhere. MapKit does not draw CarPlay templates,
|
|
39
|
+
launch App Clips or pair Bluetooth hardware, so never claim it does. What this
|
|
40
|
+
skill owns is the handoff point: a geofence event, a search result or a route
|
|
41
|
+
that the other framework then acts on.
|
|
44
42
|
|
|
45
|
-
|
|
43
|
+
| Topic | Where it goes |
|
|
44
|
+
|-------|---------------|
|
|
45
|
+
| CarPlay map templates, navigation alerts, CarPlay UI | CarPlay framework guidance (no skill in this collection) |
|
|
46
|
+
| App Clip invocation from a place card, size limits, handoff to the full app | `app-clips` |
|
|
47
|
+
| Pairing a beacon or accessory when a geofence fires | `core-bluetooth`, or AccessorySetupKit |
|
|
48
|
+
| A Live Activity used to keep background location alive | `live-activities` |
|
|
46
49
|
|
|
47
|
-
|
|
48
|
-
2. On iOS 18+, create a `CLServiceSession` to manage authorization.
|
|
49
|
-
3. Iterate `CLLocationUpdate.liveUpdates()` in a `Task`.
|
|
50
|
-
4. Filter updates by distance or accuracy before updating the UI.
|
|
51
|
-
5. Stop the task when location tracking is no longer needed.
|
|
50
|
+
## Workflows
|
|
52
51
|
|
|
53
|
-
###
|
|
52
|
+
### 1. Put places on a map
|
|
54
53
|
|
|
55
|
-
1.
|
|
56
|
-
2.
|
|
57
|
-
3.
|
|
58
|
-
|
|
54
|
+
1. `import MapKit` (and `SwiftUI`).
|
|
55
|
+
2. Create a `Map`. Bind a `MapCameraPosition` if code needs to move the camera.
|
|
56
|
+
3. Inside the content closure add `Marker`, `Annotation`, `MapPolyline`,
|
|
57
|
+
`MapPolygon` or `MapCircle`.
|
|
58
|
+
4. Pick a look with `.mapStyle(...)`.
|
|
59
|
+
5. Add controls with `.mapControls { ... }`.
|
|
60
|
+
6. For tap-to-select, pass a `selection:` binding.
|
|
59
61
|
|
|
60
|
-
###
|
|
62
|
+
### 2. Follow the user's position
|
|
61
63
|
|
|
62
|
-
1.
|
|
63
|
-
2.
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
1. Put a `NSLocationWhenInUseUsageDescription` string in Info.plist.
|
|
65
|
+
2. On iOS 18 and later, create a `CLServiceSession` when the feature starts
|
|
66
|
+
and keep it until the feature ends; it drives the authorization prompt.
|
|
67
|
+
3. Iterate `CLLocationUpdate.liveUpdates()` inside a `Task` that you keep.
|
|
68
|
+
4. Drop samples that are inaccurate, old or implausible before the UI sees them.
|
|
69
|
+
5. Cancel the task as soon as the feature no longer needs position.
|
|
66
70
|
|
|
67
|
-
###
|
|
71
|
+
### 3. Find places
|
|
68
72
|
|
|
69
|
-
|
|
73
|
+
1. Feed typed text to an `MKLocalSearchCompleter` for suggestions.
|
|
74
|
+
2. Wait at least 300 ms after the last keystroke before updating the query.
|
|
75
|
+
3. Turn the picked suggestion into an `MKLocalSearch.Request` for full results.
|
|
76
|
+
4. Show the `MKMapItem` results as markers, a list, or both.
|
|
70
77
|
|
|
71
|
-
|
|
78
|
+
### 4. Route between two places
|
|
79
|
+
|
|
80
|
+
1. Build an `MKDirections.Request` with a source and destination `MKMapItem`.
|
|
81
|
+
2. Choose a mode in `transportType` (car, walking, transit or cycling).
|
|
82
|
+
3. `try await MKDirections(request:).calculate()`.
|
|
83
|
+
4. Draw `MapPolyline(route.polyline)`.
|
|
84
|
+
|
|
85
|
+
### 5. Review existing code
|
|
86
|
+
|
|
87
|
+
Go through the [Review Checklist](#review-checklist) below.
|
|
88
|
+
|
|
89
|
+
## SwiftUI Map (iOS 17+)
|
|
90
|
+
|
|
91
|
+
A SwiftUI map file imports both `SwiftUI` and `MapKit`.
|
|
72
92
|
|
|
73
93
|
```swift
|
|
74
|
-
import MapKit
|
|
75
94
|
import SwiftUI
|
|
95
|
+
import MapKit
|
|
76
96
|
|
|
77
|
-
struct
|
|
78
|
-
@State private var
|
|
97
|
+
struct TrailheadMap: View {
|
|
98
|
+
@State private var camera: MapCameraPosition = .automatic
|
|
99
|
+
|
|
100
|
+
private let lot = CLLocationCoordinate2D(latitude: 46.852, longitude: -121.760)
|
|
101
|
+
private let summit = CLLocationCoordinate2D(latitude: 46.853, longitude: -121.727)
|
|
79
102
|
|
|
80
103
|
var body: some View {
|
|
81
|
-
Map(position: $
|
|
82
|
-
Marker("
|
|
83
|
-
Marker("
|
|
84
|
-
coordinate: infiniteLoop)
|
|
104
|
+
Map(position: $camera) {
|
|
105
|
+
Marker("Parking", coordinate: lot)
|
|
106
|
+
Marker("Summit", systemImage: "mountain.2.fill", coordinate: summit)
|
|
85
107
|
}
|
|
86
108
|
.mapStyle(.standard(elevation: .realistic))
|
|
87
109
|
.mapControls {
|
|
@@ -93,173 +115,175 @@ struct PlaceMap: View {
|
|
|
93
115
|
}
|
|
94
116
|
```
|
|
95
117
|
|
|
96
|
-
### Marker
|
|
118
|
+
### Marker or Annotation
|
|
97
119
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
120
|
+
- `Marker(_:systemImage:coordinate:)` draws the system balloon. It is the
|
|
121
|
+
cheapest way to flag a spot, VoiceOver reads its title, and `.tint(_:)`
|
|
122
|
+
recolours it.
|
|
123
|
+
- `Annotation(_:coordinate:anchor:content:)` puts any SwiftUI view at a
|
|
124
|
+
coordinate. `anchor` is a `UnitPoint`; `.bottom` makes the bottom edge of
|
|
125
|
+
the view touch the coordinate, which suits pin-shaped content.
|
|
102
126
|
|
|
103
|
-
|
|
104
|
-
Annotation("
|
|
105
|
-
Image(systemName: "
|
|
127
|
+
```swift
|
|
128
|
+
Annotation("Ranger station", coordinate: station, anchor: .bottom) {
|
|
129
|
+
Image(systemName: "house.fill")
|
|
106
130
|
.padding(6)
|
|
107
|
-
.background(.blue.gradient, in: .circle)
|
|
108
131
|
.foregroundStyle(.white)
|
|
132
|
+
.background(Circle().fill(.green.gradient))
|
|
109
133
|
}
|
|
110
134
|
```
|
|
111
135
|
|
|
112
|
-
### Overlays
|
|
136
|
+
### Overlays
|
|
113
137
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
// Polygon (area highlight)
|
|
121
|
-
MapPolygon(coordinates: parkBoundary)
|
|
122
|
-
.foregroundStyle(.green.opacity(0.3))
|
|
123
|
-
.stroke(.green, lineWidth: 2)
|
|
124
|
-
|
|
125
|
-
// Circle (radius around a point)
|
|
126
|
-
MapCircle(center: storeCoord, radius: 500)
|
|
127
|
-
.foregroundStyle(.red.opacity(0.15))
|
|
128
|
-
.stroke(.red, lineWidth: 1)
|
|
129
|
-
}
|
|
130
|
-
```
|
|
138
|
+
| Content | Draws | Styling |
|
|
139
|
+
|---------|-------|---------|
|
|
140
|
+
| `MapPolyline(coordinates:)` | a line through the points | `.stroke(.orange, lineWidth: 4)` |
|
|
141
|
+
| `MapPolygon(coordinates:)` | a filled area | `.foregroundStyle(.teal.opacity(0.25))` plus `.stroke(...)` for the edge |
|
|
142
|
+
| `MapCircle(center:radius:)` | a circle, radius in metres | `.foregroundStyle(...)` and `.stroke(...)` |
|
|
131
143
|
|
|
132
|
-
### Camera
|
|
144
|
+
### Camera position
|
|
133
145
|
|
|
134
|
-
`MapCameraPosition`
|
|
135
|
-
|
|
136
|
-
`.userLocation(fallback:)`, `.camera`, `.item`, `.rect`,
|
|
137
|
-
|
|
138
|
-
|
|
146
|
+
`MapCameraPosition` is what the map is showing. Bound to the map, it moves
|
|
147
|
+
when the user pans and moves the map when code assigns it. Its constructors:
|
|
148
|
+
`.region`, `.userLocation(fallback:)`, `.camera`, `.item`, `.rect`,
|
|
149
|
+
`.automatic`. Animated moves, framing all content and reading the visible
|
|
150
|
+
region are in [Camera Control](references/mapkit-patterns.md#camera-control).
|
|
139
151
|
|
|
140
|
-
### Map
|
|
152
|
+
### Map styles
|
|
141
153
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
154
|
+
| Modifier | Result |
|
|
155
|
+
|----------|--------|
|
|
156
|
+
| `.mapStyle(.standard)` | default road map |
|
|
157
|
+
| `.mapStyle(.standard(elevation: .realistic, showsTraffic: true))` | roads, 3D terrain, live traffic |
|
|
158
|
+
| `.mapStyle(.imagery)` | satellite |
|
|
159
|
+
| `.mapStyle(.imagery(elevation: .realistic))` | satellite in 3D |
|
|
160
|
+
| `.mapStyle(.hybrid)` | satellite with labels |
|
|
161
|
+
| `.mapStyle(.hybrid(elevation: .realistic, showsTraffic: true))` | labelled satellite, 3D terrain, live traffic |
|
|
150
162
|
|
|
151
|
-
###
|
|
163
|
+
### Interaction modes
|
|
152
164
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
165
|
+
| Modifier | Allows |
|
|
166
|
+
|----------|--------|
|
|
167
|
+
| `.mapInteractionModes(.all)` | pan, zoom, rotate, pitch (the default) |
|
|
168
|
+
| `.mapInteractionModes(.pan)` | panning only |
|
|
169
|
+
| `.mapInteractionModes([.pan, .zoom])` | moving and zooming, no rotation or tilt |
|
|
170
|
+
| `.mapInteractionModes([])` | nothing; a static picture |
|
|
171
|
+
|
|
172
|
+
### Selection
|
|
159
173
|
|
|
160
|
-
|
|
174
|
+
The value in `.tag(_:)` must have the same type as the `selection` binding,
|
|
175
|
+
or taps select nothing.
|
|
161
176
|
|
|
162
177
|
```swift
|
|
163
|
-
|
|
178
|
+
struct CampsiteMap: View {
|
|
179
|
+
let sites: [Campsite]
|
|
180
|
+
@State private var chosen: MKMapItem?
|
|
181
|
+
@State private var headline = ""
|
|
164
182
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
183
|
+
var body: some View {
|
|
184
|
+
Map(selection: $chosen) {
|
|
185
|
+
ForEach(sites) { site in
|
|
186
|
+
Marker(site.name, coordinate: site.coordinate)
|
|
187
|
+
.tag(site.mapItem)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
.onChange(of: chosen) { _, item in
|
|
191
|
+
guard let item else { return }
|
|
192
|
+
headline = item.name ?? ""
|
|
193
|
+
}
|
|
169
194
|
}
|
|
170
195
|
}
|
|
171
|
-
.onChange(of: selectedMarker) { _, newValue in
|
|
172
|
-
guard let item = newValue else { return }
|
|
173
|
-
// React to selection
|
|
174
|
-
}
|
|
175
196
|
```
|
|
176
197
|
|
|
177
|
-
## CoreLocation
|
|
198
|
+
## CoreLocation
|
|
178
199
|
|
|
179
200
|
### CLLocationUpdate.liveUpdates() (iOS 17+)
|
|
180
201
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
202
|
+
One async sequence replaces the `CLLocationManagerDelegate` callbacks. Each
|
|
203
|
+
element is a `CLLocationUpdate` whose `location` is optional. Rules:
|
|
204
|
+
|
|
205
|
+
- Store the iterating `Task` in a property; cancelling it is how tracking stops.
|
|
206
|
+
- Throw away samples with invalid or poor accuracy, old timestamps or
|
|
207
|
+
impossible jumps before they touch map UI or background work.
|
|
208
|
+
- On iOS 18 and later the update also carries diagnostics (authorization
|
|
209
|
+
denied, Location Services off system-wide, location unavailable, app not
|
|
210
|
+
sufficiently in use). React with a visible fallback instead of waiting on a
|
|
211
|
+
sequence that will not deliver.
|
|
188
212
|
|
|
189
213
|
```swift
|
|
190
214
|
import CoreLocation
|
|
215
|
+
import Observation
|
|
191
216
|
|
|
192
217
|
@MainActor
|
|
193
218
|
@Observable
|
|
194
|
-
final class
|
|
195
|
-
var
|
|
196
|
-
private var
|
|
219
|
+
final class RunTracker {
|
|
220
|
+
private(set) var lastFix: CLLocation?
|
|
221
|
+
private var stream: Task<Void, Never>?
|
|
197
222
|
|
|
198
|
-
func
|
|
199
|
-
|
|
223
|
+
func begin() {
|
|
224
|
+
stream?.cancel()
|
|
225
|
+
stream = Task {
|
|
200
226
|
do {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
location.horizontalAccuracy < 50 else { continue }
|
|
207
|
-
currentLocation = location
|
|
227
|
+
for try await reading in CLLocationUpdate.liveUpdates() {
|
|
228
|
+
guard let fix = reading.location,
|
|
229
|
+
fix.horizontalAccuracy >= 0,
|
|
230
|
+
fix.horizontalAccuracy < 50 else { continue }
|
|
231
|
+
lastFix = fix
|
|
208
232
|
}
|
|
209
233
|
} catch is CancellationError {
|
|
210
|
-
// Expected when tracking stops.
|
|
211
234
|
} catch {
|
|
212
|
-
|
|
235
|
+
lastFix = nil
|
|
213
236
|
}
|
|
214
237
|
}
|
|
215
238
|
}
|
|
216
239
|
|
|
217
|
-
func
|
|
218
|
-
|
|
219
|
-
|
|
240
|
+
func end() {
|
|
241
|
+
stream?.cancel()
|
|
242
|
+
stream = nil
|
|
220
243
|
}
|
|
221
244
|
}
|
|
222
245
|
```
|
|
223
246
|
|
|
247
|
+
Diagnostics, the full filter set and a mapping from each state to UI are in
|
|
248
|
+
the [location reference](references/mapkit-corelocation-patterns.md#diagnostics-and-fallbacks-ios-18).
|
|
249
|
+
|
|
224
250
|
### CLServiceSession (iOS 18+)
|
|
225
251
|
|
|
226
|
-
|
|
227
|
-
|
|
252
|
+
A `CLServiceSession` states what authorization a feature needs, for as long
|
|
253
|
+
as the feature runs. Store it in a property and release it when the feature
|
|
254
|
+
ends. On iOS 18 `liveUpdates()` and `CLMonitor` create a session implicitly
|
|
255
|
+
when you have not; create one yourself when you need `.always` or full
|
|
256
|
+
accuracy. It is not available on macOS.
|
|
228
257
|
|
|
229
258
|
```swift
|
|
230
|
-
|
|
231
|
-
let
|
|
259
|
+
@available(iOS 18, *)
|
|
260
|
+
let nearbySession = CLServiceSession(
|
|
232
261
|
authorization: .whenInUse,
|
|
233
|
-
fullAccuracyPurposeKey: "
|
|
262
|
+
fullAccuracyPurposeKey: "NearbyTrails"
|
|
234
263
|
)
|
|
235
|
-
// Hold `session` as a stored property; release it when done.
|
|
236
264
|
```
|
|
237
265
|
|
|
238
|
-
|
|
239
|
-
`CLServiceSession` if you do not create one explicitly. Create one explicitly
|
|
240
|
-
when you need `.always` authorization or full accuracy.
|
|
266
|
+
### Authorization
|
|
241
267
|
|
|
242
|
-
|
|
268
|
+
- `NSLocationWhenInUseUsageDescription` is required for any location use.
|
|
269
|
+
- `NSLocationAlwaysAndWhenInUseUsageDescription` only when the feature truly
|
|
270
|
+
needs `.always`.
|
|
243
271
|
|
|
244
|
-
|
|
245
|
-
// Info.plist keys (required):
|
|
246
|
-
// NSLocationWhenInUseUsageDescription
|
|
247
|
-
// NSLocationAlwaysAndWhenInUseUsageDescription (only if .always needed)
|
|
272
|
+
A denied user should see why the feature is empty and a way out:
|
|
248
273
|
|
|
249
|
-
|
|
250
|
-
struct
|
|
274
|
+
```swift
|
|
275
|
+
struct LocationOffView: View {
|
|
251
276
|
@Environment(\.openURL) private var openURL
|
|
252
277
|
|
|
253
278
|
var body: some View {
|
|
254
279
|
ContentUnavailableView {
|
|
255
|
-
Label("Location
|
|
280
|
+
Label("Location is off", systemImage: "location.slash")
|
|
256
281
|
} description: {
|
|
257
|
-
Text("
|
|
282
|
+
Text("Allow location access to see trails near you.")
|
|
258
283
|
} actions: {
|
|
259
284
|
Button("Open Settings") {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
285
|
+
guard let settings = URL(string: UIApplication.openSettingsURLString) else { return }
|
|
286
|
+
openURL(settings)
|
|
263
287
|
}
|
|
264
288
|
}
|
|
265
289
|
}
|
|
@@ -268,164 +292,168 @@ struct LocationPermissionView: View {
|
|
|
268
292
|
|
|
269
293
|
## Geocoding
|
|
270
294
|
|
|
271
|
-
###
|
|
295
|
+
### MKGeocodingRequest and MKReverseGeocodingRequest (iOS 26+)
|
|
296
|
+
|
|
297
|
+
MapKit now geocodes by itself and returns `MKMapItem`, which carries more than
|
|
298
|
+
a placemark: an `MKAddress` and `MKAddressRepresentations` for formatting.
|
|
299
|
+
Both initializers are failable; `mapItems` is an async throwing property.
|
|
272
300
|
|
|
273
301
|
```swift
|
|
274
|
-
|
|
302
|
+
@available(iOS 26, *)
|
|
303
|
+
func lookUp(_ text: String) async throws -> MKMapItem? {
|
|
304
|
+
guard let request = MKGeocodingRequest(addressString: text) else { return nil }
|
|
305
|
+
return try await request.mapItems.first
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Reverse lookup is `MKReverseGeocodingRequest(location:)`. Formatting and
|
|
310
|
+
batch lookups are in [Geocoding and Places on iOS 26](references/mapkit-patterns.md#geocoding-and-places-on-ios-26).
|
|
311
|
+
|
|
312
|
+
### CLGeocoder (iOS 5 to iOS 25, deprecated in iOS 26)
|
|
275
313
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
314
|
+
Use it only while the deployment target is below iOS 26. Every `CLPlacemark`
|
|
315
|
+
field is optional. `CLGeocoder` is not `Sendable`, so Swift 6 rejects one
|
|
316
|
+
shared global instance; make a geocoder for each lookup instead.
|
|
317
|
+
|
|
318
|
+
```swift
|
|
319
|
+
func coordinate(for address: String) async throws -> CLLocationCoordinate2D? {
|
|
320
|
+
try await CLGeocoder().geocodeAddressString(address).first?.location?.coordinate
|
|
280
321
|
}
|
|
281
322
|
|
|
282
|
-
|
|
283
|
-
let
|
|
284
|
-
let
|
|
285
|
-
|
|
286
|
-
let address = [placemark.name, placemark.locality, placemark.administrativeArea]
|
|
323
|
+
func label(for spot: CLLocation) async throws -> String {
|
|
324
|
+
let marks = try await CLGeocoder().reverseGeocodeLocation(spot)
|
|
325
|
+
guard let mark = marks.first else { return "" }
|
|
326
|
+
return [mark.name, mark.locality, mark.administrativeArea]
|
|
287
327
|
.compactMap { $0 }
|
|
288
328
|
.joined(separator: ", ")
|
|
289
329
|
}
|
|
290
330
|
```
|
|
291
331
|
|
|
292
|
-
### MKGeocodingRequest and MKReverseGeocodingRequest (iOS 26+)
|
|
293
|
-
|
|
294
|
-
New MapKit-native geocoding returns `MKMapItem` with richer data and `MKAddress`
|
|
295
|
-
/ `MKAddressRepresentations` for flexible address formatting. Construct
|
|
296
|
-
`MKGeocodingRequest(addressString:)` or `MKReverseGeocodingRequest(location:)` and
|
|
297
|
-
await `.mapItems`. See [references/mapkit-patterns.md](references/mapkit-patterns.md) ("iOS 26 New
|
|
298
|
-
APIs") for both, plus `MKAddressRepresentations` formatting.
|
|
299
|
-
|
|
300
332
|
## Search
|
|
301
333
|
|
|
302
|
-
### MKLocalSearchCompleter (
|
|
303
|
-
|
|
304
|
-
Set `queryFragment` on an `MKLocalSearchCompleter` and read `results` from its
|
|
305
|
-
delegate for suggestions. Debounce input (300ms+) and constrain `completer.region`
|
|
306
|
-
to the visible map region.
|
|
334
|
+
### MKLocalSearchCompleter (autocomplete)
|
|
307
335
|
|
|
308
|
-
|
|
336
|
+
Set `queryFragment` and read `results` from the delegate. Debounce input by
|
|
337
|
+
300 ms or more and set `completer.region` to the visible map region so
|
|
338
|
+
suggestions stay local.
|
|
309
339
|
|
|
310
|
-
|
|
311
|
-
results:
|
|
340
|
+
### MKLocalSearch (full results)
|
|
312
341
|
|
|
313
342
|
```swift
|
|
314
|
-
func
|
|
315
|
-
let request = MKLocalSearch.Request(completion:
|
|
343
|
+
func places(for pick: MKLocalSearchCompletion) async throws -> [MKMapItem] {
|
|
344
|
+
let request = MKLocalSearch.Request(completion: pick)
|
|
316
345
|
request.resultTypes = [.pointOfInterest, .address]
|
|
317
|
-
let
|
|
318
|
-
return
|
|
346
|
+
let reply = try await MKLocalSearch(request: request).start()
|
|
347
|
+
return reply.mapItems
|
|
319
348
|
}
|
|
320
349
|
```
|
|
321
350
|
|
|
322
|
-
For
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
351
|
+
For free text, set `request.naturalLanguageQuery` and `request.region`
|
|
352
|
+
instead of passing a completion. The delegate-based service and the
|
|
353
|
+
`.searchable` screen are in
|
|
354
|
+
[Autocomplete Search](references/mapkit-patterns.md#autocomplete-search).
|
|
326
355
|
|
|
327
356
|
## Directions
|
|
328
357
|
|
|
358
|
+
`MKMapItem` and `MKRoute` are not `Sendable`. Keep the helper on the main
|
|
359
|
+
actor, where the SwiftUI view that calls it already runs, so the items never
|
|
360
|
+
cross an isolation boundary:
|
|
361
|
+
|
|
329
362
|
```swift
|
|
330
|
-
|
|
331
|
-
|
|
363
|
+
@MainActor
|
|
364
|
+
func fastestRoute(
|
|
365
|
+
from start: MKMapItem,
|
|
366
|
+
to end: MKMapItem,
|
|
367
|
+
by mode: MKDirectionsTransportType = .automobile
|
|
368
|
+
) async throws -> MKRoute? {
|
|
332
369
|
let request = MKDirections.Request()
|
|
333
|
-
request.source =
|
|
334
|
-
request.destination =
|
|
335
|
-
request.transportType =
|
|
336
|
-
let
|
|
337
|
-
|
|
338
|
-
return response.routes.first
|
|
370
|
+
request.source = start
|
|
371
|
+
request.destination = end
|
|
372
|
+
request.transportType = mode
|
|
373
|
+
let reply = try await MKDirections(request: request).calculate()
|
|
374
|
+
return reply.routes.first
|
|
339
375
|
}
|
|
340
376
|
```
|
|
341
377
|
|
|
342
|
-
###
|
|
378
|
+
### Drawing the route
|
|
343
379
|
|
|
344
380
|
```swift
|
|
345
|
-
|
|
381
|
+
struct FerryRouteMap: View {
|
|
382
|
+
let pier: MKMapItem
|
|
383
|
+
let island: MKMapItem
|
|
384
|
+
@State private var route: MKRoute?
|
|
346
385
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
386
|
+
var body: some View {
|
|
387
|
+
Map {
|
|
388
|
+
if let route {
|
|
389
|
+
MapPolyline(route.polyline).stroke(.teal, lineWidth: 5)
|
|
390
|
+
}
|
|
391
|
+
Marker(item: pier)
|
|
392
|
+
Marker(item: island)
|
|
393
|
+
}
|
|
394
|
+
.task { route = try? await fastestRoute(from: pier, to: island) }
|
|
351
395
|
}
|
|
352
|
-
Marker("Start", coordinate: startCoord)
|
|
353
|
-
Marker("End", coordinate: endCoord)
|
|
354
|
-
}
|
|
355
|
-
.task {
|
|
356
|
-
route = try? await getDirections(from: startItem, to: endItem)
|
|
357
396
|
}
|
|
358
397
|
```
|
|
359
398
|
|
|
360
|
-
###
|
|
399
|
+
### Travel time only, and cycling
|
|
361
400
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
Directions
|
|
401
|
+
- `MKDirections.calculateETA()` returns `expectedTravelTime` without route
|
|
402
|
+
geometry; use it when a label is all you need
|
|
403
|
+
([ETA Calculation](references/mapkit-patterns.md#eta-calculation)).
|
|
404
|
+
- Bike routes: `request.transportType = .cycling`, iOS 14 and later
|
|
405
|
+
([Cycling Directions](references/mapkit-patterns.md#cycling-directions-ios-14)).
|
|
367
406
|
|
|
368
407
|
## PlaceDescriptor (iOS 26+)
|
|
369
408
|
|
|
370
|
-
|
|
371
|
-
|
|
409
|
+
`PlaceDescriptor`, from `GeoToolbox`, describes a place by coordinate or
|
|
410
|
+
address when you have no MapKit place ID. `MKMapItemRequest` resolves it into
|
|
411
|
+
a full `MKMapItem`.
|
|
372
412
|
|
|
373
413
|
```swift
|
|
374
414
|
import GeoToolbox
|
|
415
|
+
import MapKit
|
|
375
416
|
|
|
376
417
|
@available(iOS 26, *)
|
|
377
|
-
func
|
|
378
|
-
let
|
|
379
|
-
|
|
380
|
-
commonName: name
|
|
381
|
-
)
|
|
382
|
-
let request = MKMapItemRequest(placeDescriptor: descriptor)
|
|
383
|
-
return try await request.mapItem
|
|
418
|
+
func mapItem(near point: CLLocationCoordinate2D, named name: String) async throws -> MKMapItem {
|
|
419
|
+
let place = PlaceDescriptor(representations: [.coordinate(point)], commonName: name)
|
|
420
|
+
return try await MKMapItemRequest(placeDescriptor: place).mapItem
|
|
384
421
|
}
|
|
385
422
|
```
|
|
386
423
|
|
|
387
424
|
## Common Mistakes
|
|
388
425
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
for
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
**DON'T:** Let `liveUpdates()` run from an unowned task after the map/view is gone.
|
|
401
|
-
**DO:** Store the `Task`, cancel it when the feature stops, and filter invalid, inaccurate, stale, or impossible movement fixes.
|
|
402
|
-
|
|
403
|
-
**DON'T:** Force-unwrap `CLPlacemark` properties - they are all optional.
|
|
404
|
-
**DO:** Use nil-coalescing: `placemark.locality ?? "Unknown"`.
|
|
405
|
-
|
|
406
|
-
**DON'T:** Fire `MKLocalSearchCompleter` queries on every keystroke.
|
|
407
|
-
**DO:** Debounce with `.task(id: searchText)` + `Task.sleep(for: .milliseconds(300))`.
|
|
408
|
-
|
|
409
|
-
**DON'T:** Silently fail when location authorization is denied.
|
|
410
|
-
**DO:** Detect `.denied` status and show an alert with a Settings deep link.
|
|
411
|
-
|
|
412
|
-
**DON'T:** Assume geocoding always succeeds - handle empty results and network errors.
|
|
426
|
+
| Mistake | Do instead |
|
|
427
|
+
|---------|-----------|
|
|
428
|
+
| Asking for Always authorization at launch | Start with when-in-use; ask for `.always` only for a background feature that needs relaunch after termination |
|
|
429
|
+
| No `CLServiceSession` on iOS 18+ | Hold one for the life of the feature |
|
|
430
|
+
| A `CLLocationManagerDelegate` just to read position on iOS 17+ | Iterate `liveUpdates()`; less code, same result |
|
|
431
|
+
| Ignoring diagnostics (denied, off system-wide, unavailable) | Switch to a reduced mode, point the user at Settings, and leave search and manual entry usable |
|
|
432
|
+
| `liveUpdates()` in a task nobody owns | Keep the `Task` in a property and cancel it with the screen; filter bad fixes |
|
|
433
|
+
| Force-unwrapping `CLPlacemark` fields | They are all optional: `mark.locality ?? "Unknown"` |
|
|
434
|
+
| Querying the completer on every keystroke | `.task(id: text)` plus `Task.sleep(for: .milliseconds(300))` |
|
|
435
|
+
| Silent failure when denied | Detect `.denied` and offer a Settings link |
|
|
436
|
+
| Assuming geocoding returns something | Plan for zero matches and for being offline |
|
|
413
437
|
|
|
414
438
|
## Review Checklist
|
|
415
439
|
|
|
416
|
-
- [ ]
|
|
417
|
-
- [ ]
|
|
418
|
-
- [ ] `CLLocationUpdate` task cancelled when not needed
|
|
419
|
-
- [ ]
|
|
420
|
-
- [ ]
|
|
421
|
-
- [ ] Geocoding
|
|
422
|
-
- [ ]
|
|
423
|
-
- [ ] `CLMonitor`
|
|
440
|
+
- [ ] `NSLocationWhenInUseUsageDescription` is present and names a concrete benefit
|
|
441
|
+
- [ ] A denied user gets an explanation and a Settings deep link
|
|
442
|
+
- [ ] The `CLLocationUpdate` task is cancelled when not needed, to save battery
|
|
443
|
+
- [ ] Requested accuracy matches what the feature needs
|
|
444
|
+
- [ ] Annotation data is `Identifiable` with stable IDs
|
|
445
|
+
- [ ] Geocoding handles network failure and empty results
|
|
446
|
+
- [ ] Completer input is debounced
|
|
447
|
+
- [ ] `CLMonitor` stays at or under 20 conditions and the instance is kept alive
|
|
424
448
|
- [ ] Background location uses `CLBackgroundActivitySession`
|
|
425
|
-
- [ ]
|
|
426
|
-
- [ ]
|
|
449
|
+
- [ ] The map has been tried with VoiceOver
|
|
450
|
+
- [ ] Annotation models and location-driven UI state are `@MainActor`
|
|
427
451
|
|
|
428
452
|
## References
|
|
429
453
|
|
|
430
|
-
- [
|
|
431
|
-
|
|
454
|
+
- [Map patterns](references/mapkit-patterns.md): map setup,
|
|
455
|
+
annotations, camera, search, routes, dense maps and clustering, Look Around,
|
|
456
|
+
snapshots, iOS 26 APIs.
|
|
457
|
+
- [Location patterns](references/mapkit-corelocation-patterns.md):
|
|
458
|
+
`CLLocationUpdate`, `CLServiceSession`, `CLMonitor`, background location,
|
|
459
|
+
testing, privacy keys.
|