LaunchDarkly Observability SDK for Swift
NB: APIs are subject to change until a 1.x version is released.
The iOS observability plugin automatically instruments:
- Activity Lifecycle: App lifecycle events and transitions
- HTTP Requests: URLSession requests
- Crash Reporting: Automatic crash reporting
- Feature Flag Evaluations: Evaluation events added to your spans.
- Session Management: User session tracking and background timeout handling
A complete example application is available in the swift-launchdarkly-observability/ExampleApp directory.
Add the Swift Package dependency in Xcode or
NOTE: since APIs are subject to change until a 1.x version is released, pointing to main branch is a temporal workaround to test/use the package
LaunchDarkly Observability is designed for Swift 5. To depend on the swift-launchdarkly-observability package, you need to add it in your Package.swift as follows:
.package(url: "https://github.com/launchdarkly/swift-launchdarkly-observability", branch: "main"),Add the observability plugin to your LaunchDarkly iOS Client SDK configuration:
import UIKit
import LaunchDarkly
import LaunchDarklyObservability
let mobileKey = "your-mobile-key"
let config = { () -> LDConfig in
var config = LDConfig(
mobileKey: mobileKey,
autoEnvAttributes: .enabled
)
config.plugins = [
Observability(options: .init(sessionBackgroundTimeout: 3))
]
return config
}()
let context = { () -> LDContext in
var contextBuilder = LDContextBuilder(
key: "12345"
)
contextBuilder.kind("user")
do {
return try contextBuilder.build().get()
} catch {
abort()
}
}()
final class AppDelegate: NSObject, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
) -> Bool {
LDClient.start(
config: config,
context: context,
startWaitSeconds: 5.0,
completion: { (timedOut: Bool) -> Void in
if timedOut {
// Client may not have the most recent flags for the configured context
} else {
// Client has received flags for the configured context
}
}
)
return true
}
}Session Replay captures user interactions and screen recordings to help you understand how users interact with your application. To enable Session Replay, add the SessionReplay plugin alongside the Observability plugin:
import UIKit
import LaunchDarkly
import LaunchDarklyObservability
import LaunchDarklySessionReplay
let mobileKey = "your-mobile-key"
let config = { () -> LDConfig in
var config = LDConfig(
mobileKey: mobileKey,
autoEnvAttributes: .enabled
)
config.plugins = [
// Observability plugin must be added before SessionReplay
Observability(options: .init(
serviceName: "ios-app",
sessionBackgroundTimeout: 3)),
SessionReplay(options: .init(
isEnabled: true,
privacy: .init(
maskTextInputs: true,
maskWebViews: false,
maskImages: false,
maskAccessibilityIdentifiers: ["email-field", "password-field"]
)
))
]
return config
}()
let context = { () -> LDContext in
var contextBuilder = LDContextBuilder(key: "12345")
contextBuilder.kind("user")
do {
return try contextBuilder.build().get()
} catch {
abort()
}
}()
final class AppDelegate: NSObject, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
) -> Bool {
LDClient.start(
config: config,
context: context,
startWaitSeconds: 5.0,
completion: { (timedOut: Bool) -> Void in
if timedOut {
// Client may not have the most recent flags for the configured context
} else {
// Client has received flags for the configured context
}
}
)
return true
}
}By default, Session Replay starts recording as soon as the SDK is initialized if isEnabled is set to true. If you want to initialize Session Replay without activating recording immediately (e.g., to wait for user consent or a specific event), set isEnabled to false in the options:
SessionReplay(options: .init(
isEnabled: false,
// ... other options
))You can then activate recording later by setting LDReplay.shared.isEnabled to true.
// From a SwiftUI View or @MainActor isolated class
LDReplay.shared.isEnabled = trueConfigure privacy settings to control what data is captured:
- maskTextInputs: Mask all text input fields (default:
true) - maskWebViews: Mask contents of Web Views (default:
false) - maskLabels: Mask all text labels (default:
false) - maskImages: Mask all images (default:
false) - maskAccessibilityIdentifiers: Array of accessibility identifiers to mask
- maskUIViews: Array of UIView classes to mask
- minimumAlpha: Minimum alpha value for view visibility (default:
0.02)
You can override the default privacy settings on individual views using modifiers:
SwiftUI Views:
import SwiftUI
import LaunchDarklySessionReplay
struct ContentView: View {
var body: some View {
VStack {
// Mask this specific view
Text("Sensitive information")
.ldMask()
// Unmask this view (even if it would be masked by default)
Image("profile-photo")
.ldUnmask()
// Conditionally mask based on a flag
TextField("Email", text: $email)
.ldPrivate(isEnabled: shouldMaskEmail)
}
}
}UIKit Views:
import UIKit
import LaunchDarklySessionReplay
class CreditCardViewController: UIViewController {
let cvvField = UITextField()
let nameField = UITextField()
override func viewDidLoad() {
super.viewDidLoad()
// Mask the CVV container
cvvField.ldMask()
// Unmask the name field (even if text inputs are masked by default)
nameField.ldUnmask()
// Conditionally mask based on a flag
cvvField.ldPrivate(isEnabled: true)
}
}You can customize the observability plugin with various options:
import UIKit
import LaunchDarkly
import LaunchDarklyObservability
let config = { () -> LDConfig in
var config = LDConfig(
mobileKey: mobileKey,
autoEnvAttributes: .enabled
)
config.plugins = [
Observability(
options: .init(
serviceName: "ios-app",
serviceVersion: "0.1.0",
resourceAttributes: [
"environment": .string("production"),
"team": .string("mobile")
],
customHeaders: [
("X-Custom-Header", "custom-value")
],
sessionBackgroundTimeout: 60,
isDebug: true
)
)
]
return config
}()After initialization of the LaunchDarkly iOS Client SDK, use LDObserve to record metrics, logs, errors, and traces:
import LaunchDarklyObservability
import OpenTelemetryApi
// Record metrics
LDObserve.shared.recordMetric(metric: .init(name: "user_actions", value: 1.0))
LDObserve.shared.recordCount(metric: .init(name: "api_calls", value: 1.0))
LDObserve.shared.recordIncr(metric: .init(name: "page_views", value: 1.0))
LDObserve.shared.recordHistogram(metric: .init(name: "response_time", value: 150.0))
LDObserve.shared.recordUpDownCounter(metric: .init(name: "active_connections", value: 1.0))
// Record logs
LDObserve.shared.recordLog(
message: "User performed action",
severity: .info,
attributes: [
"user_id": .string("12345"),
"action": .string("button_click")
]
)
// Record errors
LDObserve.shared.recordError(
error: paymentError,
attributes: [
"component": .string("payment"),
"error_code": .string("PAYMENT_FAILED")
]
)
// Create spans for tracing
let span = LDObserve.shared.startSpan(
name: "api_request",
attributes: [
"endpoint": .string("/api/users"),
"method": .string("GET")
]
)
span.end()We encourage pull requests and other contributions from the community. Check out our contributing guidelines for instructions on how to contribute to this SDK.
- LaunchDarkly is a continuous delivery platform that provides feature flags as a service and allows developers to iterate quickly and safely. We allow you to easily flag your features and manage them from the LaunchDarkly dashboard. With LaunchDarkly, you can:
- Roll out a new feature to a subset of your users (like a group of users who opt-in to a beta tester group), gathering feedback and bug reports from real-world use cases.
- Gradually roll out a feature to an increasing percentage of users, and track the effect that the feature has on key metrics (for instance, how likely is a user to complete a purchase if they have feature A versus feature B?).
- Turn off a feature that you realize is causing performance problems in production, without needing to re-deploy, or even restart the application with a changed configuration file.
- Grant access to certain features based on user attributes, like payment plan (eg: users on the ‘gold’ plan get access to more features than users in the ‘silver’ plan). Disable parts of your application to facilitate maintenance, without taking everything offline.
- LaunchDarkly provides feature flag SDKs for a wide variety of languages and technologies. Read our documentation for a complete list.
- Explore LaunchDarkly
- launchdarkly.com for more information
- docs.launchdarkly.com for our documentation and SDK reference guides
- apidocs.launchdarkly.com for our API documentation
- launchdarkly.com/blog for the latest product updates