Mobile SDK
iOS SDK
Native Swift SDK for deep linking, attribution, user binding, and conversion tracking. Built with Rust and compiled to a Swift Package via UniFFI. Install ID persists across app reinstalls via the iOS Keychain.
Quick Start
Add the Swift Package
In Xcode, File → Add Package Dependencies and enter the repository URL:
https://github.com/saltyskip/riftChoose Up to Next Major Version from 0.2.3 and add the RiftSDK library to your app target. Requires iOS 15+.
rift-ios-sdk-*.tar.gz from GitHub Releases, extract it, and use Add Local on the ios/ directory instead.Initialize (one line)
You need a publishable key. The convenience constructor auto-wires Keychain storage and reads the app version from Bundle.main.
import RiftSDK
// One line — Keychain storage, app version, all defaults.
let rift = RiftSdk.create(publishableKey: "pk_live_YOUR_KEY")install_id (UUID) on first launch and stores it in the Keychain. It survives app deletion and reinstallation.Bind the user (one line)
Call setUserId wherever you handle your user session — after signup, login, or session restore. Safe to call on every launch. The SDK handles persistence, sync, and retry.
// Wherever you know the user is signed in:
Task {
try? await rift.setUserId(userId: currentUser.id)
}Track conversions (one line)
Fire a conversion event whenever a user does something worth counting. The SDK reads the bound user_id and POSTs to the Rift API using your publishable key.
// On trade completion, purchase, signup — whatever you're measuring:
try await rift.trackConversion(
conversionType: "trade",
idempotencyKey: orderId,
metadata: ["asset": "ETH", "side": "buy"]
)The server dedupes via idempotencyKey, so retries are safe.
Deferred Deep Linking
One-call deferred deep link (3 lines)
On first launch, check the pasteboard for a Rift link. The SDK detects whether a URL is present, parses it, reports attribution, and returns the link data for navigation — all in one call.
// On first launch:
if let result = try await rift.checkDeferredDeepLinkFromPasteboard() {
if let deepLink = result.iosDeepLink {
handleDeepLink(deepLink)
}
}UIPasteboard.detectPatterns(for:) to check for a URL without reading the contents, so it only touches the pasteboard — and only triggers the iOS 16+ paste banner — when a URL is actually present. On a match it clears the pasteboard so a re-launch doesn't re-attribute. Pass your own text via checkDeferredDeepLink(clipboardText:) if you need custom gating.Handle an incoming link
Resolve a link that opened the app
When the app is already installed, the OS hands you the link URL directly via a Universal Link (or your custom scheme) — no clipboard involved. Record the attribution and route to the destination:
import RiftSDK
// SwiftUI — on your App or root view:
.onOpenURL { url in
// Your link id is the last path component, e.g.
// https://go.yourcompany.com/summer-sale
guard let linkId = url.pathComponents.last, !linkId.isEmpty else { return }
Task {
try? await rift.attributeLink(linkId: linkId) // record attribution
if let link = try? await rift.getLink(linkId: linkId), // resolve destination
let deepLink = link.iosDeepLink {
handleDeepLink(deepLink)
}
}
}Click Tracking
Record a click
If your app opens Rift links internally (e.g., share sheets), record the click:
let result = try await rift.click(linkId: "summer-sale")
print("Platform: \(result.platform)")
print("Deep link: \(result.iosDeepLink ?? "none")")Logout
Call clearUserId() when the user signs out. The install ID is preserved — only the user binding is removed.
try rift.clearUserId()API Reference
Constructors
| Constructor | Description |
|---|---|
| RiftSdk.create(publishableKey:) | Convenience. Auto-wires Keychain storage + app version. Recommended. |
| RiftSdk(config:, storage:) | Full control. Pass custom RiftConfig and RiftStorage implementation. |
Methods
| Method | Returns | Description |
|---|---|---|
| setUserId(userId:) | Void (async throws) | Bind the install to a user. Persists + syncs + retries on next launch. |
| trackConversion(conversionType:, idempotencyKey:, metadata:?) | Void (async throws) | Fire a conversion event. POSTs to the Rift API via publishable key. |
| checkDeferredDeepLinkFromPasteboard(clearOnMatch:) | DeferredDeepLinkResult? (async throws) | Recommended. Detects a URL on the pasteboard without reading it (no paste banner), then resolves + attributes. Clears the pasteboard on a match. |
| checkDeferredDeepLink(clipboardText:) | DeferredDeepLinkResult? (async throws) | Lower-level: you supply the clipboard text. Parses, attributes, and fetches link data. |
| clearUserId() | Void (throws) | Remove stored user binding. Call on logout. |
| installId() | String (throws) | Persistent install UUID. Generates on first call. |
| attributeLink(linkId:) | Bool (async throws) | Report a deferred attribution using the SDK's internal install_id + app version. Persists and retries on next launch if the network call fails. |
| click(linkId:) | ClickResult (async throws) | Record a click and return link data. |
| getLink(linkId:) | GetLinkResult (async throws) | Fetch link data without recording a click. |
Free functions
| Function | Description |
|---|---|
| parseClipboardLink(text:, allowedHosts:) | Low-level: extracts a link ID from a URL whose host is in allowedHosts. Most apps use checkDeferredDeepLinkFromPasteboard() instead. |