Rootless

Automate iOS, iPadOS, and macOS using plain JavaScript. Build custom widgets and interact directly with native APIs.

Download on the App Store
Rootless app interface

Plain JavaScript

Supports ES6. Scripts are stored as plain .js files on your disk.

Native APIs & Documentation

Integrate directly with iOS native APIs. Fully documented, available offline.

Siri Shortcuts

Trigger scripts via Siri. Present tables, websites, and custom HTML natively.

System Integration

Run from the Share Sheet, process inputs, and manage local files via Files.app.

x-callback-url

Communicate seamlessly and trigger workflows with other apps on your device.

Customizable & Ready

Tailor the code editor to your liking. Includes several example scripts to start immediately.

Documentation

Also inside the app, or as rootless.json.

Photos

The photo library

requestAccess() works only in the app: a widget cannot show a permission sheet. iOS also offers limited access, where the user picks which photos an app can see. status reports it separately and reads simply return fewer assets. image() takes a maxSize because a widget is killed above roughly 30MB, and a full-size photo decodes to more than that.

Properties

statusString

"authorized", "limited", "denied", "restricted" or "notDetermined".

isAuthorizedBoolean

True for authorized and for limited.

Methods

Photos.requestAccess()String

Shows the permission sheet. App only.

returns The status afterwards.

Photos.albums()[Any]

Albums holding at least one item. Smart albums such as Favourites and Screenshots come first.

returns One { id, title, count } per album. Pass id to latest() or random().

Photos.latest(options)[Any]

The newest assets first.

  • options Any required
    • count Number

      How many. Defaults to 10.

    • album String

      An album id from albums(). Left out, the whole library.

    • type String

      "image" (default), "video" or "any".

returns Each asset is { id, width, height, type, isFavorite, createdAt, latitude?, longitude? }.

Photos.random(options)Any

One asset at random, or null if there are none.

  • options Any required
    • album String

      An album id to pick from.

    • type String

      "image" (default), "video" or "any".

returns One asset, shaped as in latest(), or null when nothing matches.

Photos.image(asset, options)Any

Loads the pixels. Downloads from iCloud if the asset is not on the device.

  • asset Any required

    An asset from latest() or random(), or just its id.

  • options Any required
    • maxSize Number

      Longest edge in pixels. Default 1024.

returns An image, ready for an Image node or for Vision.

Examples

A random photo, every hour

if (!Photos.isAuthorized) {
  Script.setWidget(new Widget({ child: Text("Grant photo access in the app") }));
} else {
  var pick = Photos.random();
  Script.setWidget(new Widget({
    refreshInterval: Duration.hours(1),
    child: Image.from(Photos.image(pick, { maxSize: 600 }), { fit: BoxFit.cover }),
  }));
}