One Logo Pool Ball
wwwwwwwwwwwwwwwwwww

Calendar

Read and write iOS calendar events and reminders

One.Calendar reads and writes events through EventKit. Give the app a purpose string before prebuild:

calendar: {
usage: 'Show and manage your events.',
remindersUsage: 'Show and manage your reminders.',
}

Expo-prebuilt apps pass the same calendar option to the VXRN Expo plugin. The event and reminders purpose strings are independent. Configure only the one an app uses. Prebuild sets NSCalendarsFullAccessUsageDescription and NSRemindersFullAccessUsageDescription for the strings supplied.

import { One } from 'one'
const status = await One.Calendar.requestPermission()
if (status === 'fullAccess') {
const startMs = Date.now() + 86_400_000
const identifier = await One.Calendar.create({
title: 'Team review',
startMs,
endMs: startMs + 3_600_000,
})
const events = await One.Calendar.list(startMs - 1, startMs + 3_600_001)
const event = events.find((item) => item.identifier === identifier)
if (event) {
const updated = await One.Calendar.update(event.identifier, event.startMs, {
title: 'Team review moved',
startMs: event.startMs + 3_600_000,
endMs: event.endMs + 3_600_000,
location: 'Room 4',
})
await One.Calendar.delete(updated.identifier, updated.startMs)
}
}

getPermissionStatus() reads the status without prompting. It returns notDetermined, restricted, denied, writeOnly, or fullAccess. requestPermission() requests full access and resolves to the resulting status, including when the person denies it. Reading events requires full access; write-only access does not permit reading even events the app created.

list(startMs, endMs, limit = 100) searches all visible calendars. Times are Unix milliseconds. The range must increase and span at most 366 days; the limit must be a whole number from 1 to 500. Events are sorted by start time and include their identifier, title, start and end times, all-day flag, and location. create({ title, startMs, endMs, allDay? }) saves to the default writable calendar and returns the identifier. delete(identifier, startMs) removes the occurrence at that exact start time. Pass the startMs returned by list for a recurring event, since its occurrences share an identifier. update(identifier, originalStartMs, changes) edits that occurrence and returns its new fields, including the identifier and start time to use for later edits or deletion. Pass one or more of title, startMs, endMs, allDay, or location; omitted fields keep their current values. Pass an empty location to clear it. Ask the person before creating, editing, or deleting an event.

To create a repeating event, pass a recurrence rule to create:

await One.Calendar.create({
title: 'Review notes',
startMs,
endMs: startMs + 3_600_000,
recurrence: { frequency: 'weekly', interval: 2, occurrenceCount: 8 },
})

The frequency is daily, weekly, monthly, or yearly; interval defaults to 1. Choose at most one end: occurrenceCount or endDateMs. Leave both out for an ongoing series. The interval must be a whole number from 1 to 1,000; the occurrence count must be a whole number from 1 to 10,000. list includes the rule in each occurrence’s optional recurrence field. update and delete change only the selected occurrence, not the whole series. Use the occurrence’s own startMs when changing it. Apple’s EventKit recurrence guide describes these rules.

Reminders

getRemindersPermissionStatus() reads reminders access without a prompt. requestRemindersPermission() requests full reminders access and resolves to the resulting status, including denial. It requires calendar.remindersUsage.

if (await One.Calendar.requestRemindersPermission() === 'fullAccess') {
const identifier = await One.Calendar.createReminder({
title: 'Send the report',
dueMs: Date.now() + 86_400_000,
})
const reminders = await One.Calendar.listReminders()
await One.Calendar.setReminderCompleted(identifier, true)
const history = await One.Calendar.listReminders(100, true)
await One.Calendar.deleteReminder(identifier)
}

listReminders(limit = 100, includeCompleted = false) reads incomplete reminders from visible lists. Pass true to include completed reminders. Results are ordered by due date with undated reminders last. The limit is a whole number from 1 to 500. Each item has an identifier, title, completion flag, and optional due time in Unix milliseconds. createReminder saves to the default writable reminders list. Ask the person before changing or deleting reminders.

createReminder accepts the same optional recurrence rule as events when dueMs is supplied. A listed reminder includes its rule in recurrence. EventKit exposes only the first incomplete reminder in a repeating set, so completing it makes the next due reminder available. Use listReminders again after completion to read the new occurrence.

Missing reminders purpose text rejects with E_REMINDERS_MANIFEST; missing full access with E_REMINDERS_PERMISSION; invalid input with E_REMINDERS_INPUT. A missing default list gives E_REMINDERS_UNAVAILABLE. Writes use E_REMINDERS_SAVE or E_REMINDERS_DELETE, and an unknown identifier uses E_REMINDERS_NOT_FOUND.

Missing purpose text rejects with E_CALENDAR_MANIFEST; missing full access with E_CALENDAR_PERMISSION; invalid input with E_CALENDAR_INPUT. An absent writable calendar gives E_CALENDAR_UNAVAILABLE. Store failures use E_CALENDAR_SAVE or E_CALENDAR_DELETE; editing or deleting an unknown occurrence gives E_CALENDAR_NOT_FOUND. Calendar events are implemented on iOS and Android. On Android, events run over CalendarContract with native.app.calendar.usage; single-occurrence edits become exceptions that keep sibling occurrences. Reminders stay unavailable on Android and web: denied reminder permissions and empty lists. Calls that create or update a reminder reject with Calendar.<verb> needs an iOS or Android build; reminder completion and deletion do nothing.

See Apple’s EventKit access guide for the full and write-only permission modes.

Edit this page on GitHub.