diff --git a/apps/ios/Sources/Calendar/CalendarService.swift b/apps/ios/Sources/Calendar/CalendarService.swift index 9ac83dd392..94b2d9ea3f 100644 --- a/apps/ios/Sources/Calendar/CalendarService.swift +++ b/apps/ios/Sources/Calendar/CalendarService.swift @@ -6,7 +6,7 @@ final class CalendarService: CalendarServicing { func events(params: OpenClawCalendarEventsParams) async throws -> OpenClawCalendarEventsPayload { let store = EKEventStore() let status = EKEventStore.authorizationStatus(for: .event) - let authorized = await Self.ensureAuthorization(store: store, status: status) + let authorized = EventKitAuthorization.allowsRead(status: status) guard authorized else { throw NSError(domain: "Calendar", code: 1, userInfo: [ NSLocalizedDescriptionKey: "CALENDAR_PERMISSION_REQUIRED: grant Calendar permission", @@ -39,7 +39,7 @@ final class CalendarService: CalendarServicing { func add(params: OpenClawCalendarAddParams) async throws -> OpenClawCalendarAddPayload { let store = EKEventStore() let status = EKEventStore.authorizationStatus(for: .event) - let authorized = await Self.ensureWriteAuthorization(store: store, status: status) + let authorized = EventKitAuthorization.allowsWrite(status: status) guard authorized else { throw NSError(domain: "Calendar", code: 2, userInfo: [ NSLocalizedDescriptionKey: "CALENDAR_PERMISSION_REQUIRED: grant Calendar permission", @@ -95,38 +95,6 @@ final class CalendarService: CalendarServicing { return OpenClawCalendarAddPayload(event: payload) } - private static func ensureAuthorization(store: EKEventStore, status: EKAuthorizationStatus) async -> Bool { - switch status { - case .authorized: - return true - case .notDetermined: - // Don’t prompt during node.invoke; prompts block the invoke and lead to timeouts. - return false - case .restricted, .denied: - return false - case .fullAccess: - return true - case .writeOnly: - return false - @unknown default: - return false - } - } - - private static func ensureWriteAuthorization(store: EKEventStore, status: EKAuthorizationStatus) async -> Bool { - switch status { - case .authorized, .fullAccess, .writeOnly: - return true - case .notDetermined: - // Don’t prompt during node.invoke; prompts block the invoke and lead to timeouts. - return false - case .restricted, .denied: - return false - @unknown default: - return false - } - } - private static func resolveCalendar( store: EKEventStore, calendarId: String?, diff --git a/apps/ios/Sources/EventKit/EventKitAuthorization.swift b/apps/ios/Sources/EventKit/EventKitAuthorization.swift new file mode 100644 index 0000000000..c27e9a3efd --- /dev/null +++ b/apps/ios/Sources/EventKit/EventKitAuthorization.swift @@ -0,0 +1,34 @@ +import EventKit + +enum EventKitAuthorization { + static func allowsRead(status: EKAuthorizationStatus) -> Bool { + switch status { + case .authorized, .fullAccess: + return true + case .writeOnly: + return false + case .notDetermined: + // Don’t prompt during node.invoke; prompts block the invoke and lead to timeouts. + return false + case .restricted, .denied: + return false + @unknown default: + return false + } + } + + static func allowsWrite(status: EKAuthorizationStatus) -> Bool { + switch status { + case .authorized, .fullAccess, .writeOnly: + return true + case .notDetermined: + // Don’t prompt during node.invoke; prompts block the invoke and lead to timeouts. + return false + case .restricted, .denied: + return false + @unknown default: + return false + } + } +} + diff --git a/apps/ios/Sources/Reminders/RemindersService.swift b/apps/ios/Sources/Reminders/RemindersService.swift index 36eea52217..249f439fb1 100644 --- a/apps/ios/Sources/Reminders/RemindersService.swift +++ b/apps/ios/Sources/Reminders/RemindersService.swift @@ -6,7 +6,7 @@ final class RemindersService: RemindersServicing { func list(params: OpenClawRemindersListParams) async throws -> OpenClawRemindersListPayload { let store = EKEventStore() let status = EKEventStore.authorizationStatus(for: .reminder) - let authorized = await Self.ensureAuthorization(store: store, status: status) + let authorized = EventKitAuthorization.allowsRead(status: status) guard authorized else { throw NSError(domain: "Reminders", code: 1, userInfo: [ NSLocalizedDescriptionKey: "REMINDERS_PERMISSION_REQUIRED: grant Reminders permission", @@ -50,7 +50,7 @@ final class RemindersService: RemindersServicing { func add(params: OpenClawRemindersAddParams) async throws -> OpenClawRemindersAddPayload { let store = EKEventStore() let status = EKEventStore.authorizationStatus(for: .reminder) - let authorized = await Self.ensureWriteAuthorization(store: store, status: status) + let authorized = EventKitAuthorization.allowsWrite(status: status) guard authorized else { throw NSError(domain: "Reminders", code: 2, userInfo: [ NSLocalizedDescriptionKey: "REMINDERS_PERMISSION_REQUIRED: grant Reminders permission", @@ -100,38 +100,6 @@ final class RemindersService: RemindersServicing { return OpenClawRemindersAddPayload(reminder: payload) } - private static func ensureAuthorization(store: EKEventStore, status: EKAuthorizationStatus) async -> Bool { - switch status { - case .authorized: - return true - case .notDetermined: - // Don’t prompt during node.invoke; prompts block the invoke and lead to timeouts. - return false - case .restricted, .denied: - return false - case .fullAccess: - return true - case .writeOnly: - return false - @unknown default: - return false - } - } - - private static func ensureWriteAuthorization(store: EKEventStore, status: EKAuthorizationStatus) async -> Bool { - switch status { - case .authorized, .fullAccess, .writeOnly: - return true - case .notDetermined: - // Don’t prompt during node.invoke; prompts block the invoke and lead to timeouts. - return false - case .restricted, .denied: - return false - @unknown default: - return false - } - } - private static func resolveList( store: EKEventStore, listId: String?,