ApplesauceOS
Read Markdown ↗Download .md

Objective-C from JavaScript

Use UIHelper to find buttons, enter text and scroll. Use ObjC when you want to call a native Objective-C method directly, such as reading a Foundation object. You write JavaScript; you do not need to write or compile Objective-C.

Choose where the method runs

WhereAPIExample
Inside SpringBoardObjCObjC.class("NSUUID").UUID()
Inside another appRemoteObjC.forApp(bundleId)app.class("NSBundle").mainBundle()

Local means SpringBoard. Opening another app or selecting it in the inspector does not change the target of ObjC. You do not need to connect to another app for local calls. Both APIs are available directly in scripts without imports or the at. prefix.

To confirm the target:

js
console.log(ObjC.getInfo().processName); // "SpringBoard"

Call methods directly in SpringBoard

Get a class, call its methods, and convert native strings with toJS():

js
const NSUUID = ObjC.class("NSUUID");
const uuid = NSUUID.UUID();
const text = uuid.UUIDString();
try {
  console.log(text.toJS()); // A UUID string
} finally {
  text.release();
  uuid.release();
}

You can also obtain SpringBoard's application object:

js
const SpringBoard = ObjC.class("SpringBoard");
const application = SpringBoard.sharedApplication();
try {
  console.log(application.respondsTo("canOpenURL:"));
} finally {
  application.release();
}

The same style works with other classes already loaded in SpringBoard. Use ObjC.hasClass("ClassName") to check availability. Installing an app or library does not mean its classes exist in SpringBoard. Private classes and methods can differ between iOS versions.

Four words you need

WordMeaningExample
ClientChooses which process receives your callsObjC or RemoteObjC.forApp(bundleId)
ClassA native type that can create objects or provide class methodsNSString, the native string type
Object / proxyA JavaScript handle to a native objectThe string returned by stringWithString:
SelectorThe exact name of a native method, including its colonsstringWithString:

ObjCObject on reference pages is a label for returned objects. It is not a global to import or construct. Likewise, client and object in a signature are example variable names.

1. Make a native string and read its value

js
const text = ObjC.call("NSString", "stringWithString:", "Hello");
try {
  console.log(text.toJS());          // "Hello": a normal JS string
  console.log(text.call("length"));  // 5: a normal JS number
} finally {
  text.release();
}

Read the first line as: ask the NSString class to run stringWithString: with "Hello" as its argument. The result is a native object handle. toJS() copies its string value into JavaScript. length already returns a number, so it needs no toJS().

release() releases your handle when finished. It does not close an app. Do not use that handle afterward. Script cleanup also releases references, but explicit cleanup avoids accumulation in long loops.

2. Call a method with more than one argument

js
const dictionary = ObjC.create("NSMutableDictionary");
try {
  dictionary.call("setObject:forKey:", "Hello", "message");
  console.log(dictionary.toJS()); // {message: "Hello"}
} finally {
  dictionary.release();
}

ObjC.create() allocates and initializes an object. setObject:forKey: takes two arguments in that order: the value, then its key. Each colon belongs to one argument. You must know which class or object provides the method; the bridge does not invent app methods.

3. Optional shorthand

These two calls are equivalent:

js
const dictionary = ObjC.create("NSMutableDictionary");
try {
  dictionary.call("setObject:forKey:", "Hello", "message");
  dictionary.setObject_forKey_("Hello", "message");
} finally {
  dictionary.release();
}

Replace selector colons with underscores. length() has no arguments and needs no underscore. The final underscore is optional: setObject_forKey("Hello", "message") also works. Use call("exact:selector:", ...) when you prefer exact native spelling or when a native method name collides with a helper such as get, set or release. A double underscore represents a literal underscore.

4. Read information inside another app

ObjC always addresses SpringBoard. To call inside Settings, open it and choose a remote client:

js
if (!openApp("com.apple.Preferences")) throw new Error("Could not open Settings");
const app = RemoteObjC.forApp("com.apple.Preferences");
console.log(app.getInfo().bundleId); // "com.apple.Preferences"
const bundle = app.class("NSBundle").mainBundle();
let identifier;
try {
  identifier = bundle.bundleIdentifier();
  console.log(identifier ? identifier.toJS() : null);
} finally {
  if (identifier) identifier.release();
  bundle.release();
}

The remote client has the same class, call, create, out and other methods as ObjC. Creating it does not launch the app. The app must be running with its ApplesauceOS helper enabled; a suspended or blacklisted app may not answer.

Keep one client per app and reuse it. Objects belong to the client/session that created them: copy ordinary data with toJS() before passing it to another client. After an app restart, call app.reconnect() and obtain new object handles.

What comes back?

Native resultJavaScript resultWhat to do
BOOL, integer, floating pointBoolean or numberUse directly
Integer outside the safe Number rangeBigInt, or decimal string if BigInt is unavailablePreserve precision; convert to a string before JSON serialization
NSString, NSArray, NSDictionary, NSNumberNative object proxyUse toJS() when you need ordinary JS data
Other native objectNative object proxyCall its known methods; toJS() is not a universal object serializer
nil or voidnullCheck before calling methods
Supported structure, such as CGRectPlain object with named fieldsRead x, y, width, height for CGRect

Pass JavaScript null for native nil; the string "nil" is just text. get(key) and set(key, value) use native key-value coding. Assigning object.someProperty = value is unsupported; use set() or an exact native setter.

Errors and limits

If a class or selector is unavailable, calls throw. hasClass() and respondsTo() let you check support. signature() reports native type encodings; it is an advanced diagnostic, not a human description of what a method does.

Calls are synchronous and execute on the target's main thread. Keep them short. Remote timeouts stop waiting; they do not undo a method already executing. Do not automatically repeat an uncertain mutation.

Callbacks, non-null Objective-C blocks, variadic methods, unions and arbitrary pointer arguments are unsupported. Known C functions use C and RemoteC. Wrong native calls can crash the target process.

Find the method you need

TaskReference
Choose another appRemoteObjC.forApp
Find a class / check availabilityclass, hasClass
Call a class or object methodObjC.call, object.call
Create an objectcreate
Copy a native value into JStoJS
Read or write a native propertyget, set
Receive an NSError output parameterout
Inspect method supportrespondsTo, signature
Release a handle / sessionrelease, close
Recover after an app restartreconnect

Each reference page has its own arguments, result and small example. Read the target native API's documentation too: an ApplesauceOS method explains how to call it, not the meaning of every method supplied by every app.