device reference
Every method of the device global, with its arguments, return value, errors, where it works, and an example. The Device API guide explains where device exists and how to call it from a server kernel or a browser.
IMPORTANT
macOS and iOS only, for now. The native side is the Swift DeviceBridge in Ganymede's Mac, iPad and iPhone apps (macOS 26 and iOS 26). There is no host for Windows, Linux or Android. A notebook page in any browser can answer a few methods that need no permission with web APIs (In a browser); everything else needs one of the apps.
device is undefined on kernels the app doesn't host, so a cell checks for it first (device?.clipboard.readText(), or if (device) …). The examples skip that check, and the SoundPlayer examples use a player from device.sound.player, and the Peripheral examples a peripheral from device.bluetooth.connect.
For editor types, add this line once to a cell or a project file:
/// <reference types="@tlockney/ganymede-device/globals" />Every method takes a last options argument with a signal (CallOptions), and fails with a DeviceError. Availability says where each method works: in the app (a kernel the Mac, iPad or iPhone app runs itself), the system permission it needs there, from a remote kernel in the app, and from a kernel driven from a browser. "planned" means it doesn't yet but will; "no" means the platform has no such API.
| Method | What it does |
|---|---|
device.info(options?) | Reads what the host is and the device's current state. |
device.has(method) | Whether the host answers a method. |
device.on(event, handler, options?) | Calls handler each time the device changes in the way event names, until it is removed: by calling the function on answers, by aborting options.signal, or by a kernel restart. |
device.openURL(url, options?) | Opens a link: in the browser, or the mail app for mailto. |
device.share(options) | Shows the system share sheet (NSSharingServicePicker on the Mac). |
device.preview(files, options?) | Shows files in Quick Look. |
device.output(result, options?) | Hands result back to whatever started this run: the share sheet's host app, or the Shortcut's next action. |
device.clipboard.readText(options?) | Reads the clipboard's text. |
device.clipboard.writeText(text, options?) | Replaces the clipboard's contents with text. |
device.clipboard.readImage(options?) | Reads the clipboard's image as PNG bytes, whatever format was copied. |
device.clipboard.writeImage(image, options?) | Replaces the clipboard's contents with an image. |
device.haptics.impact(style?, options?) | Plays a tap. |
device.haptics.notification(type, options?) | Plays the system's success, warning or error pattern. |
device.haptics.selection(options?) | Plays the tick of a picker moving to a new value. |
device.ui.alert(message, options?) | Shows a message with an OK button. |
device.ui.confirm(message, options?) | Asks a yes-or-no question. |
device.ui.prompt(message, options?) | Asks for a line of text. |
device.ui.actionSheet(title, options, sheetOptions?) | Asks the person to choose one of several options. |
device.ui.pickDate(options?) | Asks for a date, a time, or both. |
device.files.pick(options?) | Shows the open panel on the Mac, or the document picker on iPad and iPhone, and reads the files the person picks. |
device.files.export(data, options) | Asks where to save data, and saves it there: the save panel on the Mac, the document picker on iPad and iPhone. |
device.photos.authorizationStatus(options?) | Reads the permission's status, without asking. |
device.photos.isAuthorized(options?) | Whether the permission is granted, without asking. |
device.photos.requestAuthorization(options?) | Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. |
device.photos.pick(options?) | Shows the system photo picker and reads the photos the person picks. |
device.photos.save(image, options?) | Adds an image to the photo library, as a new photo. |
device.sound.play(data, options?) | Starts playing a sound, which plays to its end on its own. |
device.sound.player(data, options?) | Loads a sound into a player, paused at the start, for a cell to play, pause, seek and close. |
device.notifications.authorizationStatus(options?) | Reads the permission's status, without asking. |
device.notifications.isAuthorized(options?) | Whether the permission is granted, without asking. |
device.notifications.requestAuthorization(options?) | Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. |
device.notifications.schedule(body, options) | Schedules a notification to show after a delay, even if the notebook or the app has closed by then. |
device.notifications.pending(options?) | Lists this notebook's notifications that haven't been delivered yet. |
device.notifications.cancel(id, options?) | Takes one of this notebook's notifications off the schedule. |
device.notifications.cancelAll(options?) | Takes all of this notebook's notifications off the schedule. |
device.location.authorizationStatus(options?) | Reads the permission's status, without asking. |
device.location.isAuthorized(options?) | Whether the permission is granted, without asking. |
device.location.requestAuthorization(options?) | Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. |
device.location.current(options?) | One fix, at the accuracy asked for. |
device.location.watch(options?) | Fixes as the device moves, for for await, until the loop ends or the cell is interrupted. |
device.location.geocode(address, options?) | The places an address may be. |
device.location.reverseGeocode(position, options?) | What is at a position, such as a fix from current(). |
device.motion.authorizationStatus(options?) | Reads the permission's status, without asking. |
device.motion.isAuthorized(options?) | Whether the permission is granted, without asking. |
device.motion.requestAuthorization(options?) | Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. |
device.motion.watch(options?) | Samples every interval seconds, for for await, until the loop ends or the cell is interrupted. |
device.motion.latest(options?) | The newest sample. |
device.contacts.authorizationStatus(options?) | Reads the permission's status, without asking. |
device.contacts.isAuthorized(options?) | Whether the permission is granted, without asking. |
device.contacts.requestAuthorization(options?) | Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. |
device.contacts.search(query, options?) | Contacts whose name matches query; or whose email matches, when query is a whole email address (a part of one, such as "@example.com", matches nothing); or whose phone number matches, when it is a phone number. |
device.contacts.get(id, options?) | One contact, by the id search, all or add gave. |
device.contacts.all(options?) | Every contact the app may see, in the person's sort order. |
device.contacts.add(contact, options?) | Adds a contact to the default account. |
device.contacts.delete(id, options?) | Deletes a contact. |
device.camera.authorizationStatus(options?) | Reads the permission's status, without asking. |
device.camera.isAuthorized(options?) | Whether the permission is granted, without asking. |
device.camera.requestAuthorization(options?) | Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. |
device.camera.capture(options?) | Shows the system camera and waits for the person to take a photo. |
device.reminders.authorizationStatus(options?) | Reads the permission's status, without asking. |
device.reminders.isAuthorized(options?) | Whether the permission is granted, without asking. |
device.reminders.requestAuthorization(options?) | Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. |
device.reminders.calendars(options?) | The reminder lists. |
device.reminders.list(options?) | Reminders, from one list or all of them. |
device.reminders.add(reminder, options?) | Adds a reminder. |
device.reminders.complete(id, options?) | Marks a reminder done. |
device.reminders.uncomplete(id, options?) | Marks a reminder not done. |
device.reminders.delete(id, options?) | Deletes a reminder. |
device.bluetooth.authorizationStatus(options?) | Reads the permission's status, without asking. |
device.bluetooth.isAuthorized(options?) | Whether the permission is granted, without asking. |
device.bluetooth.requestAuthorization(options?) | Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. |
device.bluetooth.scan(options?) | Advertisements from nearby peripherals, for for await, until the loop ends or the cell is interrupted. |
device.bluetooth.known(ids, options?) | Peripherals the system knows by id, such as one a past scan found, to connect again without scanning. |
device.bluetooth.connect(id, options?) | Connects to a peripheral. |
device.folders.pick(options?) | Shows the system folder picker and keeps what the person picks for this notebook. |
device.folders.list(options?) | The folders this notebook was given, in the order they were picked. |
device.widget.update(kind, content, options?) | Publishes content for this notebook's widget kind, in place of what it showed, and asks the system to redraw it. |
device.shortcuts.authorizationStatus(options?) | Reads the permission's status, without asking. |
device.shortcuts.isAuthorized(options?) | Whether the permission is granted, without asking. |
device.shortcuts.requestAuthorization(options?) | Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. |
device.shortcuts.run(name, options?) | Runs a Shortcut by name and waits for it to finish. |
device.secrets.authorizationStatus(options?) | Reads the permission's status, without asking. |
device.secrets.isAuthorized(options?) | Whether the permission is granted, without asking. |
device.secrets.requestAuthorization(options?) | Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. |
device.secrets.get(name, options?) | Reads a secret. |
device.secrets.set(name, value, options?) | Saves a secret, in place of one by the same name. |
device.secrets.delete(name, options?) | Deletes a secret. |
device.secrets.list(options?) | The names of this notebook's secrets, never their values. |
player.play(options?) | Plays from the current time. |
player.pause(options?) | Pauses at the current time. |
player.stop(options?) | Pauses and goes back to the start. |
player.seek(seconds, options?) | Moves to a time, playing or not. |
player.setVolume(volume, options?) | Sets the volume. |
player.state(options?) | Reads the player's state, changing nothing. |
player.close() | Stops the player and frees it; later calls fail failed. |
peripheral.services(options?) | Its services and their characteristics, discovered on the first call. |
peripheral.read(service, characteristic, options?) | Reads a characteristic's value. |
peripheral.write(service, characteristic, data, options?) | Writes a characteristic's value. |
peripheral.notify(service, characteristic, options?) | A characteristic's values as it notifies or indicates them, for for await, until the loop ends, the cell is interrupted, or the peripheral closes. |
peripheral.rssi(options?) | Reads the signal strength now. |
folder.entries(path?, options?) | Lists a directory in the folder, sorted by name. |
folder.read(path, options?) | Reads a file in the folder. |
folder.write(path, data, options?) | Writes a file in the folder, replacing what is there and making the directories on the way. |
folder.remove(path, options?) | Removes a file, a symlink (not what it points to), or an empty directory, in the folder. |
device
The device global: native features of the Mac, iPad and iPhone apps. Undefined on kernels the app doesn't host, so check for it first.
device.info(options?)
info(options?: CallOptions): Promise<DeviceInfo>;Reads what the host is and the device's current state.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<DeviceInfo>: The host's answer to hello as it was when the kernel started, with the current screen, battery, appearance and locale where the host reads them.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | yes | yes |
A browser has no model or battery.
const info = await device.info();
console.log(info.platform, info.osVersion, info.battery?.level);device.has(method)
has(method: string): boolean;Whether the host answers a method. Synchronous: it reads what the host said when the kernel started, so it can't be cancelled.
| Parameter | Type | Required | Description |
|---|---|---|---|
method | string | yes | A method's name as SUPPORT lists it, such as "clipboard.readText" or "openURL". |
Returns boolean: true when the host answers it; calling a method it doesn't fails unavailable.
if (device.has("haptics.impact")) await device.haptics.impact();
else console.log("no haptics on", (await device.info()).platform);device.on(event, handler, options?)
on<E extends HostEvent>(event: E, handler: (value: HostEvents[E]) => void, options?: OnOptions): () => void;Calls handler each time the device changes in the way event names, until it is removed: by calling the function on answers, by aborting options.signal, or by a kernel restart. Unlike other calls, an interrupt leaves it running, since it outlives the cell that added it. A handler that runs after its cell finished writes its output where the kernel sends output with no running cell, so keep handlers to updating state or a display by id. No permission; in the app only. When the host can't send events, the handler is removed and one line on stderr says why; has("on") tells beforehand.
| Parameter | Type | Required | Description |
|---|---|---|---|
event | E | yes | What to watch: appearance, foreground, background, battery or locale. |
handler | (value: HostEvents[E]) => void | yes | Called with the event's value (HostEvents). An error it throws goes to stderr and doesn't remove it. |
options | OnOptions | no | The signal that removes the handler. |
Returns () => void: A function that removes the handler.
Throws DeviceError: failed when event isn't one of the five or handler isn't a function.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | no | no |
Appearance, foreground, background, battery and locale changes; an interrupt doesn't remove a handler.
const off = device.on("appearance", (mode) => console.log("now", mode));
// Later, in any cell:
off();device.openURL(url, options?)
openURL(url: string | URL, options?: OpenURLOptions): Promise<void>;Opens a link: in the browser, or the mail app for mailto.
| Parameter | Type | Required | Description |
|---|---|---|---|
url | string | URL | yes | An http, https or mailto URL. |
options | OpenURLOptions | no | inApp shows a web page in the app on iPhone and iPad, and the call's signal. Cancel rejects cancelled; the URL may already have opened. |
Throws DeviceError: failed for any other scheme.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | yes | after a click |
http, https and mailto only. inApp works on iPhone and iPad, not the Mac; a browser opens a tab after a click, and ignores inApp.
await device.openURL("https://ganymede-server.com/docs/", { inApp: true });device.share(options)
share(options: ShareOptions): Promise<boolean>;Shows the system share sheet (NSSharingServicePicker on the Mac). The sheet is its own consent, so no permission.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | ShareOptions | yes | text, url and files to share (at least one; files together at most 16 MiB), and the call's signal, which closes the sheet and rejects cancelled. |
Returns Promise<boolean>: true when the person picked where to send them (on iPhone and iPad once the share completes; on the Mac as soon as they pick a service), false when they dismissed it.
Throws DeviceError: failed when there is nothing to share, a file has no plain name, or the files are over 16 MiB.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | yes | after a click |
The share sheet; NSSharingServicePicker on the Mac. A browser uses the Web Share API, after a click, and shares files only where it can.
const csv = "city,temp\nOslo,4\nLima,19\n";
await device.share({
text: "Today's temperatures",
files: [{ name: "temps.csv", data: new TextEncoder().encode(csv) }],
});device.preview(files, options?)
preview(files: NamedBytes[], options?: CallOptions): Promise<void>;Shows files in Quick Look. Quick Look picks the viewer from each name's extension, so name a PNG *.png.
| Parameter | Type | Required | Description |
|---|---|---|---|
files | NamedBytes[] | yes | The files, together at most 16 MiB. |
options | CallOptions | no | The call's signal, which closes Quick Look and rejects cancelled. |
Returns once the person closes it.
Throws DeviceError: failed when files is empty, a file has no plain name, or the files are over 16 MiB.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | yes | no |
Quick Look, which picks the viewer from each file name's extension. A browser has no Quick Look.
const png = await device.clipboard.readImage();
if (png) await device.preview([{ name: "clipboard.png", data: png }]);device.output(result, options?)
output(result: RunOutput, options?: CallOptions): Promise<void>;Hands result back to whatever started this run: the share sheet's host app, or the Shortcut's next action. A later call replaces it. In a notebook there is nothing to hand it to.
| Parameter | Type | Required | Description |
|---|---|---|---|
result | RunOutput | yes | text, url and files (together at most 16 MiB). |
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Throws DeviceError: unavailable in a notebook; failed when result has nothing in it or a file has no plain name.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| no | none | no | no |
Only in a run the share sheet, Shortcuts or a widget started, which also has device.input; a notebook has nothing to hand a result to.
const page = device.input?.url;
if (page) await device.output({ text: `Summary of ${page}: ...` });Properties
| Property | Type | Description |
|---|---|---|
clipboard | Clipboard | The system clipboard: see Clipboard. |
haptics | Haptics | Taptic feedback, on iPhone: see Haptics. |
ui | UI | Dialogs over the notebook: see UI. |
files | Files | The system's file pickers: see Files. |
photos | Photos | The system photo picker: see Photos. |
sound | Sound | Sound playback: see Sound. |
notifications | Notifications | Local notifications after a delay: see Notifications. |
location | DeviceLocation | Where the device is, and geocoding: see DeviceLocation. |
motion | DeviceMotion | The motion sensors, on iPad and iPhone: see DeviceMotion. |
contacts | Contacts | The person's contacts: see Contacts. |
camera | Camera | The system camera: see Camera. |
reminders | Reminders | The person's reminders: see Reminders. |
bluetooth | Bluetooth | Bluetooth LE peripherals: see Bluetooth. |
folders | Folders | Folders the person picked for this notebook: see Folders. |
widget | Widget | Widgets that show what a run published: see Widget. |
input | RunInput | null | What this run was given, when the share sheet, Shortcuts or a widget started it; null in a notebook. |
secrets | Secrets | Strings this notebook saved in the keychain: see Secrets. |
shortcuts | Shortcuts | The person's Shortcuts, on iPad, iPhone and Mac: see Shortcuts. |
device.clipboard
The system clipboard. iPadOS may show its paste prompt on a read.
device.clipboard.readText(options?)
readText(options?: CallOptions): Promise<string | null>;Reads the clipboard's text.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<string | null>: The text, or null when the clipboard holds none.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | yes | after a click |
iPadOS shows its paste prompt; a browser needs a click for the Clipboard API.
const text = await device.clipboard.readText();
if (text !== null) console.log(text.split(/\s+/).length, "words");device.clipboard.writeText(text, options?)
writeText(text: string, options?: CallOptions): Promise<void>;Replaces the clipboard's contents with text.
| Parameter | Type | Required | Description |
|---|---|---|---|
text | string | yes | The text to copy. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled; the write may already have happened. |
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | yes | after a click |
A browser needs a click for the Clipboard API.
await device.clipboard.writeText(JSON.stringify({ rows: 3 }, null, 2));device.clipboard.readImage(options?)
readImage(options?: CallOptions): Promise<Uint8Array | null>;Reads the clipboard's image as PNG bytes, whatever format was copied.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<Uint8Array | null>: PNG bytes, or null when the clipboard holds no image.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | yes | after a click |
PNG. iPadOS shows its paste prompt; a browser needs a click for the Clipboard API.
const png = await device.clipboard.readImage();
if (png) console.log(`${png.byteLength} bytes`);device.clipboard.writeImage(image, options?)
writeImage(image: Bytes, options?: CallOptions): Promise<void>;Replaces the clipboard's contents with an image.
| Parameter | Type | Required | Description |
|---|---|---|---|
image | Bytes | yes | PNG, JPEG, GIF, HEIC or TIFF bytes, at most 16 MiB. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled; the write may already have happened. |
Throws DeviceError: failed when the bytes aren't an image the device reads.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | yes | after a click |
A browser needs a click for the Clipboard API, and takes PNG only.
const [photo] = await device.photos.pick();
if (photo) await device.clipboard.writeImage(photo);device.haptics
Taptic feedback. iPhone only: iPads and Macs have no Taptic Engine, so there device.has("haptics.impact") is false and calls fail unavailable.
device.haptics.impact(style?, options?)
impact(style?: ImpactStyle, options?: CallOptions): Promise<void>;Plays a tap.
| Parameter | Type | Required | Description |
|---|---|---|---|
style | ImpactStyle | no | How strong a tap: "light", "medium" (the default), "heavy", "soft" or "rigid". |
options | CallOptions | no | The call's signal. Cancel rejects cancelled; the tap may already have played. |
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| iPhone | none | yes | no |
iPhone only: iPads and Macs have no Taptic Engine. A browser has no haptics API Safari supports.
if (device.has("haptics.impact")) await device.haptics.impact("heavy");device.haptics.notification(type, options?)
notification(type: NotificationFeedback, options?: CallOptions): Promise<void>;Plays the system's success, warning or error pattern.
| Parameter | Type | Required | Description |
|---|---|---|---|
type | NotificationFeedback | yes | "success", "warning" or "error". |
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| iPhone | none | yes | no |
iPhone only: iPads and Macs have no Taptic Engine.
await device.haptics.notification("success");device.haptics.selection(options?)
selection(options?: CallOptions): Promise<void>;Plays the tick of a picker moving to a new value.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| iPhone | none | yes | no |
iPhone only: iPads and Macs have no Taptic Engine.
for (let i = 0; i < 3; i++) {
await device.haptics.selection();
await new Promise((done) => setTimeout(done, 200));
}device.ui
Dialogs the app shows over the notebook: sheets on the Mac, alerts on iPhone and iPad. One shows at a time; a second call waits for the first to close. Dismissing one is not an error: it returns null or false. An interrupt or an aborted signal closes it and rejects cancelled (timeout for AbortSignal.timeout()).
device.ui.alert(message, options?)
alert(message: string, options?: AlertOptions): Promise<void>;Shows a message with an OK button.
| Parameter | Type | Required | Description |
|---|---|---|---|
message | string | yes | The message. |
options | AlertOptions | no | title is a heading above it; signal cancels: it closes the alert and rejects cancelled. |
Returns once the person closes it.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | yes | planned |
A sheet on the Mac, an alert on iPhone and iPad; a browser will show it in the cell's output.
await device.ui.alert("The import finished: 1,204 rows.", { title: "Done" });device.ui.confirm(message, options?)
confirm(message: string, options?: ConfirmOptions): Promise<boolean>;Asks a yes-or-no question.
| Parameter | Type | Required | Description |
|---|---|---|---|
message | string | yes | The question. |
options | ConfirmOptions | no | title, okLabel for the confirming button, destructive to show it red, and the call's signal. |
Returns Promise<boolean>: true when the person confirms, false when they cancel or dismiss it.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | yes | planned |
if (await device.ui.confirm("Delete the scratch files?", { destructive: true, okLabel: "Delete" })) {
console.log("deleting");
}device.ui.prompt(message, options?)
prompt(message: string, options?: PromptOptions): Promise<string | null>;Asks for a line of text.
| Parameter | Type | Required | Description |
|---|---|---|---|
message | string | yes | The question. |
options | PromptOptions | no | title, placeholder, defaultValue, secure to hide what is typed, and the call's signal. |
Returns Promise<string | null>: The text entered, or null when the person cancels.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | yes | planned |
const name = await device.ui.prompt("Name the run", { defaultValue: "baseline" });
if (name !== null) console.log(`saving ${name}`);device.ui.actionSheet(title, options, sheetOptions?)
actionSheet(title: string, options: string[], sheetOptions?: ActionSheetOptions): Promise<number | null>;Asks the person to choose one of several options.
| Parameter | Type | Required | Description |
|---|---|---|---|
title | string | yes | The sheet's heading. |
options | string[] | yes | The choices' labels, at least one. |
sheetOptions | ActionSheetOptions | no | message under the title, cancel for the cancel button's label, destructive for the index of an option to show red, and the call's signal. |
Returns Promise<number | null>: The index of the option chosen, or null when the person cancels.
Throws DeviceError: failed when options is empty.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | yes | planned |
const formats = ["CSV", "JSON", "Parquet"];
const i = await device.ui.actionSheet("Export as", formats);
if (i !== null) console.log(`exporting ${formats[i]}`);device.ui.pickDate(options?)
pickDate(options?: PickDateOptions): Promise<Date | null>;Asks for a date, a time, or both.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | PickDateOptions | no | mode ("date", "time" or "datetime", the default), title, value to start at, min and max, and the call's signal. |
Returns Promise<Date | null>: The date chosen, or null when the person cancels.
Throws DeviceError: failed when value, min or max isn't a valid Date.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | yes | planned |
const start = await device.ui.pickDate({ mode: "date", title: "Report from", max: new Date() });
if (start) console.log(start.toISOString().slice(0, 10));device.files
Files in and out through the system's own pickers, which are the person's consent, so no permission. A picked file is copied in by value: the notebook keeps no access to where it came from.
device.files.pick(options?)
pick(options?: PickFilesOptions): Promise<File[]>;Shows the open panel on the Mac, or the document picker on iPad and iPhone, and reads the files the person picks. Together they are at most 16 MiB.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | PickFilesOptions | no | types limits what can be picked, multiple allows more than one, and signal cancels: it closes the picker and rejects cancelled. |
Returns Promise<File[]>: The picked files, with their names and MIME types, or [] when the person cancels.
Throws DeviceError: failed when types or multiple has the wrong type, or the files together are over 16 MiB.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | yes | after a click |
The open panel on the Mac, the document picker on iPad and iPhone. Files are copied in; no access to where they came from is kept. A browser can't filter by a type identifier such as public.json, so with one it allows any file.
const [file] = await device.files.pick({ types: [".csv", "text/csv"] });
if (file) {
const rows = (await file.text()).trim().split("\n");
console.log(file.name, rows.length, "rows");
}device.files.export(data, options)
export(data: Bytes, options: ExportFileOptions): Promise<boolean>;Asks where to save data, and saves it there: the save panel on the Mac, the document picker on iPad and iPhone.
| Parameter | Type | Required | Description |
|---|---|---|---|
data | Bytes | yes | The file's contents, at most 16 MiB. |
options | ExportFileOptions | yes | name is the file name the panel suggests; signal cancels: it closes the panel and rejects cancelled. |
Returns Promise<boolean>: true when saved, false when the person cancels.
Throws DeviceError: failed when name isn't a plain file name, or data is over 16 MiB.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | yes | after a click |
The save panel on the Mac, the document picker on iPad and iPhone. A browser downloads it after a click, and answers true then, since it can't tell whether it was saved.
const csv = "city,temp\nOslo,4\nLima,19\n";
const saved = await device.files.export(new TextEncoder().encode(csv), { name: "temps.csv" });
console.log(saved ? "saved" : "cancelled");device.photos
The photo library: the system photo picker to read photos, which needs no permission, and save to add one, which needs add-only access. The permission methods are about that add-only access: the first save asks (the app's prompt for this notebook, then the system's), and a refusal rejects denied. Granted, authorizationStatus() reads writeOnly: the app may add photos, never read the library.
The permission methods (authorizationStatus, isAuthorized, requestAuthorization) are about save's add-only access, and work where save does: in the app, not from a remote kernel or a browser.
device.photos.authorizationStatus(options?)
authorizationStatus(options?: CallOptions): Promise<AuthorizationStatus>;Reads the permission's status, without asking.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<AuthorizationStatus>: "notDetermined" until the person is asked, then a grant ("authorized", or a partial one) or "denied" or "restricted".
console.log(await device.photos.authorizationStatus());device.photos.isAuthorized(options?)
isAuthorized(options?: CallOptions): Promise<boolean>;Whether the permission is granted, without asking.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<boolean>: true when the status is a grant, partial ones included.
if (!(await device.photos.isAuthorized())) console.log("not allowed yet");device.photos.requestAuthorization(options?)
requestAuthorization(options?: CallOptions): Promise<AuthorizationStatus>;Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. The namespace's methods ask on their own, so calling this first is optional. Cancel rejects cancelled; a system prompt already up stays, and its answer still counts for later calls.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. |
Returns Promise<AuthorizationStatus>: The status after the prompt, or as it was when the person had already been asked.
const status = await device.photos.requestAuthorization();
if (status === "denied") console.log("allow it in Settings to go on");device.photos.pick(options?)
pick(options?: PickPhotosOptions): Promise<File[]>;Shows the system photo picker and reads the photos the person picks. The picker shows the library without the app being let in, so no permission. Together the photos are at most 16 MiB.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | PickPhotosOptions | no | limit is how many may be picked (1 by default, 0 for no limit); signal cancels: it closes the picker and rejects cancelled. |
Returns Promise<File[]>: The picked photos as image Files in their stored format (often HEIC or JPEG), or [] when the person cancels.
Throws DeviceError: failed when limit isn't a whole number of 0 or more, or the photos together are over 16 MiB.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | yes | after a click |
The system photo picker, which needs no photo library permission. A browser shows a file picker for images, and keeps the first limit picked.
const photos = await device.photos.pick({ limit: 3 });
for (const photo of photos) console.log(photo.name, photo.type, photo.size);device.photos.save(image, options?)
save(image: Bytes, options?: CallOptions): Promise<void>;Adds an image to the photo library, as a new photo.
| Parameter | Type | Required | Description |
|---|---|---|---|
image | Bytes | yes | PNG, JPEG, GIF, HEIC or TIFF bytes, or a Blob of them, at most 16 MiB. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled; the photo may already have been added. |
Throws DeviceError: denied when the notebook or the system may not add photos; failed when the bytes aren't PNG, JPEG, GIF, HEIC or TIFF, or the library refuses them.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | photos | no | no |
Add-only access: the app may add photos, never read the library.
const canvas = new OffscreenCanvas(256, 256);
const g = canvas.getContext("2d")!;
g.fillStyle = "teal";
g.fillRect(0, 0, 256, 256);
await device.photos.save(await canvas.convertToBlob({ type: "image/png" }));device.sound
Sound from bytes in any format the device reads (WAV, AIFF, MP3, AAC/M4A, CAF, FLAC), at most 16 MiB. No permission. On iPhone the ring/silent switch can silence it.
device.sound.play(data, options?)
play(data: Bytes, options?: PlaySoundOptions): Promise<void>;Starts playing a sound, which plays to its end on its own.
| Parameter | Type | Required | Description |
|---|---|---|---|
data | Bytes | yes | The sound's bytes, at most 16 MiB. |
options | PlaySoundOptions | no | volume from 0 to 1 (1 by default), and the call's signal. Cancel rejects cancelled; the sound may already be playing. |
Returns once the sound has started.
Throws DeviceError: failed when data is empty, over 16 MiB, or not a sound the device reads.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | yes | after a click |
A browser asks for a click only until the page has had one.
const [file] = await device.files.pick({ types: ["audio/*"] });
if (file) await device.sound.play(file, { volume: 0.5 });device.sound.player(data, options?)
player(data: Bytes, options?: CallOptions): Promise<SoundPlayer>;Loads a sound into a player, paused at the start, for a cell to play, pause, seek and close. A kernel has at most 32 open at once.
| Parameter | Type | Required | Description |
|---|---|---|---|
data | Bytes | yes | The sound's bytes, at most 16 MiB. |
options | CallOptions | no | The call's signal. |
Returns Promise<SoundPlayer>: A SoundPlayer.
Throws DeviceError: failed when data is empty, over 16 MiB, or not a sound the device reads, or the kernel has 32 players open.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | no | no |
Play, pause, stop, seek and volume; close it, or hold it with await using. Remote kernels and browsers: a handle wouldn't survive a disconnect.
const [file] = await device.files.pick({ types: ["audio/*"] });
if (file) {
await using player = await device.sound.player(file);
await player.play();
// Leaving the block closes the player, which stops it: wait for the end.
await new Promise((done) => setTimeout(done, player.duration * 1000));
}device.notifications
Local notifications after a delay: the app posts them even when the notebook or the app is closed. Delay only, no calendar times or repeats. Each notification belongs to the notebook that scheduled it: pending(), cancel() and cancelAll() see only that notebook's.
Needs the notifications permission, for schedule(): the first call asks (the app's prompt for this notebook, then the system's) and a refusal rejects denied. authorizationStatus(), isAuthorized() and requestAuthorization() read and ask for it.
The permission methods (authorizationStatus, isAuthorized, requestAuthorization) work where the rest of the namespace does: in the app, not from a remote kernel or a browser.
device.notifications.authorizationStatus(options?)
authorizationStatus(options?: CallOptions): Promise<AuthorizationStatus>;Reads the permission's status, without asking.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<AuthorizationStatus>: "notDetermined" until the person is asked, then a grant ("authorized", or a partial one) or "denied" or "restricted".
console.log(await device.notifications.authorizationStatus());device.notifications.isAuthorized(options?)
isAuthorized(options?: CallOptions): Promise<boolean>;Whether the permission is granted, without asking.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<boolean>: true when the status is a grant, partial ones included.
if (!(await device.notifications.isAuthorized())) console.log("not allowed yet");device.notifications.requestAuthorization(options?)
requestAuthorization(options?: CallOptions): Promise<AuthorizationStatus>;Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. The namespace's methods ask on their own, so calling this first is optional. Cancel rejects cancelled; a system prompt already up stays, and its answer still counts for later calls.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. |
Returns Promise<AuthorizationStatus>: The status after the prompt, or as it was when the person had already been asked.
const status = await device.notifications.requestAuthorization();
if (status === "denied") console.log("allow it in Settings to go on");device.notifications.schedule(body, options)
schedule(body: string, options: ScheduleNotificationOptions): Promise<string>;Schedules a notification to show after a delay, even if the notebook or the app has closed by then. iOS and iPadOS keep at most 64 per app, dropping the soonest beyond that.
| Parameter | Type | Required | Description |
|---|---|---|---|
body | string | yes | The text it shows. |
options | ScheduleNotificationOptions | yes | delay in seconds (more than 0), an optional title, and the call's signal. Cancel rejects cancelled; it may already be scheduled. |
Returns Promise<string>: Its id, for cancel().
Throws DeviceError: denied when the notifications permission is refused; failed when body is empty, delay isn't a finite number above 0, or title isn't a string.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | notifications | no | no |
After delay seconds, even when the notebook is closed. iOS and iPadOS keep at most 64 per app.
const id = await device.notifications.schedule("The training run should be done.", {
title: "Check the notebook",
delay: 30 * 60,
});device.notifications.pending(options?)
pending(options?: CallOptions): Promise<PendingNotification[]>;Lists this notebook's notifications that haven't been delivered yet.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<PendingNotification[]>: This notebook's pending notifications; other notebooks' aren't listed.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | no | no |
Only the calling notebook's.
for (const n of await device.notifications.pending()) {
console.log(n.id, n.body, n.date?.toLocaleTimeString());
}device.notifications.cancel(id, options?)
cancel(id: string, options?: CallOptions): Promise<void>;Takes one of this notebook's notifications off the schedule. An id that's gone, or another notebook's, changes nothing.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | yes | What schedule() returned. |
options | CallOptions | no | The call's signal. |
Throws DeviceError: failed when id is empty.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | no | no |
Only the calling notebook's.
const id = await device.notifications.schedule("Stretch", { delay: 3600 });
await device.notifications.cancel(id);device.notifications.cancelAll(options?)
cancelAll(options?: CallOptions): Promise<void>;Takes all of this notebook's notifications off the schedule.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. |
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | no | no |
Only the calling notebook's.
await device.notifications.cancelAll();device.location
Where the device is, over Core Location, and addresses to places and back. While in use only: the app asks for location when in use, never always, so nothing runs once the app quits.
current() and watch() need the location permission: the first call asks (the app's prompt for this notebook, then the system's) and a refusal rejects denied. With precise location turned off for the app, authorizationStatus() reads reducedAccuracy and fixes are within a few kilometres. Geocoding needs no permission but does need the network.
Over Core Location, while the app is in use only. current and watch need the location permission; geocoding needs the network but no permission. With precise location off for the app, authorizationStatus() reads reducedAccuracy and fixes are within a few kilometres. The permission methods work where the rest of the namespace does.
device.location.authorizationStatus(options?)
authorizationStatus(options?: CallOptions): Promise<AuthorizationStatus>;Reads the permission's status, without asking.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<AuthorizationStatus>: "notDetermined" until the person is asked, then a grant ("authorized", or a partial one) or "denied" or "restricted".
console.log(await device.location.authorizationStatus());device.location.isAuthorized(options?)
isAuthorized(options?: CallOptions): Promise<boolean>;Whether the permission is granted, without asking.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<boolean>: true when the status is a grant, partial ones included.
if (!(await device.location.isAuthorized())) console.log("not allowed yet");device.location.requestAuthorization(options?)
requestAuthorization(options?: CallOptions): Promise<AuthorizationStatus>;Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. The namespace's methods ask on their own, so calling this first is optional. Cancel rejects cancelled; a system prompt already up stays, and its answer still counts for later calls.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. |
Returns Promise<AuthorizationStatus>: The status after the prompt, or as it was when the person had already been asked.
const status = await device.location.requestAuthorization();
if (status === "denied") console.log("allow it in Settings to go on");device.location.current(options?)
current(options?: LocationOptions): Promise<LocationFix>;One fix, at the accuracy asked for.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | LocationOptions | no | accuracy: "best" (the default), "block" or "city"; and the call's signal. Cancel stops the search and rejects cancelled. |
Returns Promise<LocationFix>: The fix: latitude, longitude, its accuracy in metres, and altitude, speed and course when known.
Throws DeviceError: denied when the notebook or the system may not use location, or Location Services is off; failed when no fix can be had (no signal) or accuracy isn't one of the three.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | location | no | no |
While the app is in use only. With precise location off, within a few kilometres.
const fix = await device.location.current({ accuracy: "block" });
console.log(fix.latitude, fix.longitude, `±${fix.accuracy} m`);device.location.watch(options?)
watch(options?: LocationOptions): DeviceStream<LocationFix>;Fixes as the device moves, for for await, until the loop ends or the cell is interrupted. Keeps the newest 16 when the cell reads slower than they come (dropped counts the rest).
| Parameter | Type | Required | Description |
|---|---|---|---|
options | LocationOptions | no | accuracy, as for current(); and the call's signal, which ends the stream. |
Returns DeviceStream<LocationFix>: A stream of fixes; close() or leaving the loop stops Core Location.
Throws DeviceError: denied when the notebook or the system may not use location, or Location Services is off; failed when accuracy isn't one of the three, or when Core Location stops sending fixes.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | location | no | no |
Until the loop ends or the cell is interrupted; keeps the newest 16.
for await (const fix of device.location.watch({ accuracy: "block" })) {
console.log(fix.latitude, fix.longitude);
if (fix.speed === 0) break;
}device.location.geocode(address, options?)
geocode(address: string, options?: CallOptions): Promise<Place[]>;The places an address may be. Needs the network, not the permission.
| Parameter | Type | Required | Description |
|---|---|---|---|
address | string | yes | A street address, city or landmark, as a person would type it. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<Place[]>: The places, best match first, or [] when none.
Throws DeviceError: failed when address is empty or the geocoder can't be reached.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | no | no |
Needs the network, not the location permission.
const [place] = await device.location.geocode("1 Infinite Loop, Cupertino");
if (place) console.log(place.latitude, place.longitude);device.location.reverseGeocode(position, options?)
reverseGeocode(position: { latitude: number; longitude: number }, options?: CallOptions): Promise<Place[]>;What is at a position, such as a fix from current(). Needs the network, not the permission.
| Parameter | Type | Required | Description |
|---|---|---|---|
position | { latitude: number; longitude: number } | yes | latitude (-90 to 90) and longitude (-180 to 180), in degrees. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<Place[]>: The places there, or [] when nothing.
Throws DeviceError: failed when the position is out of range or the geocoder can't be reached.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | no | no |
Needs the network, not the location permission.
const [here] = await device.location.reverseGeocode(await device.location.current());
console.log(here?.city);device.motion
The device's motion sensors over Core Motion: attitude, rotation rate, gravity and the person's acceleration. iPad and iPhone only; a Mac has no motion sensors, so has("motion.watch") is false there.
Two ways to read, sharing one source: watch() for a sample every interval, as when recording, and latest() for the newest sample, as in a polling loop. Both need the motion permission: the app asks for this notebook (the system has no prompt for these sensors) and a refusal rejects denied.
Over Core Motion, on iPad and iPhone; a Mac has no motion sensors. watch and latest share one source. The system has no prompt for these sensors, so the permission is the app's own, per notebook. The permission methods work where the rest of the namespace does.
device.motion.authorizationStatus(options?)
authorizationStatus(options?: CallOptions): Promise<AuthorizationStatus>;Reads the permission's status, without asking.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<AuthorizationStatus>: "notDetermined" until the person is asked, then a grant ("authorized", or a partial one) or "denied" or "restricted".
console.log(await device.motion.authorizationStatus());device.motion.isAuthorized(options?)
isAuthorized(options?: CallOptions): Promise<boolean>;Whether the permission is granted, without asking.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<boolean>: true when the status is a grant, partial ones included.
if (!(await device.motion.isAuthorized())) console.log("not allowed yet");device.motion.requestAuthorization(options?)
requestAuthorization(options?: CallOptions): Promise<AuthorizationStatus>;Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. The namespace's methods ask on their own, so calling this first is optional. Cancel rejects cancelled; a system prompt already up stays, and its answer still counts for later calls.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. |
Returns Promise<AuthorizationStatus>: The status after the prompt, or as it was when the person had already been asked.
const status = await device.motion.requestAuthorization();
if (status === "denied") console.log("allow it in Settings to go on");device.motion.watch(options?)
watch(options?: MotionWatchOptions): DeviceStream<MotionSample>;Samples every interval seconds, for for await, until the loop ends or the cell is interrupted. Keeps the newest 64 when the cell reads slower than they come (dropped counts the rest).
| Parameter | Type | Required | Description |
|---|---|---|---|
options | MotionWatchOptions | no | interval in seconds, 0.01 to 1 (0.1 by default); and the call's signal, which ends the stream. |
Returns DeviceStream<MotionSample>: A stream of samples; close() or leaving the loop stops the sensors unless something else still reads them.
Throws DeviceError: denied when the notebook may not use motion; failed when interval is out of range, or Core Motion stops with an error.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| iPad, iPhone | motion | no | no |
Attitude, rotation rate, gravity and acceleration every interval seconds (0.01 to 1); keeps the newest 64. A Mac has no motion sensors.
const samples = [];
for await (const s of device.motion.watch({ interval: 0.05 })) {
samples.push(s.userAcceleration);
if (samples.length === 100) break;
}device.motion.latest(options?)
latest(options?: CallOptions): Promise<MotionSample>;The newest sample. The first call starts the sensors and waits for a reading; they keep running while calls keep coming, and stop about 10 seconds after the last.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<MotionSample>: The newest sample.
Throws DeviceError: denied when the notebook may not use motion; failed when Core Motion stops with an error.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| iPad, iPhone | motion | no | no |
Starts the sensors and keeps them running while calls keep coming; they stop about 10 seconds after the last.
const { attitude } = await device.motion.latest();
console.log(`tilted ${((attitude.pitch * 180) / Math.PI).toFixed(0)}°`);device.contacts
The person's contacts, over the Contacts framework. Every method needs the contacts permission: the first call asks (the app's prompt for this notebook, then the system's), and a refusal rejects denied. With limited access (iPhone and iPad), authorizationStatus() reads limited and the methods see only the contacts the person shared with the app.
Over the Contacts framework, on the Mac, iPad and iPhone. Every method needs the contacts permission. Contacts are plain records (Contact); groups, accounts, photos and notes aren't wrapped. The permission methods work where the rest of the namespace does.
device.contacts.authorizationStatus(options?)
authorizationStatus(options?: CallOptions): Promise<AuthorizationStatus>;Reads the permission's status, without asking.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<AuthorizationStatus>: "notDetermined" until the person is asked, then a grant ("authorized", or a partial one) or "denied" or "restricted".
console.log(await device.contacts.authorizationStatus());device.contacts.isAuthorized(options?)
isAuthorized(options?: CallOptions): Promise<boolean>;Whether the permission is granted, without asking.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<boolean>: true when the status is a grant, partial ones included.
if (!(await device.contacts.isAuthorized())) console.log("not allowed yet");device.contacts.requestAuthorization(options?)
requestAuthorization(options?: CallOptions): Promise<AuthorizationStatus>;Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. The namespace's methods ask on their own, so calling this first is optional. Cancel rejects cancelled; a system prompt already up stays, and its answer still counts for later calls.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. |
Returns Promise<AuthorizationStatus>: The status after the prompt, or as it was when the person had already been asked.
const status = await device.contacts.requestAuthorization();
if (status === "denied") console.log("allow it in Settings to go on");device.contacts.search(query, options?)
search(query: string, options?: CallOptions): Promise<Contact[]>;Contacts whose name matches query; or whose email matches, when query is a whole email address (a part of one, such as "@example.com", matches nothing); or whose phone number matches, when it is a phone number.
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | yes | A name or part of one, an email address, or a phone number. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<Contact[]>: The matching contacts, or [].
Throws DeviceError: denied when the notebook or the system may not read contacts; failed when query is empty or the store can't be read.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | contacts | no | no |
By name, or by email or phone number when the query is one. With limited access, only the contacts shared with the app.
for (const contact of await device.contacts.search("Ada")) {
console.log(contact.givenName, contact.familyName, contact.emails?.[0]?.value);
}device.contacts.get(id, options?)
get(id: string, options?: CallOptions): Promise<Contact | null>;One contact, by the id search, all or add gave.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | yes | The contact's id. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<Contact | null>: The contact, or null when there is none with that id (or it isn't shared with the app).
Throws DeviceError: denied when the notebook or the system may not read contacts; failed when id is empty or the store can't be read.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | contacts | no | no |
const [ada] = await device.contacts.search("Ada");
if (ada) console.log(await device.contacts.get(ada.id));device.contacts.all(options?)
all(options?: CallOptions): Promise<Contact[]>;Every contact the app may see, in the person's sort order.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<Contact[]>: The contacts, or [].
Throws DeviceError: denied when the notebook or the system may not read contacts; failed when the store can't be read.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | contacts | no | no |
const all = await device.contacts.all();
console.log(`${all.length} contacts`);device.contacts.add(contact, options?)
add(contact: Omit<Contact, "id">, options?: CallOptions): Promise<string>;Adds a contact to the default account. Every field is optional, but it needs a name, an organization, an email or a phone number.
| Parameter | Type | Required | Description |
|---|---|---|---|
contact | Omit<Contact, "id"> | yes | The contact, without an id. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled; a contact already saved stays. |
Returns Promise<string>: The new contact's id.
Throws DeviceError: denied when the notebook or the system may not change contacts; failed when the contact is empty, a field has the wrong type, birthday isn't YYYY-MM-DD or --MM-DD, or the store refuses it.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | contacts | no | no |
To the default account; groups and accounts aren't wrapped.
const id = await device.contacts.add({
givenName: "Ada",
familyName: "Lovelace",
emails: [{ label: "work", value: "ada@example.com" }],
birthday: "1815-12-10",
});device.contacts.delete(id, options?)
delete(id: string, options?: CallOptions): Promise<boolean>;Deletes a contact.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | yes | The contact's id. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled; a contact already deleted stays deleted. |
Returns Promise<boolean>: true, or false when there was no contact with that id.
Throws DeviceError: denied when the notebook or the system may not change contacts; failed when id is empty or the store refuses.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | contacts | no | no |
const id = await device.contacts.add({ givenName: "Test" });
console.log(await device.contacts.delete(id)); // truedevice.camera
The camera, through the system camera screen.
The system camera screen, on iPad and iPhone. capture needs the camera permission. The permission methods work where the rest of the namespace does.
device.camera.authorizationStatus(options?)
authorizationStatus(options?: CallOptions): Promise<AuthorizationStatus>;Reads the permission's status, without asking.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<AuthorizationStatus>: "notDetermined" until the person is asked, then a grant ("authorized", or a partial one) or "denied" or "restricted".
console.log(await device.camera.authorizationStatus());device.camera.isAuthorized(options?)
isAuthorized(options?: CallOptions): Promise<boolean>;Whether the permission is granted, without asking.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<boolean>: true when the status is a grant, partial ones included.
if (!(await device.camera.isAuthorized())) console.log("not allowed yet");device.camera.requestAuthorization(options?)
requestAuthorization(options?: CallOptions): Promise<AuthorizationStatus>;Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. The namespace's methods ask on their own, so calling this first is optional. Cancel rejects cancelled; a system prompt already up stays, and its answer still counts for later calls.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. |
Returns Promise<AuthorizationStatus>: The status after the prompt, or as it was when the person had already been asked.
const status = await device.camera.requestAuthorization();
if (status === "denied") console.log("allow it in Settings to go on");device.camera.capture(options?)
capture(options?: CallOptions): Promise<Uint8Array | null>;Shows the system camera and waits for the person to take a photo. Needs the camera permission; the first call asks for it.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal, which closes the camera and rejects cancelled. An interrupt does the same. |
Returns Promise<Uint8Array | null>: The photo as JPEG bytes, or null when the person cancels.
Throws DeviceError: denied when the notebook or the system may not use the camera; failed when the photo can't be read or is over 16 MiB.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| iPad, iPhone | camera | no | no |
The system camera screen. A Mac has none in the app; a browser would need its own camera view.
const jpeg = await device.camera.capture();
if (jpeg) await device.preview([{ name: "photo.jpg", data: jpeg }]);device.reminders
The person's reminders, over EventKit. Every method needs the reminders permission: the first call asks (the app's prompt for this notebook, then the system's), and a refusal rejects denied. No recurrence, alarms or subtasks.
Over EventKit, on the Mac, iPad and iPhone. Every method needs the reminders permission. Reminders are plain records (Reminder); recurrence, alarms and subtasks aren't wrapped. The permission methods work where the rest of the namespace does.
device.reminders.authorizationStatus(options?)
authorizationStatus(options?: CallOptions): Promise<AuthorizationStatus>;Reads the permission's status, without asking.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<AuthorizationStatus>: "notDetermined" until the person is asked, then a grant ("authorized", or a partial one) or "denied" or "restricted".
console.log(await device.reminders.authorizationStatus());device.reminders.isAuthorized(options?)
isAuthorized(options?: CallOptions): Promise<boolean>;Whether the permission is granted, without asking.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<boolean>: true when the status is a grant, partial ones included.
if (!(await device.reminders.isAuthorized())) console.log("not allowed yet");device.reminders.requestAuthorization(options?)
requestAuthorization(options?: CallOptions): Promise<AuthorizationStatus>;Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. The namespace's methods ask on their own, so calling this first is optional. Cancel rejects cancelled; a system prompt already up stays, and its answer still counts for later calls.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. |
Returns Promise<AuthorizationStatus>: The status after the prompt, or as it was when the person had already been asked.
const status = await device.reminders.requestAuthorization();
if (status === "denied") console.log("allow it in Settings to go on");device.reminders.calendars(options?)
calendars(options?: CallOptions): Promise<ReminderCalendar[]>;The reminder lists.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<ReminderCalendar[]>: The lists, the default one marked isDefault.
Throws DeviceError: denied when the notebook or the system may not read reminders.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | reminders | no | no |
for (const list of await device.reminders.calendars()) console.log(list.title, list.isDefault);device.reminders.list(options?)
list(options?: ListRemindersOptions): Promise<Reminder[]>;Reminders, from one list or all of them.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | ListRemindersOptions | no | calendar, a list's id; completed, to see only completed or only incomplete ones; and the call's signal. |
Returns Promise<Reminder[]>: The reminders, or [].
Throws DeviceError: denied when the notebook or the system may not read reminders; failed when calendar isn't a reminder list.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | reminders | no | no |
const open = await device.reminders.list({ completed: false });
for (const r of open) console.log(r.title, r.due ?? "");device.reminders.add(reminder, options?)
add(reminder: NewReminder, options?: CallOptions): Promise<string>;Adds a reminder.
| Parameter | Type | Required | Description |
|---|---|---|---|
reminder | NewReminder | yes | A title, and optional notes, due, priority and calendar. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled; a reminder already saved stays. |
Returns Promise<string>: The new reminder's id.
Throws DeviceError: denied when the notebook or the system may not add reminders; failed when the title is empty, due isn't a date, priority isn't 0 to 9, or calendar isn't a list that can be added to.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | reminders | no | no |
No recurrence or alarms.
const id = await device.reminders.add({ title: "Buy milk", due: "2026-11-10", priority: 1 });device.reminders.complete(id, options?)
complete(id: string, options?: CallOptions): Promise<boolean>;Marks a reminder done.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | yes | The reminder's id. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<boolean>: true, or false when there is no reminder with that id.
Throws DeviceError: denied when the notebook or the system may not read reminders; failed when id is empty or the store refuses.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | reminders | no | no |
const [first] = await device.reminders.list({ completed: false });
if (first) await device.reminders.complete(first.id);device.reminders.uncomplete(id, options?)
uncomplete(id: string, options?: CallOptions): Promise<boolean>;Marks a reminder not done.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | yes | The reminder's id. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<boolean>: true, or false when there is no reminder with that id.
Throws DeviceError: denied when the notebook or the system may not read reminders; failed when id is empty or the store refuses.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | reminders | no | no |
const [last] = await device.reminders.list({ completed: true });
if (last) await device.reminders.uncomplete(last.id);device.reminders.delete(id, options?)
delete(id: string, options?: CallOptions): Promise<boolean>;Deletes a reminder.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | yes | The reminder's id. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled; a reminder already deleted stays deleted. |
Returns Promise<boolean>: true, or false when there is no reminder with that id.
Throws DeviceError: denied when the notebook or the system may not read reminders; failed when id is empty or the store refuses.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | reminders | no | no |
const id = await device.reminders.add({ title: "Test" });
console.log(await device.reminders.delete(id)); // truedevice.bluetooth
A Bluetooth LE central, over Core Bluetooth: scan for peripherals, connect, and read, write and subscribe to their characteristics. On a Mac and an iPad or iPhone, in kernels in the app.
scan(), known() and connect() need the bluetooth permission: the first call asks (the app's prompt for this notebook, then the system's) and a refusal rejects denied. They reject unavailable while Bluetooth is off.
A Bluetooth LE central over Core Bluetooth, on the Mac, iPad and iPhone. scan, known and connect need the bluetooth permission and reject unavailable while Bluetooth is off. A connected peripheral is a Peripheral. The permission methods work where the rest of the namespace does.
device.bluetooth.authorizationStatus(options?)
authorizationStatus(options?: CallOptions): Promise<AuthorizationStatus>;Reads the permission's status, without asking.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<AuthorizationStatus>: "notDetermined" until the person is asked, then a grant ("authorized", or a partial one) or "denied" or "restricted".
console.log(await device.bluetooth.authorizationStatus());device.bluetooth.isAuthorized(options?)
isAuthorized(options?: CallOptions): Promise<boolean>;Whether the permission is granted, without asking.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<boolean>: true when the status is a grant, partial ones included.
if (!(await device.bluetooth.isAuthorized())) console.log("not allowed yet");device.bluetooth.requestAuthorization(options?)
requestAuthorization(options?: CallOptions): Promise<AuthorizationStatus>;Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. The namespace's methods ask on their own, so calling this first is optional. Cancel rejects cancelled; a system prompt already up stays, and its answer still counts for later calls.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. |
Returns Promise<AuthorizationStatus>: The status after the prompt, or as it was when the person had already been asked.
const status = await device.bluetooth.requestAuthorization();
if (status === "denied") console.log("allow it in Settings to go on");device.bluetooth.scan(options?)
scan(options?: ScanOptions): DeviceStream<Advertisement>;Advertisements from nearby peripherals, for for await, until the loop ends or the cell is interrupted. Keeps the newest 128 when the cell reads slower than they come.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | ScanOptions | no | services to filter by, allowDuplicates, and the call's signal, which ends the stream. |
Returns DeviceStream<Advertisement>: A stream of advertisements; leaving the loop stops scanning.
Throws DeviceError: denied when the notebook or the system may not use Bluetooth; unavailable when Bluetooth is off; failed when a service isn't a UUID.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | bluetooth | no | no |
Bluetooth LE only. A browser would need Web Bluetooth, which Safari doesn't have.
for await (const ad of device.bluetooth.scan({ services: ["180D"] })) {
console.log(ad.name, ad.rssi);
break;
}device.bluetooth.known(ids, options?)
known(ids: string[], options?: CallOptions): Promise<KnownPeripheral[]>;Peripherals the system knows by id, such as one a past scan found, to connect again without scanning.
| Parameter | Type | Required | Description |
|---|---|---|---|
ids | string[] | yes | Peripheral ids from scan() on this device, at most 64. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<KnownPeripheral[]>: The ones it knows; the others are left out.
Throws DeviceError: denied when the notebook or the system may not use Bluetooth; unavailable when Bluetooth is off; failed when an id isn't a UUID.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | bluetooth | no | no |
const [strap] = await device.bluetooth.known(["6E400001-B5A3-F393-E0A9-E50E24DCCA9E"]);
if (strap) {
await using peripheral = await device.bluetooth.connect(strap.id);
console.log(await peripheral.rssi());
}device.bluetooth.connect(id, options?)
connect(id: string, options?: ConnectOptions): Promise<Peripheral>;Connects to a peripheral.
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | yes | Its id, from scan() or known(). |
options | ConnectOptions | no | timeout in seconds (15 by default), and the call's signal. Cancel stops connecting and rejects cancelled. |
Returns Promise<Peripheral>: The connected peripheral, a handle to close when done.
Throws DeviceError: denied when the notebook or the system may not use Bluetooth; unavailable when Bluetooth is off; timeout when it doesn't answer in time; failed when it is unknown, already connected, or the kernel has 8 connected.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | bluetooth | no | no |
Services, read, write, notify and RSSI; close it, or hold it with await using. At most 8 per kernel.
for await (const ad of device.bluetooth.scan({ services: ["180D"] })) {
await using strap = await device.bluetooth.connect(ad.id);
console.log(await strap.services());
break;
}device.folders
Folders the person picked, for lasting access from this notebook: the kernel has no file system of its own. The picker is the consent, so there is no permission prompt; Settings lists each notebook's folders and revokes them. Works in kernels in the app only.
Folders the person picked, kept per notebook as bookmarks, on the Mac, iPad and iPhone. No permission: the picker is the consent, and Settings revokes. A picked folder is a Folder.
device.folders.pick(options?)
pick(options?: PickFolderOptions): Promise<Folder | null>;Shows the system folder picker and keeps what the person picks for this notebook. Picking a folder it already has replaces that grant, keeping its id.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | PickFolderOptions | no | write to ask for writing too, and the call's signal, which closes the picker and rejects cancelled. |
Returns Promise<Folder | null>: The folder, or null when the person cancelled.
Throws DeviceError: denied when the app doesn't know which notebook this is; failed when access to the folder can't be kept.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | no | no |
The system folder picker, which is the consent; the folder stays the notebook's until revoked in Settings. A browser would need the File System Access API, which Safari doesn't have.
const folder = await device.folders.pick();
if (folder) console.log(folder.name, (await folder.entries()).length);device.folders.list(options?)
list(options?: CallOptions): Promise<Folder[]>;The folders this notebook was given, in the order they were picked.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<Folder[]>: Its folders, or [].
Throws DeviceError: denied when the app doesn't know which notebook this is.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | no | no |
A Folder lists, reads, writes and removes inside the folder only; reads up to 16 MiB.
const folders = await device.folders.list();
const data = folders.find((f) => f.name === "Data") ?? (await device.folders.pick());device.widget
Widgets on the home screen, desktop or lock screen. A widget can't run a script itself, so it shows what a run last published: from a notebook or a headless run. No permission.
Widgets show what a run last published, since a widget can't run a script itself. The content is a small layout (WidgetContent): a title, up to six rows, a tint and a sparkline. No permission.
device.widget.update(kind, content, options?)
update(kind: string, content: WidgetContent, options?: CallOptions): Promise<void>;Publishes content for this notebook's widget kind, in place of what it showed, and asks the system to redraw it.
| Parameter | Type | Required | Description |
|---|---|---|---|
kind | string | yes | A name for the widget, 1 to 64 letters, digits, ., _ or -; a widget configured for this notebook and kind shows it. |
content | WidgetContent | yes | The layout: title, rows, tint, sparkline. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled; content already published stays. |
Throws DeviceError: failed when kind or content isn't valid; denied when the app hasn't said which notebook runs.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | none | no | no |
Stored in the app's App Group for its widgets; a widget can't run a script itself.
await device.widget.update("weather", {
title: "Oslo",
rows: [{ label: "Now", value: 4, unit: "°C" }, { text: "Rain at 15:00" }],
tint: "blue",
sparkline: [3, 4, 4, 5, 6, 5],
});device.shortcuts
The person's Shortcuts, run from a cell. The Shortcuts app comes forward while one runs, and Ganymede comes back when it is done. iPad and iPhone stop the app's kernel about 30 seconds after the app leaves the screen, so keep these Shortcuts short. Every call needs the shortcuts permission, which is the app's prompt alone: a Shortcut can do whatever the person set it up to.
The person's Shortcuts, run from a cell through the Shortcuts app, which comes forward while one runs. Every call needs the shortcuts permission, the app's prompt alone. The permission methods work where run does.
device.shortcuts.authorizationStatus(options?)
authorizationStatus(options?: CallOptions): Promise<AuthorizationStatus>;Reads the permission's status, without asking.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<AuthorizationStatus>: "notDetermined" until the person is asked, then a grant ("authorized", or a partial one) or "denied" or "restricted".
console.log(await device.shortcuts.authorizationStatus());device.shortcuts.isAuthorized(options?)
isAuthorized(options?: CallOptions): Promise<boolean>;Whether the permission is granted, without asking.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<boolean>: true when the status is a grant, partial ones included.
if (!(await device.shortcuts.isAuthorized())) console.log("not allowed yet");device.shortcuts.requestAuthorization(options?)
requestAuthorization(options?: CallOptions): Promise<AuthorizationStatus>;Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. The namespace's methods ask on their own, so calling this first is optional. Cancel rejects cancelled; a system prompt already up stays, and its answer still counts for later calls.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. |
Returns Promise<AuthorizationStatus>: The status after the prompt, or as it was when the person had already been asked.
const status = await device.shortcuts.requestAuthorization();
if (status === "denied") console.log("allow it in Settings to go on");device.shortcuts.run(name, options?)
run(name: string, options?: RunShortcutOptions): Promise<string | null>;Runs a Shortcut by name and waits for it to finish.
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | yes | The Shortcut's name as the Shortcuts app shows it. |
options | RunShortcutOptions | no | input, text for the Shortcut, and the call's signal. Cancel (an interrupt) stops waiting and rejects cancelled; the Shortcut may still run to its end. |
Returns Promise<string | null>: The Shortcut's output as text, or null when it gave none.
Throws DeviceError: cancelled when the Shortcut was cancelled; failed with the Shortcuts app's message when it failed or there is no Shortcut by that name, or when name or input isn't valid; denied when the notebook may not run Shortcuts; unavailable in a headless run.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | shortcuts | no | no |
Through the Shortcuts app, which comes forward while it runs. On iPad and iPhone the kernel stops about 30 seconds after the app leaves the screen, so keep the Shortcut short.
const reply = await device.shortcuts.run("Make Uppercase", { input: "hi" });
console.log(reply); // "HI"device.secrets
Strings this notebook saved, such as an API token, kept in the system keychain on this device and never synced. Each notebook sees only its own names; a renamed or moved notebook keeps them. There is no system permission: the first call asks the app's prompt for this notebook, and Settings lists and deletes each notebook's secrets.
Strings a notebook keeps in the system keychain, on this device only and per notebook. Every method needs the secrets permission, which is the app's prompt alone: there is no system one. The permission methods work where the rest of the namespace does.
device.secrets.authorizationStatus(options?)
authorizationStatus(options?: CallOptions): Promise<AuthorizationStatus>;Reads the permission's status, without asking.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<AuthorizationStatus>: "notDetermined" until the person is asked, then a grant ("authorized", or a partial one) or "denied" or "restricted".
console.log(await device.secrets.authorizationStatus());device.secrets.isAuthorized(options?)
isAuthorized(options?: CallOptions): Promise<boolean>;Whether the permission is granted, without asking.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<boolean>: true when the status is a grant, partial ones included.
if (!(await device.secrets.isAuthorized())) console.log("not allowed yet");device.secrets.requestAuthorization(options?)
requestAuthorization(options?: CallOptions): Promise<AuthorizationStatus>;Asks the person when they haven't been asked (the app's prompt for this notebook, then the system's where there is one), and resolves with the status after it; otherwise resolves with the status as it is. The namespace's methods ask on their own, so calling this first is optional. Cancel rejects cancelled; a system prompt already up stays, and its answer still counts for later calls.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. |
Returns Promise<AuthorizationStatus>: The status after the prompt, or as it was when the person had already been asked.
const status = await device.secrets.requestAuthorization();
if (status === "denied") console.log("allow it in Settings to go on");device.secrets.get(name, options?)
get(name: string, options?: CallOptions): Promise<string | null>;Reads a secret.
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | yes | The secret's name: 1 to 128 characters, no NUL. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<string | null>: The value, or null when this notebook has no secret by that name.
Throws DeviceError: denied when the notebook may not use secrets; unavailable before the device's first unlock since it started; failed when the name isn't valid.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | secrets | no | no |
The keychain, this device only. A notebook sees only its own secrets; the app's prompt is the only consent.
const token = await device.secrets.get("github");
if (!token) console.log("set it first: await device.secrets.set('github', '...')");device.secrets.set(name, value, options?)
set(name: string, value: string, options?: CallOptions): Promise<void>;Saves a secret, in place of one by the same name.
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | yes | The secret's name: 1 to 128 characters, no NUL. |
value | string | yes | The value: a string up to 64 KiB as UTF-8. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled; a value already saved stays. |
Throws DeviceError: denied when the notebook may not use secrets; unavailable before the device's first unlock since it started; failed when the name or value isn't valid.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | secrets | no | no |
Values up to 64 KiB; names 1 to 128 characters.
const token = await device.ui.prompt("GitHub token");
if (token) await device.secrets.set("github", token);device.secrets.delete(name, options?)
delete(name: string, options?: CallOptions): Promise<boolean>;Deletes a secret.
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | yes | The secret's name. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled; a secret already deleted stays deleted. |
Returns Promise<boolean>: true, or false when there was no secret by that name.
Throws DeviceError: denied when the notebook may not use secrets; unavailable before the device's first unlock since it started; failed when the name isn't valid.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | secrets | no | no |
console.log(await device.secrets.delete("github"));device.secrets.list(options?)
list(options?: CallOptions): Promise<string[]>;The names of this notebook's secrets, never their values.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<string[]>: The names, sorted, or [].
Throws DeviceError: denied when the notebook may not use secrets; unavailable before the device's first unlock since it started.
Availability
| In the app | Permission | Remote kernel | Browser |
|---|---|---|---|
| yes | secrets | no | no |
console.log(await device.secrets.list()); // ["github"]SoundPlayer
A loaded sound a cell controls, from device.sound.player: a NativeHandle. Close it when done, or hold it with await using. The app closes a kernel's players when that kernel restarts or shuts down; a kernel has at most 32 open. Each method answers the player's state after the change.
Its methods work wherever device.sound.player does.
player.play(options?)
play(options?: CallOptions): Promise<SoundState>;Plays from the current time.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. |
Returns Promise<SoundState>: The player's state after the change.
Throws DeviceError: failed when the sound can't start, or the player is closed.
const state = await player.play();
console.log(state.playing, state.duration);player.pause(options?)
pause(options?: CallOptions): Promise<SoundState>;Pauses at the current time.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. |
Returns Promise<SoundState>: The player's state after the change.
const { time } = await player.pause();
console.log(`paused at ${time.toFixed(1)} s`);player.stop(options?)
stop(options?: CallOptions): Promise<SoundState>;Pauses and goes back to the start.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. |
Returns Promise<SoundState>: The player's state after the change.
await player.stop();player.seek(seconds, options?)
seek(seconds: number, options?: CallOptions): Promise<SoundState>;Moves to a time, playing or not.
| Parameter | Type | Required | Description |
|---|---|---|---|
seconds | number | yes | The time to move to, in seconds from the start: 0 or more. |
options | CallOptions | no | The call's signal. |
Returns Promise<SoundState>: The player's state after the change.
Throws DeviceError: failed when seconds is negative or not a number.
await player.seek(player.duration / 2);
await player.play();player.setVolume(volume, options?)
setVolume(volume: number, options?: CallOptions): Promise<SoundState>;Sets the volume.
| Parameter | Type | Required | Description |
|---|---|---|---|
volume | number | yes | From 0 to 1. |
options | CallOptions | no | The call's signal. |
Returns Promise<SoundState>: The player's state after the change.
Throws DeviceError: failed when volume is outside 0 to 1.
await player.setVolume(0.25);player.state(options?)
state(options?: CallOptions): Promise<SoundState>;Reads the player's state, changing nothing.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. |
Returns Promise<SoundState>: Whether it is playing, its time and its length.
const { playing, time } = await player.state();player.close()
close(): Promise<void>;Stops the player and frees it; later calls fail failed. Closing twice is fine. Leaving an await using block closes it too.
await player.close();Properties
| Property | Type | Description |
|---|---|---|
duration | number | The sound's length in seconds. |
Peripheral
A connected Bluetooth LE peripheral, from device.bluetooth.connect: a NativeHandle. Close it to disconnect, or hold it with await using. The app closes a kernel's peripherals when that kernel restarts or shuts down; a kernel has at most 8 connected. When the peripheral goes away, waiting calls and the next call reject disconnected, notify streams end with it, and the handle closes (closed reads true once a call has seen it). Services and characteristics are UUIDs: "180D", or the full form.
Its methods work wherever device.bluetooth.connect does.
peripheral.services(options?)
services(options?: CallOptions): Promise<BluetoothService[]>;Its services and their characteristics, discovered on the first call.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<BluetoothService[]>: Each service with its characteristics and what they allow.
Throws DeviceError: disconnected when it went away; failed when it is closed or discovery fails.
for (const service of await peripheral.services()) console.log(service.uuid, service.characteristics.map((c) => c.uuid));peripheral.read(service, characteristic, options?)
read(service: string, characteristic: string, options?: CallOptions): Promise<Uint8Array>;Reads a characteristic's value.
| Parameter | Type | Required | Description |
|---|---|---|---|
service | string | yes | The service's UUID. |
characteristic | string | yes | The characteristic's UUID. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<Uint8Array>: The value's bytes.
Throws DeviceError: disconnected when it went away; failed when it is closed, there is no such characteristic, or it can't be read.
const [level] = await peripheral.read("180F", "2A19");
console.log(`battery ${level}%`);peripheral.write(service, characteristic, data, options?)
write(service: string, characteristic: string, data: Bytes, options?: WriteOptions): Promise<void>;Writes a characteristic's value.
| Parameter | Type | Required | Description |
|---|---|---|---|
service | string | yes | The service's UUID. |
characteristic | string | yes | The characteristic's UUID. |
data | Bytes | yes | The bytes, at most what the link takes at once (512 with a response). |
options | WriteOptions | no | withResponse (true by default), and the call's signal. Cancel rejects cancelled; the write may already be sent. |
Returns once the peripheral confirms it, or at once without a response.
Throws DeviceError: disconnected when it went away; failed when it is closed, there is no such characteristic, it can't be written that way, or data is too long.
await peripheral.write("6E400001-B5A3-F393-E0A9-E50E24DCCA9E", "6E400002-B5A3-F393-E0A9-E50E24DCCA9E", new TextEncoder().encode("hi"));peripheral.notify(service, characteristic, options?)
notify(service: string, characteristic: string, options?: CallOptions): DeviceStream<Uint8Array>;A characteristic's values as it notifies or indicates them, for for await, until the loop ends, the cell is interrupted, or the peripheral closes. Keeps the newest 256 when the cell reads slower than they come.
| Parameter | Type | Required | Description |
|---|---|---|---|
service | string | yes | The service's UUID. |
characteristic | string | yes | The characteristic's UUID. |
options | CallOptions | no | The call's signal, which ends the stream. |
Returns DeviceStream<Uint8Array>: A stream of values; leaving the loop unsubscribes.
Throws DeviceError: disconnected when it went away; failed when it is closed, or there is no such characteristic or it doesn't notify.
for await (const value of peripheral.notify("180D", "2A37")) {
console.log(`heart rate ${value[1]}`);
}peripheral.rssi(options?)
rssi(options?: CallOptions): Promise<number>;Reads the signal strength now.
| Parameter | Type | Required | Description |
|---|---|---|---|
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<number>: dBm, such as -60.
Throws DeviceError: disconnected when it went away; failed when it is closed.
console.log(await peripheral.rssi());Properties
| Property | Type | Description |
|---|---|---|
id | string | Its id on this device. |
name | string | null | Its name when it connected, or null. |
Folder
A folder the person picked for this notebook, from device.folders.pick() or list(). It stays this notebook's until the person revokes it in Settings, across kernel and app restarts, so there is nothing to close.
Each path is relative to the folder, with / between parts. An absolute path, a .., or a symlink that leads outside the folder rejects denied.
Its methods work wherever device.folders.pick does. Paths are relative to the folder; one that leads outside it rejects denied.
folder.entries(path?, options?)
entries(path?: string, options?: CallOptions): Promise<FolderEntry[]>;Lists a directory in the folder, sorted by name.
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | no | A directory in the folder; the folder itself when left out. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<FolderEntry[]>: Its files and directories, hidden ones included.
Throws DeviceError: denied when the path leaves the folder or the grant was revoked; unavailable when the folder can't be reached now (a disconnected drive, say); failed when there is no directory there.
const [folder] = await device.folders.list();
for (const entry of await folder.entries()) console.log(entry.kind, entry.name, entry.size);folder.read(path, options?)
read(path: string, options?: CallOptions): Promise<Uint8Array>;Reads a file in the folder.
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | yes | The file, relative to the folder. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled. |
Returns Promise<Uint8Array>: Its bytes.
Throws DeviceError: denied when the path leaves the folder or the grant was revoked; unavailable when the folder can't be reached now (a disconnected drive, say); failed when there is no file there or it is over 16 MiB.
const [folder] = await device.folders.list();
const csv = new TextDecoder().decode(await folder.read("data/temps.csv"));folder.write(path, data, options?)
write(path: string, data: Bytes | string, options?: CallOptions): Promise<void>;Writes a file in the folder, replacing what is there and making the directories on the way.
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | yes | The file, relative to the folder. |
data | Bytes | string | yes | The bytes, at most 16 MiB, or a string written as UTF-8. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled; the write may already be done. |
Returns once the file is written.
Throws DeviceError: denied when the folder was picked for reading only, the path leaves the folder, or the grant was revoked; unavailable when the folder can't be reached now (a disconnected drive, say); failed when the path is a directory or data is over 16 MiB.
const folder = await device.folders.pick({ write: true });
if (folder) await folder.write("notes/today.txt", "Written from a notebook");folder.remove(path, options?)
remove(path: string, options?: CallOptions): Promise<void>;Removes a file, a symlink (not what it points to), or an empty directory, in the folder.
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | yes | What to remove, relative to the folder. |
options | CallOptions | no | The call's signal. Cancel rejects cancelled; it may already be removed. |
Returns once it is gone.
Throws DeviceError: denied when the folder was picked for reading only, the path leaves the folder or is the folder itself, or the grant was revoked; unavailable when the folder can't be reached now (a disconnected drive, say); failed when nothing is there or a directory isn't empty.
const folder = await device.folders.pick({ write: true });
if (folder) await folder.remove("notes/old.txt");Properties
| Property | Type | Description |
|---|---|---|
id | string | Names the grant; list() gives the same id after a restart. |
name | string | The folder's name, as the picker showed it. |
writable | boolean | Whether it was picked with write: true. |
Errors
A call that fails rejects with a DeviceError, an Error whose code says why. A person dismissing a picker or dialog is not an error.
code | Meaning |
|---|---|
unavailable | No native host, or this platform doesn't have the feature. |
denied | The person or the system refused permission. |
cancelled | Interrupted or aborted before the host answered. |
timeout | The call's signal came from AbortSignal.timeout() and timed out. |
failed | Anything else, such as an argument the method refuses; the message says what. |
disconnected | The peer went away mid-call, such as a Bluetooth device or a remote link. |
Types
CallOptions
Every method takes these. Aborting signal, or interrupting the kernel, cancels the call: it rejects with a DeviceError whose code is cancelled (timeout for AbortSignal.timeout()), and the host drops any dialog or prompt it showed for it.
Where each method works is in SUPPORT (src/support.ts); the docs' table and the reference's Availability lines are generated from it.
| Property | Type | Required | Description |
|---|---|---|---|
signal | AbortSignal | no | Cancels the call when aborted. In a cdp-kernel cell, jupyter.signal aborts when the cell is interrupted. |
DeviceInfo
What the host answers to hello, once, before the first cell, and the current state when it answers info.
| Property | Type | Required | Description |
|---|---|---|---|
platform | string | yes | "macos", "ipados", "ios" or "browser". |
appVersion | string | yes | The app's version, such as "0.5.0". |
model | string | no | The hardware model identifier, such as "Mac14,2" or "iPad13,4". |
osVersion | string | no | The operating system's version, such as "26.1.0". |
capabilities | string[] | yes | The methods this host answers, such as "clipboard.readText". |
isPad | boolean | no | True on an iPad. This and the properties after it are read when info() is called, on hosts that answer it. |
screen | { width: number; height: number; scale: number } | no | The main screen in points, and its points-to-pixels scale. |
battery | { level: number | null; charging: boolean } | no | Absent on a device without a battery. level is 0 to 1, or null when unknown. |
darkMode | boolean | no | Whether the system appearance is dark. |
locale | string | no | The person's locale as a BCP 47 tag, such as "en-US". |
OpenURLOptions
Options for device.openURL.
| Property | Type | Required | Description |
|---|---|---|---|
inApp | boolean | no | On iPhone and iPad, show a web page in the app (Safari view) instead of switching to Safari. Ignored on the Mac. |
It also takes CallOptions's properties.
ImpactStyle
The strength of a haptics.impact tap.
export type ImpactStyle = "light" | "medium" | "heavy" | "soft" | "rigid";NotificationFeedback
The pattern haptics.notification plays.
export type NotificationFeedback = "success" | "warning" | "error";AlertOptions
Options for device.ui.alert.
| Property | Type | Required | Description |
|---|---|---|---|
title | string | no | A heading above the message. |
It also takes CallOptions's properties.
ConfirmOptions
Options for device.ui.confirm.
| Property | Type | Required | Description |
|---|---|---|---|
destructive | boolean | no | Shows the confirming button as destructive (red). |
okLabel | string | no | The confirming button's label; "OK" by default. |
It also takes AlertOptions's properties.
PromptOptions
Options for device.ui.prompt.
| Property | Type | Required | Description |
|---|---|---|---|
placeholder | string | no | Grey text shown while the field is empty. |
defaultValue | string | no | Text the field starts with. |
secure | boolean | no | Hides what is typed, as for a password. |
It also takes AlertOptions's properties.
ActionSheetOptions
Options for device.ui.actionSheet.
| Property | Type | Required | Description |
|---|---|---|---|
message | string | no | A line under the title. |
cancel | string | no | The cancel button's label; "Cancel" by default. |
destructive | number | no | The index of an option to show as destructive (red). |
It also takes CallOptions's properties.
PickDateOptions
Options for device.ui.pickDate.
| Property | Type | Required | Description |
|---|---|---|---|
mode | DateMode | no | What to pick; "datetime" by default. |
title | string | no | A heading above the picker. |
value | Date | no | The date the picker starts at; now by default. |
min | Date | no | The earliest date that can be picked. |
max | Date | no | The latest date that can be picked. |
It also takes CallOptions's properties.
DateMode
What device.ui.pickDate picks.
export type DateMode = "date" | "time" | "datetime";ShareOptions
What device.share shares: at least one of text, url and files.
| Property | Type | Required | Description |
|---|---|---|---|
text | string | no | Text to share, such as a message or a caption. |
url | string | URL | no | A link to share: http, https or mailto. |
files | NamedBytes[] | no | Files to share, together at most 16 MiB; their names are what the receiving app sees. |
It also takes CallOptions's properties.
NamedBytes
A file a cell hands over: a File, or bytes with the name to show and save them under.
export type NamedBytes = File | { name: string; data: Bytes };Bytes
Bytes a device method takes.
export type Bytes = Uint8Array | ArrayBuffer | Blob;PickFilesOptions
Options for device.files.pick.
| Property | Type | Required | Description |
|---|---|---|---|
types | string[] | no | What may be picked: MIME types ("text/csv"), wildcards ("image/*") or extensions (".parquet"). Anything by default. |
multiple | boolean | no | Whether several files may be picked; one by default. |
It also takes CallOptions's properties.
ExportFileOptions
Options for device.files.export.
| Property | Type | Required | Description |
|---|---|---|---|
name | string | yes | The name the save panel suggests: a plain file name, without slashes. |
It also takes CallOptions's properties.
PickPhotosOptions
Options for device.photos.pick.
| Property | Type | Required | Description |
|---|---|---|---|
limit | number | no | How many photos may be picked; 1 by default, 0 for no limit. |
It also takes CallOptions's properties.
PlaySoundOptions
Options for device.sound.play.
| Property | Type | Required | Description |
|---|---|---|---|
volume | number | no | From 0 to 1; 1 by default. |
It also takes CallOptions's properties.
SoundState
Where a player is: whether it is playing, and its time and length in seconds.
| Property | Type | Required | Description |
|---|---|---|---|
playing | boolean | yes | Whether it is playing now. |
time | number | yes | Where it is, in seconds from the start. |
duration | number | yes | The sound's length in seconds. |
NativeHandle
Something on the device a call opened that outlives the call, such as a sound player. Close it when done, or hold it with await using. The app closes a kernel's handles when that kernel restarts or shuts down. A call on a closed handle rejects failed.
| Property | Type | Required | Description |
|---|---|---|---|
closed | boolean | yes | Whether it is closed: by close(), or by leaving its await using block. |
close | Promise<void> | yes | Closes it and frees what it holds. Closing twice is fine. |
HostEvents
What each host event's handler gets, by event (device.on).
| Property | Type | Required | Description |
|---|---|---|---|
appearance | "light" | "dark" | yes | The system appearance changed. |
foreground | null | yes | The app came to the front. |
background | null | yes | The app left the front: another app is in front, or the device locked. |
battery | { level: number | null; charging: boolean } | yes | The battery's level (0 to 1, or null when unknown) or charging changed. Never on a Mac without a battery. |
locale | string | yes | The system language or region changed: the new locale, a BCP 47 tag such as "fr-FR". |
HostEvent
A host event's name: a key of HostEvents.
export type HostEvent = keyof HostEvents;OnOptions
Options for device.on.
| Property | Type | Required | Description |
|---|---|---|---|
signal | AbortSignal | no | Removes the handler when aborted. An interrupt doesn't: on outlives its cell. |
ScheduleNotificationOptions
Options for device.notifications.schedule.
| Property | Type | Required | Description |
|---|---|---|---|
delay | number | yes | Seconds from now, more than 0. |
title | string | no | Shown above the body. |
It also takes CallOptions's properties.
PendingNotification
A notification this notebook scheduled that hasn't been delivered.
| Property | Type | Required | Description |
|---|---|---|---|
id | string | yes | What schedule() returned. |
title | string | no | The title it was scheduled with. |
body | string | yes | The text it shows. |
date | Date | no | When it is due, when the host knows. |
LocationOptions
Options for device.location.current and device.location.watch.
| Property | Type | Required | Description |
|---|---|---|---|
accuracy | LocationAccuracy | no | How close a fix must be; "best" by default. Coarser fixes come sooner and use less power. |
It also takes CallOptions's properties.
LocationAccuracy
How close a fix must be: within metres ("best"), a city block, or a city.
export type LocationAccuracy = "best" | "block" | "city";LocationFix
Where the device is, as Core Location reports it.
| Property | Type | Required | Description |
|---|---|---|---|
latitude | number | yes | Degrees north of the equator. |
longitude | number | yes | Degrees east of Greenwich. |
accuracy | number | yes | The fix is within this many metres of the true position. |
altitude | number | no | Metres above sea level, when known. |
altitudeAccuracy | number | no | The altitude is within this many metres, when known. |
speed | number | no | Metres per second, when moving and known. |
course | number | no | Degrees clockwise from true north, when moving and known. |
timestamp | number | yes | When the fix was taken, in milliseconds since 1970, as Date.now(). |
Place
A place a geocode found. Every part is there only when known.
| Property | Type | Required | Description |
|---|---|---|---|
name | string | no | The place's name, such as a landmark or the street address. |
street | string | no | House number and street. |
city | string | no | City or town. |
region | string | no | State, province or the like. |
postalCode | string | no | Postal or ZIP code. |
country | string | no | Country name, in the device's language. |
countryCode | string | no | ISO 3166-1 alpha-2, such as "US". |
latitude | number | no | Degrees north of the equator. |
longitude | number | no | Degrees east of Greenwich. |
MotionWatchOptions
Options for device.motion.watch.
| Property | Type | Required | Description |
|---|---|---|---|
interval | number | no | Seconds between samples, 0.01 to 1; 0.1 by default. |
It also takes CallOptions's properties.
MotionSample
One reading of Core Motion's device motion.
| Property | Type | Required | Description |
|---|---|---|---|
attitude | Attitude | yes | Which way the device is turned. |
rotationRate | Vector3 | yes | Radians per second around each axis. |
gravity | Vector3 | yes | Gravity's pull, in g (1 g is 9.81 m/s²). |
userAcceleration | Vector3 | yes | What the person adds to gravity, in g. |
timestamp | number | yes | When it was read, in milliseconds since 1970, as Date.now(). |
Attitude
Which way the device is turned, in radians.
| Property | Type | Required | Description |
|---|---|---|---|
roll | number | yes | Around the long (y) axis. |
pitch | number | yes | Around the short (x) axis. |
yaw | number | yes | Around the axis out of the screen (z). |
Vector3
A direction or rate along the device's three axes.
| Property | Type | Required | Description |
|---|---|---|---|
x | number | yes | Along the short side, to the right with the screen facing you. |
y | number | yes | Along the long side, toward the top. |
z | number | yes | Out of the screen, toward you. |
Contact
A contact as device.contacts reads it: a plain record. Fields the contact doesn't have are left out. Groups, photos and notes aren't included.
| Property | Type | Required | Description |
|---|---|---|---|
id | string | yes | The contacts store's identifier, for get and delete. |
givenName | string | no | The first name. |
middleName | string | no | The middle name. |
familyName | string | no | The last name. |
nickname | string | no | The nickname. |
organization | string | no | The company or organization. |
jobTitle | string | no | The job title. |
emails | LabeledValue[] | no | Email addresses. |
phones | LabeledValue[] | no | Phone numbers, as entered. |
addresses | PostalAddress[] | no | Postal addresses. |
birthday | string | no | "YYYY-MM-DD", or "--MM-DD" when the year isn't known. |
LabeledValue
A label and a value, such as a work email.
| Property | Type | Required | Description |
|---|---|---|---|
label | string | no | "home", "work", "mobile", "main", "iPhone", "school", "other", or the person's own label. |
value | string | yes | The email address or phone number. |
PostalAddress
A contact's postal address; any part may be missing.
| Property | Type | Required | Description |
|---|---|---|---|
label | string | no | "home", "work", "other", or the person's own label. |
street | string | no | The street, with the house number; may span lines. |
city | string | no | The city or town. |
state | string | no | The state, province or region. |
postalCode | string | no | The postal or ZIP code. |
country | string | no | The country, as written. |
ReminderCalendar
A reminder list, from device.reminders.calendars().
| Property | Type | Required | Description |
|---|---|---|---|
id | string | yes | The list's id, for list({ calendar }) and add({ calendar }). |
title | string | yes | The list's name, as the Reminders app shows it. |
isDefault | boolean | yes | Whether add puts reminders here when it isn't given a list. |
Reminder
A reminder as device.reminders reads it: a plain record.
| Property | Type | Required | Description |
|---|---|---|---|
id | string | yes | The store's identifier, for complete, uncomplete and delete. |
title | string | yes | What to do. |
calendar | string | yes | The id of the list it is in. |
completed | boolean | yes | Whether it is done. |
notes | string | no | Notes, when it has any. |
due | string | no | "YYYY-MM-DD" for a due day, or an ISO 8601 date and time when it is due at a time. |
priority | number | no | 1 (high) to 9 (low); left out for none. The Reminders app shows 1, 5 and 9 as high, medium and low. |
completedAt | string | no | When it was completed, as ISO 8601. |
NewReminder
A reminder for device.reminders.add: a title, and the rest optional.
| Property | Type | Required | Description |
|---|---|---|---|
title | string | yes | What to do. |
notes | string | no | Notes. |
due | string | Date | no | A due day ("YYYY-MM-DD"), or a day and time: an ISO 8601 string or a Date. |
priority | number | no | 0 (none, the default) or 1 (high) to 9 (low). |
calendar | string | no | The id of the list to add it to; the default list when left out. |
ListRemindersOptions
Options for device.reminders.list.
| Property | Type | Required | Description |
|---|---|---|---|
calendar | string | no | A list's id from calendars(); every list when left out. |
completed | boolean | no | true for completed reminders only, false for incomplete ones only; both when left out. |
It also takes CallOptions's properties.
ScanOptions
Options for device.bluetooth.scan.
| Property | Type | Required | Description |
|---|---|---|---|
services | string[] | no | Only peripherals that advertise one of these service UUIDs ("180D", or a full UUID). All by default. |
allowDuplicates | boolean | no | Every advertisement, not only each peripheral's first; for RSSI over time. False by default. |
It also takes CallOptions's properties.
Advertisement
One advertisement a scan heard.
| Property | Type | Required | Description |
|---|---|---|---|
id | string | yes | The peripheral's id on this device, for connect() and known(). Another device sees a different id. |
name | string | null | yes | Its advertised or cached name, or null. |
rssi | number | yes | Signal strength in dBm, such as -60; nearer is higher. |
services | string[] | yes | The service UUIDs it advertises. |
manufacturerData | Uint8Array | null | yes | Its manufacturer data, the company id first, or null. |
serviceData | Record<string, Uint8Array> | yes | Service data by service UUID. |
KnownPeripheral
A peripheral the system knows, from device.bluetooth.known.
| Property | Type | Required | Description |
|---|---|---|---|
id | string | yes | Its id, for connect(). |
name | string | null | yes | Its cached name, or null. |
ConnectOptions
Options for device.bluetooth.connect.
| Property | Type | Required | Description |
|---|---|---|---|
timeout | number | no | Seconds to wait for the peripheral, 1 to 120; 15 by default. Then the call rejects timeout. |
It also takes CallOptions's properties.
BluetoothService
A GATT service and its characteristics.
| Property | Type | Required | Description |
|---|---|---|---|
uuid | string | yes | Its UUID, as Core Bluetooth writes it ("180D", or the full form). |
characteristics | BluetoothCharacteristic[] | yes | Its characteristics. |
BluetoothCharacteristic
A GATT characteristic and what it allows.
| Property | Type | Required | Description |
|---|---|---|---|
uuid | string | yes | Its UUID, as Core Bluetooth writes it ("2A37", or the full form). |
properties | Array<"read" | "write" | "writeWithoutResponse" | "notify" | "indicate"> | yes | What it allows. |
WriteOptions
Options for Peripheral.write.
| Property | Type | Required | Description |
|---|---|---|---|
withResponse | boolean | no | Wait for the peripheral to confirm; true by default. False sends and resolves at once. |
It also takes CallOptions's properties.
PickFolderOptions
Options for device.folders.pick.
| Property | Type | Required | Description |
|---|---|---|---|
write | boolean | no | Ask for writing too, not only reading. false by default. |
It also takes CallOptions's properties.
FolderEntry
One item in a folder, from Folder.entries().
| Property | Type | Required | Description |
|---|---|---|---|
name | string | yes | Its name in its directory. |
kind | "file" | "directory" | "link" | yes | "link" is a symlink. Reading it follows it while its target stays in the folder; remove takes away the link, not what it points to. |
size | number | no | Bytes, for a file. |
modified | Date | no | When its contents last changed. |
AuthorizationStatus
A capability's authorization. Partial grants count as granted: limited (some contacts or photos), writeOnly (add reminders or photos, not read them) and reducedAccuracy (approximate location).
export type AuthorizationStatus = "notDetermined" | "denied" | "restricted" | "authorized" | "limited" | "writeOnly" | "reducedAccuracy";