SDK

Swift

iOS 15+, macOS 12+, tvOS and watchOS. Zero dependencies: Ed25519 comes from CryptoKit, which your app already has.

Install

Package.swift
.package(url: "https://github.com/ripstop-dev/ripstop-swift", from: "0.1.0")

Quickstart

SwiftUI
import Ripstop
import SwiftUI

@main
struct MyApp: App {
@State private var gate: Ripstop?

var body: some Scene {
WindowGroup {
if let gate {
RipstopShell(gate: gate, onOpenURL: { UIApplication.shared.open($0) }) {
ContentView()
}
} else {
ContentView().task {
gate = await Ripstop.initialize(apiKey: "rs_pub_your_key")
}
}
}
}
}

Headless

Swift
switch await ripstop.check() {
case .forceUpdate(let title, let body, let storeUrl):
break // Blocked. Not dismissible.

case .softUpdate(let title, let body, _, let canSnooze):
break // A nudge. await ripstop.snooze() records it.

case .maintenance(let title, let message, let endsAt, _, _, _):
break // Down on purpose. endsAt is display-only.

case .none:
break // Carry on.
}

It is an actor

Ripstop is a Swift actor, so the cache and the snooze ledger are safe to touch from anywhere without you holding a lock. Every method is awaited; none of them throw.

Remote config

Swift
let enabled = await ripstop.values["checkout_enabled"]?.boolValue ?? true
let limit = await ripstop.values["upload_limit"]?.intValue ?? 10

Always pass a fallback. On a first launch with no network there is no payload yet. That is the fail-open path working as intended.

Options

OptionDefaultWhat it does
apiKeyno defaultYour app’s public SDK key. Safe to ship
appVersionno defaultThe running build; rules evaluate against it
localeenWhich wall copy to resolve; falls back to en per key
minFetchInterval6 hoursHow long a payload is fresh enough to skip the network
timeout5 secondsFetch budget. After that, cache
storageUserDefaultsSwap for Keychain, or InMemoryStorage
signingKeyspinnedOverride for self-hosted deployments and tests
session.sharedInject your own URLSession for tests or a proxy

UserDefaults is fine here

The cache lives in UserDefaults, which is not a secret store and does not need to be: everything in it is signed, and a payload that has been tampered with fails verification rather than being believed. Swap in Keychain if your threat model differs.

Source

⌘K