LiveActivity
Lock Screen and Dynamic Island
The content is a widget tree, the same one Script.setWidget() takes. A Widget where a background and padding matter, a bare node like Text("38m") where they do not; both work anywhere content is accepted. Three limits come from iOS: starting an activity needs Rootless in the foreground, so a widget refresh or a Shortcut can update and end but not start; all four trees together must encode to under 4KB; and iOS ends an activity after eight hours. Activities are scoped per script, so all() and end() see only your own.
Properties
isSupportedBooleanWhether this device can show Live Activities at all. False on Mac.
areEnabledBooleanWhether the user has left them on for Rootless, in Settings.
Methods
LiveActivity.start(options)StringStarts one and returns its id. Options: title, content (a Widget or a node), island, staleAfter (Duration). island takes compactLeading, compactTrailing and minimal for the collapsed states, plus expanded: {leading, trailing, center, bottom} for the long-pressed one. Leading and trailing flank the camera cutout, center sits under it, bottom spans the full width. Without expanded, the banner is shown across the bottom and the band beside the cutout stays empty. Needs Rootless in the foreground.
- options Any required
- title String
Names the activity. Not drawn. It identifies it in
all(). - content Any required
The Lock Screen banner: a Widget, or a bare node when a background and padding are not wanted.
- island Any
The Dynamic Island. Leave it out and the island shows nothing.
- compactLeading Any
Left of the camera cutout when the island is collapsed. About 44pt wide, an icon or a few characters.
- compactTrailing Any
Right of the cutout, same size.
- minimal Any
A ~36pt circle, shown when another app also has an activity running and yours is squeezed.
- expanded Any
The long-pressed island, by region. Without it the banner is shown across the bottom.
- leading Any
Left of the cutout, level with it. Fill this and
trailingor the band beside the camera stays empty. - trailing Any
Right of the cutout.
- center Any
Under the cutout, between leading and trailing.
- bottom Any
Full width, beneath everything else.
- leading Any
- compactLeading Any
- staleAfter Duration
How long before what is on screen counts as out of date. The activity stays up;
all()starts reporting it as "stale".
- title String
returns The activity's id. Pass it to update() and end(). Worth putting in Storage: a later run has no other way back to it.
LiveActivity.update(id, options)Replaces the content. Options: content, island, staleAfter, and alert ({title, body}) to make the update ring.
- id String required
From start(), or from all().
- options Any required
- content Any required
Replaces the banner.
- island Any
Replaces the island. Same shape as in start().
- compactLeading Any
Left of the camera cutout when the island is collapsed. About 44pt wide, an icon or a few characters.
- compactTrailing Any
Right of the cutout, same size.
- minimal Any
A ~36pt circle, shown when another app also has an activity running and yours is squeezed.
- expanded Any
By region, as in start().
- leading Any
Left of the cutout, level with it. Fill this and
trailingor the band beside the camera stays empty. - trailing Any
Right of the cutout.
- center Any
Under the cutout, between leading and trailing.
- bottom Any
Full width, beneath everything else.
- leading Any
- compactLeading Any
- staleAfter Duration
Resets the staleness clock.
- alert Any
Makes the update ring and light up the screen. Leave it out for a silent one.
- title String
Shown on devices that cannot display the activity itself, like a Watch.
- body String
The line under it.
- title String
- content Any required
LiveActivity.end(id, options)Ends it. Options: content for a final frame, dismissAfter (Duration) to leave it on screen a while longer.
- id String required
From start(), or from all().
- options Any required
Optional, end(id) keeps whatever is on screen as the last frame.
- content Any
A final frame, if the last state is not the one to leave behind.
- dismissAfter Duration
Keeps it on the Lock Screen this much longer before it disappears. Default is the system's own timing.
- content Any
LiveActivity.all()[Any]This script's running activities.
returns One { id, title, state } per activity. state is "active", "stale" (still on screen but past staleAfter, so what it shows is out of date), "ended" or "dismissed". An activity the user swiped away is not in the list at all, so an empty result means the session is over.
LiveActivity.endAll()Ends every activity this script started, immediately.
Examples
Start, update, end
var id = LiveActivity.start({
title: "Laundry",
content: new Widget({ child: Text("38 min left") }),
island: {
compactLeading: Text("🧺"),
compactTrailing: Text("38m"),
expanded: {
leading: Text("🧺 Washing"),
trailing: Text("38m"),
bottom: ProgressBar({ value: 0.4 }),
},
},
});
LiveActivity.update(id, {
content: new Widget({ child: Text("2 min left") }),
alert: { title: "Almost done", body: "Two minutes left" },
});
LiveActivity.end(id, { dismissAfter: Duration.minutes(5) });Only when it can work
if (!LiveActivity.areEnabled) {
console.warn("Live Activities are off for Rootless.");
} else {
LiveActivity.start({ title: "Timer", content: new Widget({ child: Text("Go") }) });
}