Runtime APIs
ScriptWidget exposes JavaScript APIs for system info, device state, networking, storage, read-only HealthKit access, and location.
console
Logging helpers for scripts.
log(...args)
//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// Usage for api console
//
console.log("Hello ScriptWidget");
console.log(
"1. A grammatical unit that is syntactically independent and has a subject that is expressed or, as in imperative sentences, understood and a predicate that contains at least one finite verb. 2. The penalty imposed by a law court or other authority upon someone found guilty of a crime or other offense."
);
$scheme
Scheme helpers for deep links and widget navigation.
$scheme.open(url)
// ScriptWidget
// https://xnu.app/scriptwidget
//
// app schemes
//
/*
# open ScriptWidget app
scriptwidget://
# open ScriptWidget app and refresh all widgets
scriptwidget://reload-all
*/
$file
Read packaged files and JSON from the script bundle.
read(path)
readJSON(path)
//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// Usage for api file
//
// read as string
console.log($file.read("data.json"));
// read as json
let json = $file.readJSON("data.json");
console.log(json);
console.log(json.name);
$import
Import additional JS/JSX files inside a script package.
$import(path)
// ScriptWidget
// https://xnu.app/scriptwidget
//
// import other js/jsx files
//
$import("util.jsx");
$import("define.js");
$render(
<vstack>
<text font="title">test</text>
{textItems}
<text font="title">{sum(1, 2)}</text>
</vstack>
);
Usage Map
Example of advanced ES2020 map usage in ScriptWidget.
// ScriptWidget
// https://xnu.app/scriptwidget
//
// Mapping over an array
// Thanks for Reina for telling me this good idea
//
const values = ["one", "two", "three", "four"];
const test = values.map((value) => {
return(<text>{value}</text>)
})
$render(
<vstack>
<text font="title">test</text>
{test}
</vstack>
);
$gradient
Build gradient string values for color/background props.
$gradient(obj)
//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// Usage for api $gradient
//
let angularGradient = {
type: "angular",
colors: ["green", "blue", "black", "green", "blue", "black", "green"],
center: "center",
};
$render(
<vstack background={$gradient(angularGradient)} frame="max">
<text font="title">AngularGradient</text>
</vstack>
);
fetch
Convenience HTTP fetch returning response text.
fetch(url)
fetch(url, params)
//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// Usage for api $fetch
//
// query json example
// https://jsonplaceholder.typicode.com/
const url = "https://jsonplaceholder.typicode.com/todos/1";
const result = await fetch(url);
console.log(result);
const model = JSON.parse(result);
$render(
<vstack>
<text font="title">receive title: {model.title}</text>
</vstack>
);
Usage ES2020
ES2020 syntax showcase for ScriptWidget scripts.
//
// ScriptWidget
// https://xnu.app/scriptwidget
//
//
const foo = null ?? 'default string';
console.log(foo);
// expected output: "default string"
const baz = 0 ?? 42;
console.log(baz);
// expected output: 0
const someArray = [0, 1, 2, 3];
console.log(someArray);
const thisWillError = someArray[5];
console.log(thisWillError);
const thisWillBeUndefined = someArray?.[5];
console.log(thisWillBeUndefined);
$render(
<vstack frame="max">
<text font="title">Hello ES2020</text>
</vstack>
);
$getenv
Read environment values such as widget size and parameters.
$getenv(key)
//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// Usage for component $getenv
//
/*
widget-size
- large
- medium
- small
*/
const widget_size = $getenv("widget-size");
/*
parameter config in system widget config panel
*/
const widget_param = $getenv("widget-param");
$render(
<vstack frame="max">
<text font="title">Widget Size : {widget_size}</text>
<text font="caption">Widget Parameter : {widget_param}</text>
</vstack>
);
$device
Device hardware, screen, battery, disk space, and appearance helpers.
screen()
battery()
model()
name()
totalDiskSpace()
freeDiskSpace()
//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// Usage for api $device
//
console.log($device.name());
console.log($device.model());
console.log($device.language());
console.log($device.systemVersion());
console.log(JSON.stringify($device.screen(), null, 2));
console.log(JSON.stringify($device.battery(), null, 2));
console.log($device.isdarkmode());
console.log($device.totalDiskSpace());
console.log($device.freeDiskSpace());
$storage
Key-value storage backed by app group UserDefaults.
getString(key)
setString(key, value)
getJSON(key)
setJSON(key, obj)
keys()
clear()
//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// Usage for api storage
//
$storage.setString("greeting", "Hello ScriptWidget");
const greeting = $storage.getString("greeting");
$storage.setJSON("profile", {
name: "Alex",
city: "Shanghai",
updatedAt: new Date().toISOString()
});
const profile = $storage.getJSON("profile");
const allKeys = $storage.keys();
$render(
<vstack frame="max" padding="12" background="#0f172a">
<text font="caption" color="#94a3b8">Storage</text>
<text font="title3" color="#e2e8f0">{greeting}</text>
<text font="caption" color="#94a3b8">Name: {profile.name}</text>
<text font="caption" color="#94a3b8">Keys: {allKeys.join(", ")}</text>
</vstack>
);
$health
Read-only HealthKit access for steps, active energy, and heart rate.
isAvailable()
requestAuthorization()
stepCountToday()
activeEnergyToday()
heartRateLatest()
//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// Usage for api health
// Note: HealthKit requires main app permission and capability.
// This API reads health data only (no write).
//
if (!$health.isAvailable()) {
$render(
<vstack frame="max" padding="12" background="#0f172a">
<text font="title3" color="#f87171">HealthKit Unavailable</text>
<text font="caption" color="#94a3b8">This platform does not support HealthKit.</text>
</vstack>
);
} else {
const granted = await $health.requestAuthorization();
if (!granted) {
$render(
<vstack frame="max" padding="12" background="#0f172a">
<text font="title3" color="#fbbf24">Permission Needed</text>
<text font="caption" color="#94a3b8">Enable Health access in the main app.</text>
</vstack>
);
} else {
const steps = await $health.stepCountToday();
const energy = await $health.activeEnergyToday();
const heart = await $health.heartRateLatest();
$render(
<vstack frame="max" padding="12" background="#0f172a">
<text font="caption" color="#94a3b8">Health Today</text>
<text font="title3" color="#e2e8f0">Steps: {steps.value.toFixed(0)}</text>
<text font="caption" color="#94a3b8">Active Energy: {energy.value.toFixed(0)} kcal</text>
<text font="caption" color="#94a3b8">Latest HR: {heart.value.toFixed(0)} bpm</text>
</vstack>
);
}
}
$system
System and app metadata, locale, calendar, memory, uptime, power, and CPU info.
appInfo()
locale()
timeZone()
calendarInfo()
systemUptime()
memory()
osVersionString()
processorCount()
//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// Usage for api system
//
const app = $system.appInfo();
const tz = $system.timeZone();
const calendar = $system.calendarInfo();
const memory = $system.memory();
const uptimeHours = ($system.systemUptime() / 3600).toFixed(1);
const cpuCount = $system.processorCount();
const activeCpuCount = $system.activeProcessorCount();
$render(
<vstack frame="max" padding="12" background="#0f172a">
<text font="caption" color="#94a3b8">System</text>
<text font="title3" color="#e2e8f0">{app.name}</text>
<text font="caption" color="#94a3b8">Bundle: {app.bundleId}</text>
<text font="caption" color="#94a3b8">Version: {app.version} ({app.build})</text>
<spacer />
<text font="caption" color="#94a3b8">Platform: {$system.platform()}</text>
<text font="caption" color="#94a3b8">Locale: {$system.locale()}</text>
<text font="caption" color="#94a3b8">Timezone: {tz.identifier} ({tz.abbreviation})</text>
<text font="caption" color="#94a3b8">Calendar: {calendar.identifier}</text>
<text font="caption" color="#94a3b8">Uptime: {uptimeHours}h</text>
<text font="caption" color="#94a3b8">OS: {$system.osVersionString()}</text>
<text font="caption" color="#94a3b8">Host: {$system.hostName()}</text>
<text font="caption" color="#94a3b8">CPU: {cpuCount} ({activeCpuCount} active)</text>
<text font="caption" color="#94a3b8">Memory: {(memory.physical / 1024 / 1024 / 1024).toFixed(1)} GB</text>
<text font="caption" color="#94a3b8">Low Power: {$system.lowPowerMode() ? "On" : "Off"}</text>
</vstack>
);
$location
Core Location access for current coordinates and authorization status. Options are optional; accuracy defaults to reduced.
isAvailable()
authorizationStatus()
requestAuthorization(options?)
current({ timeout?, timeoutMs?, accuracy?, purposeKey?, maxAge?, maxAgeMs? }?)
//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// Usage for api location
// Note: Location requires main app permission.
// This API reads current location only.
// Options are optional; default accuracy is reduced.
// Use maxAge/maxAgeMs for cached location to return faster.
//
if (!$location.isAvailable()) {
$render(
<vstack frame="max" padding="12" background="#0f172a">
<text font="title3" color="#f87171">Location Unavailable</text>
<text font="caption" color="#94a3b8">This device does not support location services.</text>
</vstack>
);
} else {
const status = $location.authorizationStatus();
let granted = status === "authorizedWhenInUse" || status === "authorizedAlways";
if (!granted) {
granted = await $location.requestAuthorization({ timeout: 10 });
}
if (!granted) {
$render(
<vstack frame="max" padding="12" background="#0f172a">
<text font="title3" color="#fbbf24">Permission Needed</text>
<text font="caption" color="#94a3b8">Enable Location access in the main app.</text>
</vstack>
);
} else {
const location = await $location.current({
timeout: 10,
maxAge: 30,
accuracy: "full",
purposeKey: "ScriptWidgetLocation"
});
$render(
<vstack frame="max" padding="12" background="#0f172a">
<text font="caption" color="#94a3b8">Current Location</text>
<text font="title3" color="#e2e8f0">
{location.latitude.toFixed(5)}, {location.longitude.toFixed(5)}
</text>
<text font="caption" color="#94a3b8">
Accuracy: {Math.round(location.accuracy)}m ({location.accuracyAuthorization})
</text>
<text font="caption2" color="#64748b">
Age: {location.age.toFixed(1)}s {location.isStale ? "(stale)" : ""}
</text>
<text font="caption2" color="#64748b">Updated: {location.timestamp}</text>
</vstack>
);
}
}
$http
HTTP helpers for GET/POST/PUT/PATCH/DELETE.
get(url)
post(url, params)
put(url, params)
patch(url, params)
delete(url, params)
//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// Usage for api $http
//
// $http.get
// $http.get is identity to $fetch or fetch api
const get_result = await $http.get("https://jsonplaceholder.typicode.com/todos/1");
console.log(get_result);
// query json example with header
// https://docs.github.com/en/rest/reference/projects
const get_with_header_result = await $http.get("https://api.github.com/users/everettjf/orgs", {
headers: {
Accept: "application/vnd.github.inertia-preview+json",
},
});
console.log(get_with_header_result);
// $http.post
const post_result = await $http.post("https://jsonplaceholder.typicode.com/posts", {
body: {
userId: 1,
id: 1,
title: "Hello ScriptWidget",
}
});
console.log(post_result);
const post_with_header_result = await $http.post("https://jsonplaceholder.typicode.com/posts", {
headers: {
Accept: "application/vnd.github.inertia-preview+json",
},
body: {
userId: 1,
id: 1,
title: "Hello ScriptWidget",
}
});
console.log(post_with_header_result);
const post_string_with_header_result = await $http.post("https://jsonplaceholder.typicode.com/posts", {
headers: {
Accept: "application/vnd.github.inertia-preview+json",
},
body: "password=123&name=321"
});
console.log(post_string_with_header_result);
// $http.put (same to post)
// $http.patch (same to post)
// $http.delete (same to post)
$render(
<vstack>
<text>$http example</text>
</vstack>
);
$animation
Build animation configuration strings.
$animation(obj)
//
// ScriptWidget
// https://xnu.app/scriptwidget
//
// basic clock animation
//
$render(
<vstack frame="max,center">
<zstack>
<vstack animation="clockSecond">
<rect frame="5,50" color="yellow"></rect>
<rect frame="5,50" color="clear"></rect>
</vstack>
<vstack animation="clockMinute">
<rect frame="5,40" color="blue"></rect>
<rect frame="5,40" color="clear"></rect>
</vstack>
<vstack animation="clockHour">
<rect frame="5,30" color="red"></rect>
<rect frame="5,30" color="clear"></rect>
</vstack>
</zstack>
</vstack>
);
HealthKit and Location data are only available after user authorization in the main app. Widgets may show cached or fallback data.