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.

Contacts

Names, numbers, birthdays

requestAccess() works only in the app, like Photos. iOS 18 adds limited access, where the user picks which contacts an app can see; status reports it separately. birthdays() works out the days remaining with Calendar rather than by subtracting, so leap days and the year rollover come out right. age is absent when the birthday was stored without a year, which is common.

Properties

statusString

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

isAuthorizedBoolean

True for authorized and for limited.

Methods

Contacts.requestAccess()String

Shows the permission sheet. App only.

returns The status afterwards.

Contacts.all(options)[Any]

Every contact, by given name.

  • options Any required
    • limit Number

      Stop after this many. 0 or absent means all of them.

returns Each is { id, name, givenName, familyName, nickname?, organization?, phones, emails, birthday?, hasImage }.

Contacts.search(name, options)[Any]

Contacts whose name matches. Uses the system index.

  • name String required

    Part of a name.

  • options Any required
    • limit Number

      Stop after this many.

Contacts.birthdays(options)[Any]

Upcoming birthdays, soonest first.

  • options Any required
    • withinDays Number

      How far ahead to look. Defaults to 365.

returns A contact plus daysUntil, where 0 means today, and age. age is absent when the birthday has no year.

Contacts.image(contact, options)Any

The contact's picture, or null. Most contacts have none.

  • contact Any required

    A contact or its id.

  • options Any required
    • thumbnail Boolean

      Small version. True by default; the full one is worth it only in the app.

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

Examples

Who is next

var soon = Contacts.birthdays({ withinDays: 30 });

Script.setWidget(new Widget({
  child: Column({
    crossAxisAlignment: CrossAxisAlignment.start,
    children: soon.slice(0, 3).map(function (p) {
      var when = p.daysUntil === 0 ? "today" : "in " + p.daysUntil + " days";
      return Text(p.name + ", " + when);
    }),
  }),
}));