Installation¶
Pick your runtime below. The tab you choose is remembered across this site, so the rest of the guides will show you the same one.
Requirements¶
| SwiftUI | Compose | React | |
|---|---|---|---|
| App target | iOS 17+ (or macOS 14+) | minSdk 26, compileSdk 34 |
React 18.3.1 |
| Language | Swift 5.9+ / SwiftUI | Kotlin 2.0+, JVM 17 | TypeScript 5.5+ |
| Editor | macOS, with xcrun simctl |
macOS, with adb on PATH |
macOS |
The editor is a macOS app in every case. What differs is what it drives: an iOS Simulator, an Android emulator, or your web dev server in a web view.
Add the Swift package¶
In Xcode, choose File → Add Package Dependencies… and enter:
Add the Inertia library product to your app target.
Or declare it in a Package.swift:
dependencies: [
.package(url: "https://github.com/hpennington/Inertia", branch: "main")
],
targets: [
.target(
name: "MyApp",
dependencies: ["Inertia"]
)
]
Then import it wherever you use it:
The iOS 17 floor is not arbitrary: the runtime plays your tracks through SwiftUI's
KeyframeAnimator, which is an iOS 17 API.
Add the animation file to your target¶
The runtime loads animations from a MessagePack resource in
your app bundle, looked up by the container's id. A container created with
id: "animation" reads animation.inertia.
-
Create an empty
animation.inertianext to your Swift sources. The file is binary, so it cannot be typed into an editor — an empty array is the single byte0x90: -
Drag it into your Xcode project.
- Confirm it appears under Target → Build Phases → Copy Bundle Resources.
The file is required in release builds
Outside editor mode, InertiaContainer reads this resource during
initialization and traps if it is missing or fails to decode. An empty
array is a valid animation file; a missing file is not.
Once the editor is writing animations for this project, you copy its animation.inertia
over this one. See Projects for where the editor keeps it.
Add the editor build flag¶
Editor mode should be compiled in for development builds only. The convention used by
the example app is a INERTIA_EDITOR Swift flag on a dedicated scheme or build
configuration:
- Select your target → Build Settings.
- Find Other Swift Flags (
OTHER_SWIFT_FLAGS). - For the configuration you want to edit in, add
-D INERTIA_EDITOR.
Then read it in one place:
Add the Gradle dependency¶
The runtime is published through JitPack. Add the repository in
settings.gradle.kts:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}
Then the artifact in your app module:
dependencies {
implementation("com.github.hpennington:inertia-compose:v1.0.8")
}
Everything is in one package:
import org.inertiagraphics.inertia.InertiaContainer
import org.inertiagraphics.inertia.Inertia
import org.inertiagraphics.inertia.LocalInertia
Building against a checkout instead
To work against the runtime source rather than a published release — which is what the demo app in the repository does — include it as a composite build and substitute the module:
Allow cleartext WebSocket traffic¶
The runtime dials the editor over plain ws://, and cleartext has been denied by
default since targetSdk 28. Grant the permission and permit the hosts you may dial
the editor at:
<uses-permission android:name="android.permission.INTERNET" />
<application android:networkSecurityConfig="@xml/network_security_config" …>
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<!-- Reaches the editor through the `adb reverse` tunnel it opens. -->
<domain includeSubdomains="true">127.0.0.1</domain>
<domain includeSubdomains="true">localhost</domain>
<!-- The stock emulator's route to the host machine. -->
<domain includeSubdomains="true">10.0.2.2</domain>
</domain-config>
</network-security-config>
Keep this to the hosts you actually need, and prefer a debug source set over the
main manifest if the app is ever going to be released.
Add the animation file¶
Put the animation.inertia the editor exported in your app's assets:
The basename has to match the container's id. With dev = false the container reads
it from there; with dev = true it ignores the file and takes its schemas from the
editor over the socket instead.
Wire dev to a build flag so a release build never dials the editor:
Build and link the packages¶
The React runtime is two npm packages that are not published to a registry — you build them out of the repository and link them into your app:
inertia-base— the framework-agnostic core: schema and message types, the hierarchy tree, the interpolation, the WebSocket client.inertia-react— the React bindings that sit on top of it.
inertia-react depends on inertia-base by file path, so the two build in order.
The repository has a script that does exactly that:
Then depend on the built package from your app:
React 18.3.1 is a peer dependency of inertia-react, so your app supplies it.
Having a second copy of React resolve inside the package breaks hooks, which is why
the build script deletes node_modules/react before building.
Then import from it:
Serve the animation file¶
Outside editor mode the container does not read a bundled file — it fetches
<baseURL>/<id>.inertia over HTTP. Something has to serve the editor's animations
directory, with CORS headers, at whatever baseURL you pass:
The repository ships a small server that does this with
Access-Control-Allow-Origin: * already set, at
example/demo.inertia/animations/serve_animations.py. A plain http.server works too
as long as your page is served from the same origin.
Add the editor flag¶
Editor mode is a prop, so drive it from the environment rather than hardcoding it:
const isDev = process.env.REACT_APP_INERTIA_DEV !== "false";
const baseURL = process.env.REACT_APP_INERTIA_BASE_URL ?? "http://localhost:8000";
With dev false the container never opens a socket, so a production bundle does not
reach for an editor.
Next¶
- Quickstart — get a view animating.
- Editor mode — connect a running app to the editor.