From 3540c35cc2f0dd2b98ccf6bea1ff3050b674fd84 Mon Sep 17 00:00:00 2001
From: Eulices Lopez <105620565+eulicesl@users.noreply.github.com>
Date: Fri, 20 Mar 2026 07:25:46 -0400
Subject: [PATCH] fix(ios): tighten calendar scope and avoid headless contacts
prompts
---
.../ios/Sources/Calendar/CalendarService.swift | 18 +++++++++++++++++-
.../ios/Sources/Contacts/ContactsService.swift | 7 ++-----
apps/ios/Sources/Info.plist | 2 ++
apps/ios/project.yml | 1 +
4 files changed, 22 insertions(+), 6 deletions(-)
diff --git a/apps/ios/Sources/Calendar/CalendarService.swift b/apps/ios/Sources/Calendar/CalendarService.swift
index 01854345c86..e11a6ee4075 100644
--- a/apps/ios/Sources/Calendar/CalendarService.swift
+++ b/apps/ios/Sources/Calendar/CalendarService.swift
@@ -46,7 +46,7 @@ final class CalendarService: CalendarServicing {
let status = EKEventStore.authorizationStatus(for: .event)
let authorized: Bool
if status == .notDetermined {
- authorized = await Self.requestEventAccess(store: store)
+ authorized = await Self.requestWriteOnlyEventAccess(store: store)
} else {
authorized = EventKitAuthorization.allowsWrite(status: status)
}
@@ -121,6 +121,22 @@ final class CalendarService: CalendarServicing {
}
}
+ private static func requestWriteOnlyEventAccess(store: EKEventStore) async -> Bool {
+ if #available(iOS 17.0, *) {
+ return await withCheckedContinuation { continuation in
+ store.requestWriteOnlyAccessToEvents { granted, _ in
+ continuation.resume(returning: granted)
+ }
+ }
+ }
+
+ return await withCheckedContinuation { continuation in
+ store.requestAccess(to: .event) { granted, _ in
+ continuation.resume(returning: granted)
+ }
+ }
+ }
+
private static func resolveCalendar(
store: EKEventStore,
calendarId: String?,
diff --git a/apps/ios/Sources/Contacts/ContactsService.swift b/apps/ios/Sources/Contacts/ContactsService.swift
index 36e507c9a42..d38bc7a923c 100644
--- a/apps/ios/Sources/Contacts/ContactsService.swift
+++ b/apps/ios/Sources/Contacts/ContactsService.swift
@@ -103,11 +103,8 @@ final class ContactsService: ContactsServicing {
case .authorized, .limited:
return true
case .notDetermined:
- return await withCheckedContinuation { continuation in
- store.requestAccess(for: .contacts) { granted, _ in
- continuation.resume(returning: granted)
- }
- }
+ // Avoid prompting during node.invoke; headless/unattended flows should fail fast.
+ return false
case .restricted, .denied:
return false
@unknown default:
diff --git a/apps/ios/Sources/Info.plist b/apps/ios/Sources/Info.plist
index a3125becdcc..8950acf4b01 100644
--- a/apps/ios/Sources/Info.plist
+++ b/apps/ios/Sources/Info.plist
@@ -54,6 +54,8 @@
OpenClaw uses your calendars to show events and scheduling context when you enable calendar access.
NSCalendarsFullAccessUsageDescription
OpenClaw uses your calendars to show events and scheduling context when you enable calendar access.
+ NSCalendarsWriteOnlyAccessUsageDescription
+ OpenClaw uses your calendars to add events when you enable calendar access.
NSContactsUsageDescription
OpenClaw uses your contacts so you can search and reference people while using the assistant.
NSLocalNetworkUsageDescription
diff --git a/apps/ios/project.yml b/apps/ios/project.yml
index 67522e4e617..f345eac5baf 100644
--- a/apps/ios/project.yml
+++ b/apps/ios/project.yml
@@ -136,6 +136,7 @@ targets:
NSCameraUsageDescription: OpenClaw can capture photos or short video clips when requested via the gateway.
NSCalendarsUsageDescription: OpenClaw uses your calendars to show events and scheduling context when you enable calendar access.
NSCalendarsFullAccessUsageDescription: OpenClaw uses your calendars to show events and scheduling context when you enable calendar access.
+ NSCalendarsWriteOnlyAccessUsageDescription: OpenClaw uses your calendars to add events when you enable calendar access.
NSContactsUsageDescription: OpenClaw uses your contacts so you can search and reference people while using the assistant.
NSLocationWhenInUseUsageDescription: OpenClaw uses your location when you allow location sharing.
NSLocationAlwaysAndWhenInUseUsageDescription: OpenClaw can share your location in the background when you enable Always.