Flagbearer for iOS
The Swift SDK for Flagbearer, for iOS and other Apple platforms. Requires iOS 15, macOS 12, tvOS 15 or watchOS 8.
Install
In Xcode, choose File › Add Package Dependencies and enter:
https://github.com/flagbearer-dev/sdksOr add it to a Package.swift:
dependencies: [
.package(url: "https://github.com/flagbearer-dev/sdks", from: "1.0.0")
]Then add the Flagbearer product to your app's target.
Keys
Use a client key, which starts with fbc_. Client keys are public and safe to
include in an app. Server keys, which start with fbs_, are secret and belong
only on your server.
Set up one client
Create one client for the whole app:
// Flags.swift
import Flagbearer
let flags = Flagbearer(clientKey: "fbc_...")Flags load in the background and are kept on the device, so the app starts with the flags each user last had, even offline.
While the app is in the foreground, a change you make in the dashboard reaches it within a couple of seconds, over a live connection. If that connection drops, the SDK checks every 30 seconds until it's back. After five minutes in the background it stops both, and catches up when the app returns.
Users
When the signed-in user is known or changes, call identify:
try await flags.identify(User(key: user.id, email: user.email))The email is optional. Include it to turn flags on for testers by email. After
sign-out, call identify(User()).
Checking a flag
if flags.isEnabled("new-checkout") {
showNewCheckout()
}isEnabled never waits and never throws. It returns false until the current
user's flags have loaded, and when the flag doesn't exist. Pass another default
as flags.isEnabled("new-checkout", default: true).
flags.status is .loading until the current user's flags arrive. Use it to
hold back UI that mustn't switch between the default and the real value.
SwiftUI
Wrap the client in an observable object, so views redraw when flags change:
import Flagbearer
import SwiftUI
final class FlagStore: ObservableObject {
init() {
flags.subscribe { [weak self] in self?.objectWillChange.send() }
}
func isEnabled(_ flag: String, default defaultValue: Bool = false) -> Bool {
flags.isEnabled(flag, default: defaultValue)
}
var loaded: Bool { flags.status != .loading }
}struct CheckoutView: View {
@EnvironmentObject var flagStore: FlagStore
var body: some View {
if flagStore.isEnabled("new-checkout") {
NewCheckout()
} else {
Checkout()
}
}
}subscribe calls its listener on the main queue whenever flags or status
change. It returns a function that stops listening.
Error reports
allFlags() returns every flag's current value, so error reports show exactly
which flags were on. It doesn't count as checking them.
crashReporter.setContext("flags", flags.allFlags())
// ["new-checkout": true, "dark-mode": false]When something is wrong
The SDK never crashes or blocks the app over a flag problem. It keeps the last flags, or defaults, and says what's wrong in the Xcode console:
- A key the server refuses is explained once, with the server's reason.
- A server key (
fbs_) is never sent. The console says to use a client key. - In debug builds, checking a flag that doesn't exist warns once per flag.
Pass debug: true to also log each fetch, and to get the missing-flag warning
in release builds.
Options
let flags = Flagbearer(
clientKey: "fbc_...",
user: User(key: "42"), // the user at launch, if already known
debug: false,
appVersion: nil, // defaults to the app's version
cacheFlags: true // start with the flags this user last had
)