Five steps to offline-first sync
Add buoyient to your module-level build.gradle.kts. Pick the modules you need.
Add the buoyient XCFramework to your Xcode project via Swift Package Manager.
dependencies {
implementation("com.elvdev.buoyient:syncable-objects:<version>")
}
dependencies {
implementation("com.elvdev.buoyient:syncable-objects:<version>")
implementation("com.elvdev.buoyient:syncable-objects-hilt:<version>")
}
dependencies {
implementation("com.elvdev.buoyient:syncable-objects:<version>")
implementation("com.elvdev.buoyient:syncable-objects-hilt:<version>")
testImplementation("com.elvdev.buoyient:testing:<version>")
debugImplementation("com.elvdev.buoyient:mock-mode:<version>") // for mock mode
}
That's it for dependencies. buoyient auto-initializes via androidx.startup — no Application.onCreate() code needed for initialization.
From the buoyient repo root, build the framework:
./gradlew :syncable-objects:assembleBuoyientReleaseXCFramework
Via SPM: In Xcode, go to File → Add Package Dependencies and point to the buoyient repo root (which contains Package.swift). Select the Buoyient library product.
Manual: Drag Buoyient.xcframework from syncable-objects/build/XCFrameworks/release/ into your Xcode project. Set embedding to Do Not Embed (static framework).
The framework includes SKIE, which automatically generates Swift-friendly wrappers:
suspend functions → Swift async/awaitsealed class / sealed interface → Swift enumFlow / StateFlow → Swift AsyncSequence
buoyient requires manual initialization on iOS — see Step 5 for SwiftUI App.init() setup and Info.plist configuration.
Describe your domain object below and watch the code generate in real time. Or skip ahead — you can always copy the template.
Describe your domain object below. The model, config, and service are written in Kotlin in a small shared module — SKIE then exposes them as native Swift APIs automatically.
buoyient's service classes use Kotlin-specific features (KSerializer, @Serializable, coroutines) that can't be expressed in Swift. Even for iOS-only apps, you create a small Kotlin shared module for the service definitions. Your iOS app then calls them from Swift with async/await — see the Swift usage example in Step 4.
A typical structure looks like:
MyProject/
├── shared/ ← Kotlin module (model + service)
│ └── src/commonMain/kotlin/
│ ├── Todo.kt
│ ├── TodoRequestTag.kt
│ └── TodoService.kt
└── iosApp/ ← Swift app (calls the service)
└── Sources/
├── TodoViewModel.swift
└── ContentView.swift
Each operation type gets a tag. This lets buoyient route responses and lets you customize handling per operation.
Each operation type gets a tag. This Kotlin enum goes in your shared module alongside the model.
Extend SyncableObjectService and expose domain-specific methods using the protected create(), update(), void(), and get() methods.
Define the Kotlin service in your shared module, then see the Swift usage below for how your iOS app calls it.
CreateRequestBuilder is a lambda that builds the HTTP request — it receives the data, an idempotency key, and connectivity infoResponseUnpacker extracts your object from the server response JSONserviceName must be unique — it's the SQLite partition keyHttpRequest.serverIdOrPlaceholder(serverId) in update/void URLs for offline safety
suspend fun → Swift async — call with try awaitsealed class → Swift enum — match with switchFlow<T> → Swift AsyncSequence — consume with for try awaitTell buoyient about your service so it can sync in the background via WorkManager.
Register your service and configure iOS background task scheduling via BGTaskScheduler.
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.elvdev.buoyient.sync</string>
</array>
IosSyncScheduleNotifier.Companion.shared.registerHandler() must be called before the app finishes launchingInfo.plist entry is required for BGTaskScheduler to accept task submissionsBuoyient.shared.syncNow() on foreground transitions (scenePhase == .active) for reliable syncRun through this checklist to make sure everything is wired up correctly.
BuoyientInitializer
Buoyient.syncNow() — verify immediate sync
Your app now has offline-first sync. Time to build something great.
Dive deeper into the full documentation for advanced patterns.
ServerProcessingConfig, request builders, response unpacking, flow-based operations, and cross-service dependencies.
Read guide →TestServiceEnvironment, mock HTTP handlers, offline path testing, stateful mock server, and conflict simulation.
Read guide →Run your full app against fake data with a developer toggle. No real backend needed for manual testing.
Read guide →