Swift
Add a floating desktop companion to your macOS 14+ app. The SDK runs inside your app and displays the mascot in a separate window above other windows. Your app owns its lifecycle; it does not use the CLI’s shared host. The SDK currently has no view for placing a mascot inside your app’s interface.
Add the package
Add https://github.com/masko-ai/masko as a Swift package dependency and select the Masko library product. Use a pinned repository revision for reproducible builds. Access to the developer repository is currently required.
Show and control a character
Call the SDK on the main actor from a running macOS app. Retain the returned mascot for as long as you need it.
import Masko
@MainActor
func startMascot() async throws -> Mascot {
let ko = try await Masko.mascot("ko")
try await ko.show()
try await ko.state("working")
try await ko.play("celebrate")
return ko
}show(), state(), play() and prepare() return a request UUID as soon as they accept the command. Media loads and playback continue in the background. You can send state and play requests while show is loading. state() holds an activity until replaced; a performance returns to the latest state.
Wait explicitly when your next step depends on completion:
let shown = try await ko.show()
try await ko.waitForRequest(shown) // First visible frame.
let play = try await ko.play("celebrate", instant: true)
try await ko.waitForPlay(play) // Complete performance and return.waitForRequest also waits for state preparation/application or full graph preparation. State application does not wait for a visual transition to finish. Inspect ko.status() for request progress/errors, or set onError for background failures. Wait timeouts use seconds.
Public Ko needs no API key. The first load downloads hosted media. Use prepare() to download the full graph in advance.
Lifecycle and layout
| Call | Result |
|---|---|
try await ko.show() | Show or resume. |
ko.hide() | Hide and pause. |
try await ko.clearState() | Release the held state; an active play finishes before returning to default. |
try ko.resize(to: 180) | Set size in screen points, between 64 and 720. |
try ko.position(x: 400, y: 100) | Move using bottom-left screen coordinates. |
try await ko.prepare() | Cache the graph’s media. |
try ko.cancel() | Request the current performance’s return path. |
ko.dispose() | Close and release the mascot. |
A second simultaneous performance fails with busy. startPlay() is an alias for play(). Use waitForPlay() for completion. Call dispose() when the feature or app closes. Use onDispose to react to the user closing the native mascot window.
Your own character
Use the current complete canvas, or optionally pin a version:
let bob = try await Masko.mascot("acme/bob", canvas: "gaming")
let request = try await bob.show()
try await bob.waitForRequest(request)
// Optional fixed snapshot: Masko.mascot("acme/bob", version: 1)Omit canvas to use the default. Show prepares existing media automatically under your workspace login; it never starts generation. The loaded snapshot stays unchanged until you dispose the handle and create another.
Automatic preparation uses your workspace login and requires write access to save its private snapshot. For trusted local tooling, pass an APIClient(baseURL:token:) using the api: argument. Do not include an authoring key in a distributed app. Use customer mascot access: your backend checks the customer and returns a temporary URL; the app calls try await Masko.mascot(mascotURL). Customers need no Masko account or publishable key. See the guide for deployment availability.
See hosted characters for creation and runtime delivery, and the package source for the full Swift interface.
Account and character creation
The same package exposes account, auth, authoring, jobs and assets through Masko.client():
let client = Masko.client() // Uses the saved login for this backend.
let balance = try await client.credits()
let bob = client.character("acme/bob")
let proposal = try await bob.add("celebrate", prompts: .init(description: "Throws confetti"))
// Review the proposal before applying it.
_ = try await bob.graph.apply(proposal)Use client.login() for browser authorization or client.login(apiKey:) to save an API key. Explicit API credentials can also be supplied to the client for trusted local tooling. Client and character methods cover creation, linking, prompts, graph editing, reference images, plans, generation and release; client.jobs and client.assets provide watching, retry and file retrieval. client.mascot(...) loads with the same backend and delivery credential. Account and creation operations use the public v1 API and need the corresponding backend deployment.
Watch generation
client.generate(plan: planID, maxCredits: 100) and bob.generate(.animations, maxCredits: 100) return a JSON receipt containing run_id. They start paid work but do not wait for the videos. Persist that ID, then watch separately:
let run = try await client.jobs.watch("RUN_ID", cursor: 0) { event in
if let message = event["message"].string { print(message) }
// Persist event["sequence"] to resume from this cursor after a disconnect.
}
print(run["status"].string ?? "", run["readiness"].string ?? "")
let outputs = try await client.assets.list(run: "RUN_ID")Watching spends no credits and can be restarted. Events distinguish source-video generation, background removal/WebM, and HEVC export. Image-only runs can complete with awaiting_animations; only readiness: ready confirms a playable result. A failed or partially failed run throws generation_failed; inspect it with client.jobs.show("RUN_ID") before retrying.
A new show against incomplete media fails in the background with version_not_ready. Wait for generation to finish and issue show again; use waitForRequest() to observe the loading result. Existing instances keep playing their loaded snapshot throughout generation. Dispose and recreate them to load the completed edits.