Reactor is one session, and the tracks and commands on it. It speaks raw JSON over the wire: open a
session, send commands by name, receive generic message events. It works against any model — for the
commands and events a specific one accepts, see the
Model API Reference, or ask the running model itself with
requestSchema().
async throws, and failures arrive as a thrown ReactorError —
the same type an onError event delivers. final class, not a struct: a session has one
owner, and a Reactor released without calling disconnect() orphans the session —
the next run cannot start until it clears.
Control-event handlers —
onStatus, onError, onMessage, onRuntimeMessage — run on a serial
queue this SDK owns, never on the thread the library called on. Pass eventQueue: .main to the
initializer to run them on the main queue instead. async calls resume on the library’s own
completion thread, deliberately not through that queue — await reactor.connect() from the main
actor would otherwise wait on a queue that is waiting for it.Creating a client
Signature
String
required
The model to connect to, as
owner/name. A bare name resolves under reactor/.String?
A token minted elsewhere, used as it is. For a server that already holds one, or a client handed
one by a backend that owns the key.
String
default:"https://api.reactor.inc"
The Reactor API.
Reactor.localAPIURL is http://localhost:8080, for a local runtime.Bool
default:"false"
Accept a local Reactor API’s self-signed certificate and speak its local-development protocol. Pair
it with
apiURL: Reactor.localAPIURL, or leave apiURL at its default — local: true alone
resolves to the local Reactor API.DispatchQueue?
Where control-event handlers run.
nil — the default — means the SDK’s own serial dispatcher
queue. Pass .main to have them run on the main queue instead.init throws — a client is fully constructed or not created at all. The one failure worth naming:
ReactorError.versionMismatch when the loaded native library speaks a different ABI than this SDK
was built against, which has to be caught before any other call, since past it the stack is corrupted
rather than an error reported.
For a trusted server holding the raw API key, exchange it first — see
fetchJWT(apiKey:apiURL:options:local:) and
Authentication — or use the convenience initializer that does both in one step:
Signature
Example
Connecting
connect(sessionID:connectionID:)
Creates — or adopts — a session and brings up the transport. Resolves once the session is
.ready.
Signature
String?
Join a session that already exists rather than creating one. This is how a second client attaches
to the same session. A session adopted this way is not ended by
disconnect() — it keeps running for its owner.UInt32?
Adopt a connection slot a backend already registered for this session. The connection-level
analogue of
sessionID; most callers building one connection per session leave it nil. See
Multiple connections per session.ReactorError.unauthorized for a token problem, ReactorError.conflict for a session a
previous run left orphaned.
Example
reconnect()
Cycles the connection without ending the session — after a transient failure, or deliberately from
.ready.
Signature
disconnect() first — and doing so
would end the very session this is about to reuse. Throws when there is no session to reconnect to.
Recvonly tracks resume automatically. Sendonly tracks do not: a track published before the
reconnect is not published after it — publish again.
Track/published
says which side of that you are on, and pushFrame throws
rather than pushing into a slot with nothing behind it.disconnect()
Ends the session server-side and tears down the transport.
Signature
reconnect(). Only ends sessions this client created;
one adopted via sessionID is left running for its owner.
close()
Releases the native handle immediately, without ending the session server-side.
Signature
Reactor goes away — deinit calls it.
Call it yourself to release resources at a known point. This does not end the session; use
disconnect() for that. A creator that goes away without disconnecting orphans the
session, and the next run cannot start until it clears.
status
Signature
ReactorStatus. Readable
before connect() — a client that never connected reports .disconnected rather than nothing.
sessionID
Signature
Commands and uploads
sendCommand(_:_:uploads:)
Sends a command to the model and waits for its correlated reply.
Signature
String
required
The command name. Must match a command the model defines.
JSONValue?
The payload.
JSONValue is ExpressibleByDictionaryLiteral,
so a Swift dictionary literal works directly.[String: FileRef]
Files to pass as named parameters — see
uploadFile(at:).CommandReply, or nil when the
handler ran and acknowledged the command without returning a message, as an auto-generated
set_<field> setter does. nil is not a failure and is not folded into one:
Example
Encodable argument instead of a JSONValue dictionary — the arguments:
label is required, since JSONValue is itself Encodable and an unlabelled overload would be
ambiguous with the one above:
Signature
uploadFile(at:)
Uploads a local file and returns a FileRef to pass into a
command.
Signature
.ready session — the upload is created against it.
Example
uploadData(_:name:mimeType:)
The same result as uploadFile(at:), for a caller who has the bytes rather than a
path — a frame just rendered, a buffer just decoded.
Signature
Example
requestSchema()
Signature
Recordings
requestClip(_:)
Asks for a clip covering the last duration of the session.
Signature
download(_:to:readyTimeout:progress:) is what waits for
that.
Example
requestRecording()
Signature
download(_:to:readyTimeout:progress:)
Downloads a clip’s segments into one playable file.
Signature
Clip
required
What
requestClip(_:) or requestRecording() answered.URL
required
The file to create. Opened before the first segment is fetched, so an unwritable path fails early.
Duration?
How long to keep waiting past the runtime’s own prediction.
nil — the default — waits as long as
the session can still produce the clip, the only sane answer for a model generating slower than
real time: a clip becomes ready because the model keeps generating, so once the session is gone a
“not ready” is a “not ready” forever.((DownloadProgress) -> Void)?
Called after each segment is written, on the download’s own thread.
Example
This download outlives the client. If the client is closed mid-download the call fails with a
message saying the file may still arrive — because it may.
Tracks
tracks
Every track the session declared, as a TrackList — for
discovery, and for a caller who would rather not hardcode a name.
Signature
Example
connect().
track(_:)
The track called name, as a Track — the only way to push frames
into one, receive its frames, or pause it.
Signature
Example
ReactorError.notFound, naming what the session does declare, for a name that is not among
them. Before the session has declared anything, any name is accepted — that is what lets handlers be
registered ahead of connecting.
pausedTracks
Signature
Events
Everyon* method returns a Subscription: a token that
cancels the handler when it is released.
Signature
Example
onMessage never has to filter the platform’s out of it.
There is no client-wide frame event. Media is delivered per track, through
Track/onFrame(_:) — a single handler fed every incoming
track at once could not tell them apart.onError hands you a ReactorError, the same type a failed call throws. Match on code, or branch
on recoverable when the specific code does not matter — or pattern-match directly:
catch ReactorError.unauthorized. See ReactorError.for await over a closure, statusUpdates, errors, and messages expose
the same events as AsyncStreams:
Signature
Reactor.timeMicros()
The engine’s monotonic clock, in microseconds — the epoch a frame’s capture time is read in.
Signature
Example
Reactor.fetchJWT(apiKey:apiURL:options:local:)
Exchange an API key for a JWT, without creating a client.
Signature
Example
TokenOptions and
Authentication.