One Logo Pool Ball
wwwwwwwwwwwwwwwwwww

App Intents

Expose fixed iOS actions to Shortcuts

One.AppIntents exposes actions declared at build time to Apple’s Shortcuts app. Each action accepts an optional string and returns a string. Declare the action metadata in config, then define its JavaScript handler in a native setup file so a cold Shortcuts launch can start the React host and reach it.

On iOS prebuild generates Swift AppIntent structs with shortcut phrases, and invocations run through the Shortcuts system. On Android and web defineAction validates its arguments and returns an inert remover; it registers no system action.

Usage

import { one } from 'one/vite'
export default {
plugins: [
one({
setupFile: { native: './app-intents-setup.ts' },
native: {
app: {
name: 'MyApp',
ios: {
bundleId: 'com.example.myapp',
appIntents: {
actions: [
{
id: 'com.example.myapp.echo',
title: 'Echo Text',
textParameterTitle: 'Text',
shortcutPhrase: 'Echo text in {app}',
},
],
},
},
},
},
}),
],
}

IDs and titles must be nonempty, IDs unique. The phrase must contain {app} exactly once; prebuild substitutes the app’s name for Shortcuts. textParameterTitle is optional: without it the action takes no input and the handler receives null.

app-intents-setup.ts
import { One } from 'one'
One.AppIntents.defineAction('com.example.myapp.echo', async (text) => {
return text ?? ''
})

The handler must return a string or a promise of one. defineAction returns an idempotent remover; keep the registration for the app’s lifetime unless intentionally stopping that action.

How it works

The Shortcuts app discovers the generated Swift action types after an iOS rebuild. Registration cannot change at runtime; native rejects an action absent from the built manifest. Each invocation delivers once: actions arriving before JavaScript registers are held, then claimed by defineAction; later invocations go straight to the active handler.

A missing, thrown, or stalled handler fails the Shortcuts run. Native bounds each invocation to 20 seconds including startup, reporting a timeout past it; a thrown handler reports a handler error. The setup file must import and define handlers early, and prebuild requires it when actions are declared. Siri voice invocation, app entities, typed parameters, and device-specific policy stay outside this API.

API reference

MethodsTypeDescription
defineAction(identifier: string, handler: AppIntentHandler) => () => voidHandle an action. Returns an idempotent remover.
(identifier: string, handler: AppIntentHandler) => () => voidHandle an action. Returns an idempotent remover.

The handler is (text: string | null) => string | Promise<string>.

InvocationTypeDescription
executionIdstringThis run. Keys completion and timeout.
stringThis run. Keys completion and timeout.
identifierstringThe declared action id.
stringThe declared action id.
textstringThe input text, when the action takes one.
stringThe input text, when the action takes one.

Platform differences

iOSAndroidWeb
ActionsShortcuts appNoneNone
DeclareAt buildIgnoredIgnored
HandlerSetup fileInertInert
Timeout20 secondsNever runsNever runs

Limits and gotchas

Examples

Summarize text from Shortcuts

app-intents-setup.ts
import { One } from 'one'
function firstSentence(text: string) {
const end = text.search(/[.!?]\s/)
return end === -1 ? text : text.slice(0, end + 1)
}
One.AppIntents.defineAction('com.example.myapp.summarize', async (text) => {
if (!text) return 'No text provided.'
return firstSentence(text)
})

Pure string work fits the budget with room to spare. The null branch covers actions declared without a text parameter.

Look up a note by title

app-intents-setup.ts
import { One } from 'one'
const NOTES: Record<string, string> = {
standup: 'Standup is at 9:30 in room 4.',
}
One.AppIntents.defineAction('com.example.myapp.note', async (text) => {
const key = text?.trim().toLowerCase() ?? ''
return NOTES[key] ?? `No note for "${text ?? ''}".`
})

The handler closes over module data available at setup time. A missing key returns a helpful string rather than throwing, so the Shortcuts run succeeds with a useful spoken result.

Edit this page on GitHub.