@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,499 +1,377 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: eventkit-calendar
|
|
3
|
-
description: "
|
|
3
|
+
description: "EventKit and EventKitUI: adding events to the user's calendar, creating reminders, recurrence rules, calendar or reminders access, event editors, calendar choosers, alarms, change observation, with EKEventStore, EKEvent, EKReminder, EKCalendar, EKRecurrenceRule, EKEventEditViewController, EKCalendarChooser. Use when an app reads or writes the user's calendars or reminders. Not for app-owned local or push notifications (push-notifications)."
|
|
4
4
|
metadata:
|
|
5
|
-
source:
|
|
5
|
+
source: multi-agent-pipeline
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
# EventKit
|
|
8
|
+
# EventKit: Calendars and Reminders
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
EventKit reads and writes the same calendars and reminder lists the user sees
|
|
11
|
+
in Calendar and Reminders. EventKitUI adds system screens for editing, viewing
|
|
12
|
+
and choosing calendars. Examples assume Swift 6.3 and iOS 26.
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
- [Setup](#setup)
|
|
17
|
-
- [Authorization](#authorization)
|
|
18
|
-
- [Creating Events](#creating-events)
|
|
19
|
-
- [Fetching Events](#fetching-events)
|
|
20
|
-
- [Reminders](#reminders)
|
|
21
|
-
- [Recurrence Rules](#recurrence-rules)
|
|
22
|
-
- [Alarms](#alarms)
|
|
23
|
-
- [EventKitUI Controllers](#eventkitui-controllers)
|
|
24
|
-
- [Observing Changes](#observing-changes)
|
|
25
|
-
- [Common Mistakes](#common-mistakes)
|
|
26
|
-
- [Review Checklist](#review-checklist)
|
|
27
|
-
- [References](#references)
|
|
14
|
+
Alerts the app itself sends (local notifications, pushes) are not EventKit;
|
|
15
|
+
use `UNUserNotificationCenter` and the `push-notifications` skill for those.
|
|
28
16
|
|
|
29
17
|
## Setup
|
|
30
18
|
|
|
31
|
-
###
|
|
19
|
+
### Usage descriptions
|
|
32
20
|
|
|
33
|
-
|
|
21
|
+
| Key | When |
|
|
22
|
+
|-----|------|
|
|
23
|
+
| `NSCalendarsFullAccessUsageDescription` | The app reads and writes events |
|
|
24
|
+
| `NSCalendarsWriteOnlyAccessUsageDescription` | The app writes events directly but never reads them |
|
|
25
|
+
| `NSRemindersFullAccessUsageDescription` | The app reads or writes reminders |
|
|
34
26
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
27
|
+
- Since iOS 17, creating events solely through
|
|
28
|
+
`EKEventEditViewController` requires no calendar permission and no calendar
|
|
29
|
+
usage string.
|
|
30
|
+
- Writing events with EventKit directly needs write-only or full access.
|
|
31
|
+
Reading or fetching events needs full access.
|
|
32
|
+
- Reminders have a single level: full access.
|
|
33
|
+
- If the app also runs on iOS 10 through 16, add the older
|
|
34
|
+
`NSCalendarsUsageDescription` and `NSRemindersUsageDescription` too. When
|
|
35
|
+
EventKitUI on those versions may show contact names or photos, add
|
|
36
|
+
`NSContactsUsageDescription` as well.
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
person create an event does not need calendar authorization or calendar usage
|
|
43
|
-
strings. Direct EventKit writes need write-only or full calendar access; any
|
|
44
|
-
event read/fetch needs full calendar access. Reminders have only full access.
|
|
38
|
+
### One store
|
|
45
39
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
> UI may need contact display names or avatars.
|
|
40
|
+
Create a single `EKEventStore` and share it. Events, reminders and calendars
|
|
41
|
+
belong to the store that produced them; passing an object from one store to
|
|
42
|
+
another is undefined behaviour.
|
|
50
43
|
|
|
51
|
-
|
|
44
|
+
## Asking for access
|
|
52
45
|
|
|
53
|
-
|
|
54
|
-
|
|
46
|
+
iOS 17 split calendar access into levels. Ask for the smallest one the feature
|
|
47
|
+
needs.
|
|
55
48
|
|
|
56
49
|
```swift
|
|
57
50
|
import EventKit
|
|
58
51
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
## Authorization
|
|
63
|
-
|
|
64
|
-
iOS 17+ introduced granular access levels. Request the narrowest access that
|
|
65
|
-
matches the feature. If the deployment target includes earlier OS versions,
|
|
66
|
-
availability-guard the iOS 17+ methods and fall back to `requestAccess(to:)`
|
|
67
|
-
only before iOS 17.
|
|
68
|
-
|
|
69
|
-
### Full Access to Events
|
|
70
|
-
|
|
71
|
-
Call `try await eventStore.requestFullAccessToEvents()` when the app needs to
|
|
72
|
-
read, edit, delete, or fetch calendar events.
|
|
52
|
+
@MainActor
|
|
53
|
+
final class Planner {
|
|
54
|
+
let store = EKEventStore()
|
|
73
55
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
need to read existing events.
|
|
78
|
-
|
|
79
|
-
Call `try await eventStore.requestWriteOnlyAccessToEvents()` before direct
|
|
80
|
-
EventKit writes that do not use `EKEventEditViewController`.
|
|
81
|
-
|
|
82
|
-
With write-only access, EventKit can create events but cannot read calendars or
|
|
83
|
-
events, including events the app created. Calendar reads return a virtual
|
|
84
|
-
calendar and event fetches return no events.
|
|
56
|
+
func allowReadingSchedule() async throws -> Bool {
|
|
57
|
+
try await store.requestFullAccessToEvents()
|
|
58
|
+
}
|
|
85
59
|
|
|
86
|
-
|
|
87
|
-
|
|
60
|
+
func allowAddingEvents() async throws -> Bool {
|
|
61
|
+
try await store.requestWriteOnlyAccessToEvents()
|
|
62
|
+
}
|
|
88
63
|
|
|
89
|
-
|
|
64
|
+
func allowReminders() async throws -> Bool {
|
|
65
|
+
try await store.requestFullAccessToReminders()
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
90
69
|
|
|
91
|
-
|
|
92
|
-
|
|
70
|
+
`Planner` owns the app's one store. `EKEventStore` is not `Sendable`, so
|
|
71
|
+
Swift 6 rejects it as a plain global; keeping it on a `@MainActor` type is the
|
|
72
|
+
simple fix. Later functions that use `store` are `Planner` methods; the
|
|
73
|
+
EventKitUI snippets run inside a view controller that can reach the same store.
|
|
93
74
|
|
|
94
|
-
|
|
75
|
+
- **Full event access**: required to read, change, delete or fetch
|
|
76
|
+
events.
|
|
77
|
+
- **Write-only event access**: enough to save events directly without the
|
|
78
|
+
system editor. It cannot read anything, not even events the app itself
|
|
79
|
+
saved: calendar lookups return a single virtual calendar and event fetches
|
|
80
|
+
come back empty. If the app will later need to check, edit or sync what it
|
|
81
|
+
saved, ask for full access instead.
|
|
82
|
+
- **Full access to reminders**: required for any reminder work.
|
|
95
83
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
84
|
+
When the deployment target is below iOS 17, wrap these calls in
|
|
85
|
+
`if #available(iOS 17.0, *)` and use `requestAccess(to:)` only in the `else`
|
|
86
|
+
branch.
|
|
99
87
|
|
|
100
|
-
|
|
88
|
+
### Checking the current status
|
|
101
89
|
|
|
102
90
|
```swift
|
|
103
|
-
func
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
event.endDate = endDate
|
|
113
|
-
event.calendar = calendar ?? eventStore.defaultCalendarForNewEvents
|
|
114
|
-
|
|
115
|
-
try eventStore.save(event, span: .thisEvent)
|
|
91
|
+
func canReadEvents() -> Bool {
|
|
92
|
+
switch EKEventStore.authorizationStatus(for: .event) {
|
|
93
|
+
case .fullAccess:
|
|
94
|
+
return true
|
|
95
|
+
case .writeOnly, .notDetermined, .restricted, .denied:
|
|
96
|
+
return false
|
|
97
|
+
@unknown default:
|
|
98
|
+
return false
|
|
99
|
+
}
|
|
116
100
|
}
|
|
117
101
|
```
|
|
118
102
|
|
|
119
|
-
|
|
103
|
+
Only `.fullAccess` allows reads. The same call with `.reminder` covers
|
|
104
|
+
reminders.
|
|
120
105
|
|
|
121
|
-
|
|
122
|
-
// List writable calendars
|
|
123
|
-
let calendars = eventStore.calendars(for: .event)
|
|
124
|
-
.filter { $0.allowsContentModifications }
|
|
125
|
-
|
|
126
|
-
// Use the first writable calendar, or the default
|
|
127
|
-
let targetCalendar = calendars.first ?? eventStore.defaultCalendarForNewEvents
|
|
128
|
-
event.calendar = targetCalendar
|
|
129
|
-
```
|
|
106
|
+
## Events
|
|
130
107
|
|
|
131
|
-
###
|
|
108
|
+
### Saving an event
|
|
132
109
|
|
|
133
110
|
```swift
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
111
|
+
func addRehearsal(start: Date) throws {
|
|
112
|
+
let rehearsal = EKEvent(eventStore: store)
|
|
113
|
+
rehearsal.title = "Choir Rehearsal"
|
|
114
|
+
rehearsal.startDate = start
|
|
115
|
+
rehearsal.endDate = start.addingTimeInterval(90 * 60)
|
|
116
|
+
rehearsal.calendar = store.defaultCalendarForNewEvents
|
|
117
|
+
rehearsal.timeZone = TimeZone(identifier: "Europe/Lisbon")
|
|
118
|
+
|
|
119
|
+
let hall = EKStructuredLocation(title: "Parish Hall")
|
|
120
|
+
hall.geoLocation = CLLocation(latitude: 38.7139, longitude: -9.1334)
|
|
121
|
+
rehearsal.structuredLocation = hall
|
|
122
|
+
|
|
123
|
+
try store.save(rehearsal, span: .thisEvent)
|
|
124
|
+
}
|
|
139
125
|
```
|
|
140
126
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
Use a date-range predicate to query events. The `events(matching:)` method
|
|
144
|
-
returns occurrences of recurring events expanded within the range. Fetching
|
|
145
|
-
events requires full calendar access; write-only access returns no events.
|
|
146
|
-
Event predicates are capped to a four-year span, and `events(matching:)` /
|
|
147
|
-
`enumerateEvents(matching:using:)` are synchronous and return only committed
|
|
148
|
-
events.
|
|
127
|
+
Before saving into a calendar other than the default, make sure it accepts
|
|
128
|
+
changes:
|
|
149
129
|
|
|
150
130
|
```swift
|
|
151
|
-
|
|
152
|
-
let predicate = eventStore.predicateForEvents(
|
|
153
|
-
withStart: start,
|
|
154
|
-
end: end,
|
|
155
|
-
calendars: nil // nil = all calendars
|
|
156
|
-
)
|
|
157
|
-
return eventStore.events(matching: predicate)
|
|
158
|
-
.sorted { $0.startDate < $1.startDate }
|
|
159
|
-
}
|
|
131
|
+
let writable = store.calendars(for: .event).filter(\.allowsContentModifications)
|
|
160
132
|
```
|
|
161
133
|
|
|
162
|
-
|
|
134
|
+
Setting `timeZone` matters for events tied to a place; without it, a traveller
|
|
135
|
+
sees the wrong local time.
|
|
136
|
+
|
|
137
|
+
### Fetching events
|
|
163
138
|
|
|
164
139
|
```swift
|
|
165
|
-
|
|
166
|
-
|
|
140
|
+
func upcoming(days: Int) -> [EKEvent] {
|
|
141
|
+
let today = Calendar.current.startOfDay(for: .now)
|
|
142
|
+
let horizon = today.addingTimeInterval(TimeInterval(days) * 86_400)
|
|
143
|
+
let window = store.predicateForEvents(withStart: today, end: horizon, calendars: nil)
|
|
144
|
+
return store.events(matching: window).sorted { $0.startDate < $1.startDate }
|
|
167
145
|
}
|
|
168
146
|
```
|
|
169
147
|
|
|
170
|
-
|
|
148
|
+
- `calendars: nil` means every calendar.
|
|
149
|
+
- Recurring events come back as individual occurrences inside the range.
|
|
150
|
+
- Fetching needs full access; under write-only the result is empty.
|
|
151
|
+
- A predicate can span at most four years.
|
|
152
|
+
- Both `events(matching:)` and `enumerateEvents(matching:using:)` run
|
|
153
|
+
synchronously, and neither sees uncommitted changes.
|
|
154
|
+
- One event by id: `store.event(withIdentifier:)`.
|
|
171
155
|
|
|
172
|
-
|
|
156
|
+
## Reminders
|
|
173
157
|
|
|
174
158
|
```swift
|
|
175
|
-
func
|
|
176
|
-
let
|
|
177
|
-
reminder
|
|
178
|
-
reminder.
|
|
179
|
-
|
|
180
|
-
let
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
)
|
|
184
|
-
reminder.dueDateComponents = dueDateComponents
|
|
185
|
-
|
|
186
|
-
try eventStore.save(reminder, commit: true)
|
|
159
|
+
func remindToWater(at due: Date) throws {
|
|
160
|
+
guard let list = store.defaultCalendarForNewReminders() else { throw PlannerError.noReminderList }
|
|
161
|
+
let reminder = EKReminder(eventStore: store)
|
|
162
|
+
reminder.title = "Water the tomatoes"
|
|
163
|
+
reminder.calendar = list
|
|
164
|
+
let parts: Set<Calendar.Component> = [.year, .month, .day, .hour, .minute]
|
|
165
|
+
reminder.dueDateComponents = Calendar.current.dateComponents(parts, from: due)
|
|
166
|
+
try store.save(reminder, commit: true)
|
|
187
167
|
}
|
|
188
168
|
```
|
|
189
169
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
170
|
+
Reminder fetches use a completion handler that runs on a background queue.
|
|
171
|
+
`EKReminder` is not `Sendable`, so Swift 6 refuses to pass the array through a
|
|
172
|
+
continuation into a main-actor method. Copy what the caller needs into a
|
|
173
|
+
`Sendable` value inside the callback and keep the identifier for a later
|
|
174
|
+
`store.calendarItem(withIdentifier:)` lookup:
|
|
193
175
|
|
|
194
176
|
```swift
|
|
195
|
-
|
|
196
|
-
let
|
|
177
|
+
struct ReminderRow: Sendable {
|
|
178
|
+
let id: String
|
|
179
|
+
let title: String
|
|
180
|
+
let due: DateComponents?
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
func openReminders(until end: Date) async -> [ReminderRow] {
|
|
184
|
+
let query = store.predicateForIncompleteReminders(
|
|
197
185
|
withDueDateStarting: nil,
|
|
198
|
-
ending:
|
|
186
|
+
ending: end,
|
|
199
187
|
calendars: nil
|
|
200
188
|
)
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
189
|
+
return await withCheckedContinuation { resume in
|
|
190
|
+
store.fetchReminders(matching: query) { found in
|
|
191
|
+
let rows = (found ?? []).map {
|
|
192
|
+
ReminderRow(id: $0.calendarItemIdentifier, title: $0.title, due: $0.dueDateComponents)
|
|
193
|
+
}
|
|
194
|
+
resume.resume(returning: rows)
|
|
205
195
|
}
|
|
206
196
|
}
|
|
207
197
|
}
|
|
208
198
|
```
|
|
209
199
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
```swift
|
|
213
|
-
func completeReminder(_ reminder: EKReminder) throws {
|
|
214
|
-
reminder.isCompleted = true
|
|
215
|
-
try eventStore.save(reminder, commit: true)
|
|
216
|
-
}
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
## Recurrence Rules
|
|
200
|
+
To tick a reminder off, set `isCompleted = true` and save it with
|
|
201
|
+
`commit: true`.
|
|
220
202
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
### Simple Recurrence
|
|
203
|
+
## Recurrence
|
|
224
204
|
|
|
225
205
|
```swift
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
recurrenceWith: .weekly,
|
|
229
|
-
interval: 1,
|
|
230
|
-
end: nil
|
|
231
|
-
)
|
|
232
|
-
event.addRecurrenceRule(weeklyRule)
|
|
206
|
+
let everyWeek = EKRecurrenceRule(recurrenceWith: .weekly, interval: 1, end: nil)
|
|
207
|
+
standup.addRecurrenceRule(everyWeek)
|
|
233
208
|
|
|
234
|
-
|
|
235
|
-
let
|
|
236
|
-
recurrenceWith: .weekly,
|
|
237
|
-
interval: 2,
|
|
238
|
-
end: EKRecurrenceEnd(occurrenceCount: 10)
|
|
239
|
-
)
|
|
240
|
-
|
|
241
|
-
// Monthly, ending on a specific date
|
|
242
|
-
let monthlyRule = EKRecurrenceRule(
|
|
243
|
-
recurrenceWith: .monthly,
|
|
244
|
-
interval: 1,
|
|
245
|
-
end: EKRecurrenceEnd(end: endDate)
|
|
246
|
-
)
|
|
209
|
+
let tenTimes = EKRecurrenceEnd(occurrenceCount: 10)
|
|
210
|
+
let untilSummer = EKRecurrenceEnd(end: summerStart)
|
|
247
211
|
```
|
|
248
212
|
|
|
249
|
-
|
|
213
|
+
`end: nil` repeats forever; `EKRecurrenceEnd` stops after a count or on a
|
|
214
|
+
date. For richer patterns, the full initializer takes `daysOfTheWeek`,
|
|
215
|
+
`daysOfTheMonth`, `monthsOfTheYear`, `weeksOfTheYear`, `daysOfTheYear`,
|
|
216
|
+
`setPositions` and `end`:
|
|
250
217
|
|
|
251
218
|
```swift
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
EKRecurrenceDayOfWeek(.monday),
|
|
255
|
-
EKRecurrenceDayOfWeek(.wednesday)
|
|
256
|
-
]
|
|
257
|
-
|
|
258
|
-
let complexRule = EKRecurrenceRule(
|
|
259
|
-
recurrenceWith: .weekly,
|
|
219
|
+
let lastFridayMonthly = EKRecurrenceRule(
|
|
220
|
+
recurrenceWith: .monthly,
|
|
260
221
|
interval: 1,
|
|
261
|
-
daysOfTheWeek:
|
|
222
|
+
daysOfTheWeek: [EKRecurrenceDayOfWeek(.friday)],
|
|
262
223
|
daysOfTheMonth: nil,
|
|
263
224
|
monthsOfTheYear: nil,
|
|
264
225
|
weeksOfTheYear: nil,
|
|
265
226
|
daysOfTheYear: nil,
|
|
266
|
-
setPositions:
|
|
267
|
-
end:
|
|
227
|
+
setPositions: [-1],
|
|
228
|
+
end: tenTimes
|
|
268
229
|
)
|
|
269
|
-
event.addRecurrenceRule(complexRule)
|
|
270
230
|
```
|
|
271
231
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
When saving changes to a recurring event, specify the span:
|
|
275
|
-
|
|
276
|
-
```swift
|
|
277
|
-
// Change only this occurrence
|
|
278
|
-
try eventStore.save(event, span: .thisEvent)
|
|
279
|
-
|
|
280
|
-
// Change this and all future occurrences
|
|
281
|
-
try eventStore.save(event, span: .futureEvents)
|
|
282
|
-
```
|
|
232
|
+
Saving a recurring event needs an `EKSpan`: `.thisEvent` changes one
|
|
233
|
+
occurrence, `.futureEvents` changes it and every later one.
|
|
283
234
|
|
|
284
235
|
## Alarms
|
|
285
236
|
|
|
286
|
-
Attach alarms to events or reminders to trigger notifications.
|
|
287
|
-
|
|
288
237
|
```swift
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
event.addAlarm(alarm)
|
|
292
|
-
|
|
293
|
-
// At an absolute date
|
|
294
|
-
let absoluteAlarm = EKAlarm(absoluteDate: alertDate)
|
|
295
|
-
event.addAlarm(absoluteAlarm)
|
|
238
|
+
rehearsal.addAlarm(EKAlarm(relativeOffset: -30 * 60))
|
|
239
|
+
rehearsal.addAlarm(EKAlarm(absoluteDate: dayBefore))
|
|
296
240
|
```
|
|
297
241
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
242
|
+
A relative offset is in seconds from the start (negative means before). An
|
|
243
|
+
absolute alarm fires at a fixed moment. These alarms belong to the user's
|
|
244
|
+
calendar data; the system delivers them, and they do not need the app's
|
|
245
|
+
notification permission.
|
|
302
246
|
|
|
303
|
-
|
|
247
|
+
Reminders can also fire on arrival or departure: an `EKAlarm` carrying an
|
|
248
|
+
`EKStructuredLocation` and a `.enter` or `.leave` proximity. The full recipe,
|
|
249
|
+
including radius and priority, is in the
|
|
250
|
+
[patterns reference](references/eventkit-patterns.md).
|
|
304
251
|
|
|
305
|
-
|
|
252
|
+
## EventKitUI
|
|
306
253
|
|
|
307
|
-
|
|
254
|
+
### Event editor
|
|
308
255
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
256
|
+
`EKEventEditViewController` is the system editor for creating and changing
|
|
257
|
+
events. From iOS 17 it runs in a separate process with its own access, so the
|
|
258
|
+
app can use it without asking for calendar permission. The flip side: the app
|
|
259
|
+
cannot see what the user saved. Reading properties off the dismissed editor
|
|
260
|
+
tells you nothing reliable; a fresh fetch works only when the app has its own
|
|
261
|
+
full access.
|
|
313
262
|
|
|
314
263
|
```swift
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
264
|
+
func presentEditor(from host: UIViewController, delegate: any EKEventEditViewDelegate) {
|
|
265
|
+
let editor = EKEventEditViewController()
|
|
266
|
+
editor.eventStore = store
|
|
267
|
+
editor.editViewDelegate = delegate
|
|
268
|
+
host.present(editor, animated: true)
|
|
269
|
+
}
|
|
319
270
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
editor.eventStore = eventStore
|
|
323
|
-
editor.editViewDelegate = self
|
|
324
|
-
viewController.present(editor, animated: true)
|
|
325
|
-
}
|
|
271
|
+
final class TripViewController: UIViewController, @preconcurrency EKEventEditViewDelegate {
|
|
272
|
+
private var lastEditorOutcome: EKEventEditViewAction?
|
|
326
273
|
|
|
327
|
-
func eventEditViewController(
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
switch action {
|
|
332
|
-
case .saved:
|
|
333
|
-
// Event saved
|
|
334
|
-
break
|
|
335
|
-
case .canceled:
|
|
336
|
-
break
|
|
337
|
-
case .deleted:
|
|
338
|
-
break
|
|
339
|
-
@unknown default:
|
|
340
|
-
break
|
|
341
|
-
}
|
|
342
|
-
controller.dismiss(animated: true)
|
|
274
|
+
func eventEditViewController(_ editor: EKEventEditViewController,
|
|
275
|
+
didCompleteWith outcome: EKEventEditViewAction) {
|
|
276
|
+
lastEditorOutcome = outcome
|
|
277
|
+
editor.dismiss(animated: true)
|
|
343
278
|
}
|
|
344
279
|
}
|
|
345
280
|
```
|
|
346
281
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
let viewer = EKEventViewController()
|
|
353
|
-
viewer.event = existingEvent
|
|
354
|
-
viewer.allowsEditing = true
|
|
355
|
-
navigationController?.pushViewController(viewer, animated: true)
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
### EKCalendarChooser - Select Calendars
|
|
282
|
+
The outcome is `.saved`, `.canceled` or `.deleted`; a `switch` over it needs
|
|
283
|
+
an `@unknown default`. Whatever the outcome, the delegate dismisses the editor.
|
|
284
|
+
`EKEventEditViewDelegate` has no actor annotation, so a view controller, which
|
|
285
|
+
is main-actor isolated, adopts it with `@preconcurrency`. The editor calls the
|
|
286
|
+
delegate on the main thread, which is what that conformance checks at run time.
|
|
359
287
|
|
|
360
|
-
|
|
361
|
-
apps, the chooser behaves as writable-calendars-only and only allows a single
|
|
362
|
-
writable calendar selection.
|
|
288
|
+
### Viewer and calendar chooser
|
|
363
289
|
|
|
364
290
|
```swift
|
|
365
|
-
let
|
|
291
|
+
let detail = EKEventViewController()
|
|
292
|
+
detail.event = selectedEvent
|
|
293
|
+
detail.allowsEditing = true
|
|
294
|
+
navigationController?.pushViewController(detail, animated: true)
|
|
295
|
+
|
|
296
|
+
let picker = EKCalendarChooser(
|
|
366
297
|
selectionStyle: .multiple,
|
|
367
298
|
displayStyle: .allCalendars,
|
|
368
299
|
entityType: .event,
|
|
369
|
-
eventStore:
|
|
300
|
+
eventStore: store
|
|
370
301
|
)
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
## Observing Changes
|
|
378
|
-
|
|
379
|
-
Register for `EKEventStoreChanged` notifications to keep your UI in sync when
|
|
380
|
-
events are modified outside your app (e.g., by the Calendar app or a sync).
|
|
381
|
-
|
|
382
|
-
```swift
|
|
383
|
-
NotificationCenter.default.addObserver(
|
|
384
|
-
forName: .EKEventStoreChanged,
|
|
385
|
-
object: eventStore,
|
|
386
|
-
queue: .main
|
|
387
|
-
) { [weak self] _ in
|
|
388
|
-
self?.refreshEvents()
|
|
389
|
-
}
|
|
302
|
+
picker.showsCancelButton = true
|
|
303
|
+
picker.showsDoneButton = true
|
|
304
|
+
picker.delegate = self
|
|
305
|
+
let wrapper = UINavigationController(rootViewController: picker)
|
|
306
|
+
present(wrapper, animated: true)
|
|
390
307
|
```
|
|
391
308
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
is posted on the main actor.
|
|
395
|
-
|
|
396
|
-
On iOS 26+, you can also use the typed `EKEventStore.EventStoreChanged` /
|
|
397
|
-
`.changed` notification message behind availability checks.
|
|
309
|
+
`EKCalendarChooser` needs write-only or full access. Under write-only it lists
|
|
310
|
+
only writable calendars and allows one selection.
|
|
398
311
|
|
|
399
|
-
##
|
|
312
|
+
## Staying in sync
|
|
400
313
|
|
|
401
|
-
|
|
314
|
+
Other apps and sync services change calendars all the time. Listen for
|
|
315
|
+
`.EKEventStoreChanged` and refetch:
|
|
402
316
|
|
|
403
317
|
```swift
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
// CORRECT: Use the granular async methods
|
|
408
|
-
let granted = try await eventStore.requestFullAccessToEvents()
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
On iOS 17+, `requestAccess(to: .event)` does not prompt and throws. Keep it only
|
|
412
|
-
as an availability-guarded fallback for apps that still run on earlier systems.
|
|
413
|
-
|
|
414
|
-
### DON'T: Save events to a read-only calendar
|
|
415
|
-
|
|
416
|
-
```swift
|
|
417
|
-
// WRONG: No check -- will throw if calendar is read-only
|
|
418
|
-
event.calendar = someCalendar
|
|
419
|
-
try eventStore.save(event, span: .thisEvent)
|
|
420
|
-
|
|
421
|
-
// CORRECT: Verify the calendar allows modifications
|
|
422
|
-
guard someCalendar.allowsContentModifications else {
|
|
423
|
-
event.calendar = eventStore.defaultCalendarForNewEvents
|
|
424
|
-
return
|
|
318
|
+
let center = NotificationCenter.default
|
|
319
|
+
changeToken = center.addObserver(forName: .EKEventStoreChanged, object: store, queue: .main) { [weak self] _ in
|
|
320
|
+
Task { @MainActor in self?.reload() }
|
|
425
321
|
}
|
|
426
|
-
event.calendar = someCalendar
|
|
427
|
-
try eventStore.save(event, span: .thisEvent)
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
### DON'T: Ignore timezone when creating events
|
|
431
|
-
|
|
432
|
-
```swift
|
|
433
|
-
// WRONG: Event appears at wrong time for traveling users
|
|
434
|
-
event.startDate = Date()
|
|
435
|
-
event.endDate = Date().addingTimeInterval(3600)
|
|
436
|
-
|
|
437
|
-
// CORRECT: Set the timezone explicitly for location-specific events
|
|
438
|
-
event.timeZone = TimeZone(identifier: "America/New_York")
|
|
439
|
-
event.startDate = startDate
|
|
440
|
-
event.endDate = endDate
|
|
441
|
-
```
|
|
442
|
-
|
|
443
|
-
### DON'T: Forget to commit batched saves
|
|
444
|
-
|
|
445
|
-
```swift
|
|
446
|
-
// WRONG: Changes never persisted
|
|
447
|
-
try eventStore.save(event1, span: .thisEvent, commit: false)
|
|
448
|
-
try eventStore.save(event2, span: .thisEvent, commit: false)
|
|
449
|
-
// Missing commit!
|
|
450
|
-
|
|
451
|
-
// CORRECT: Commit after batching
|
|
452
|
-
try eventStore.save(event1, span: .thisEvent, commit: false)
|
|
453
|
-
try eventStore.save(event2, span: .thisEvent, commit: false)
|
|
454
|
-
try eventStore.commit()
|
|
455
|
-
```
|
|
456
|
-
|
|
457
|
-
### DON'T: Mix EKObjects from different event stores
|
|
458
|
-
|
|
459
|
-
```swift
|
|
460
|
-
// WRONG: Event fetched from storeA, saved to storeB
|
|
461
|
-
let event = storeA.event(withIdentifier: id)!
|
|
462
|
-
try storeB.save(event, span: .thisEvent) // Undefined behavior
|
|
463
|
-
|
|
464
|
-
// CORRECT: Use the same store throughout
|
|
465
|
-
let event = eventStore.event(withIdentifier: id)!
|
|
466
|
-
try eventStore.save(event, span: .thisEvent)
|
|
467
322
|
```
|
|
468
323
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
-
|
|
478
|
-
|
|
479
|
-
-
|
|
480
|
-
-
|
|
481
|
-
|
|
482
|
-
-
|
|
483
|
-
|
|
484
|
-
-
|
|
324
|
+
After the notification, treat every `EKEvent`, `EKReminder` and `EKCalendar`
|
|
325
|
+
you hold as possibly stale and fetch again. EventKit posts this notification
|
|
326
|
+
on the main actor. iOS 26 adds a typed message, `EKEventStore.EventStoreChanged`
|
|
327
|
+
(used as `.changed`), for the new Foundation notification API; use it only
|
|
328
|
+
behind an availability check. See the reference for both forms.
|
|
329
|
+
|
|
330
|
+
## Common mistakes
|
|
331
|
+
|
|
332
|
+
- **`requestAccess(to:)` on iOS 17 and later.** Deprecated from iOS 17; asking
|
|
333
|
+
for `.event` that way shows no prompt and fails with an error. It belongs in
|
|
334
|
+
the pre-17 branch only.
|
|
335
|
+
- **Saving into a read-only calendar.** The save throws. Check
|
|
336
|
+
`allowsContentModifications`, falling back to the default calendar.
|
|
337
|
+
- **No time zone on place-bound events.** Set `timeZone` explicitly, for
|
|
338
|
+
example `TimeZone(identifier: "Asia/Tokyo")`.
|
|
339
|
+
- **Batching without committing.** After saves with `commit: false`, call
|
|
340
|
+
`try store.commit()`, or nothing is stored.
|
|
341
|
+
- **Objects from two stores.** Fetching from one store and saving through
|
|
342
|
+
another is undefined behaviour.
|
|
343
|
+
|
|
344
|
+
## Review checklist
|
|
345
|
+
|
|
346
|
+
- [ ] Usage keys match what the app touches (calendars, reminders or both)
|
|
347
|
+
- [ ] iOS 17 granular requests used; `requestAccess(to:)` only as pre-17 fallback
|
|
348
|
+
- [ ] Write-only used only for direct creation, never for reads
|
|
349
|
+
- [ ] Status is read before any fetch or save
|
|
350
|
+
- [ ] Fetching events or reminders only happens with full access
|
|
351
|
+
- [ ] One `EKEventStore` reused throughout
|
|
352
|
+
- [ ] Target calendar checked for `allowsContentModifications`
|
|
353
|
+
- [ ] Recurring saves pass the intended `EKSpan`
|
|
354
|
+
- [ ] `commit()` follows every batch
|
|
355
|
+
- [ ] `EKEventStoreChanged` observed and data refetched
|
|
356
|
+
- [ ] iOS 26 typed `.changed` message only behind availability checks
|
|
357
|
+
- [ ] Explicit time zone on location-specific events
|
|
358
|
+
- [ ] No EventKit object crosses between stores
|
|
359
|
+
- [ ] EventKitUI delegates dismiss their controllers
|
|
485
360
|
|
|
486
361
|
## References
|
|
487
362
|
|
|
488
|
-
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
363
|
+
- [EventKit patterns](references/eventkit-patterns.md): SwiftUI manager and
|
|
364
|
+
view, predicates and enumeration, batches, calendar creation, location and
|
|
365
|
+
priority reminders, SwiftUI editor wrapper, typed change notifications
|
|
366
|
+
- Apple documentation:
|
|
367
|
+
[EventKit](https://developer.apple.com/documentation/eventkit),
|
|
368
|
+
[event store](https://developer.apple.com/documentation/eventkit/ekeventstore),
|
|
369
|
+
[event](https://developer.apple.com/documentation/eventkit/ekevent),
|
|
370
|
+
[reminder](https://developer.apple.com/documentation/eventkit/ekreminder),
|
|
371
|
+
[recurrence rule](https://developer.apple.com/documentation/eventkit/ekrecurrencerule),
|
|
372
|
+
[calendar](https://developer.apple.com/documentation/eventkit/ekcalendar),
|
|
373
|
+
[EventKitUI framework](https://developer.apple.com/documentation/eventkitui),
|
|
374
|
+
[event editor](https://developer.apple.com/documentation/eventkitui/ekeventeditviewcontroller),
|
|
375
|
+
[calendar chooser](https://developer.apple.com/documentation/eventkitui/ekcalendarchooser),
|
|
376
|
+
[article on store access](https://developer.apple.com/documentation/eventkit/accessing-the-event-store),
|
|
377
|
+
[article on repeating events](https://developer.apple.com/documentation/eventkit/creating-a-recurring-event)
|