> ## Documentation Index
> Fetch the complete documentation index at: https://launchdarkly-preview.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# iOS SDK observability reference

> This topic documents how to get started with the LaunchDarkly observability plugin for the iOS SDK.

> For the complete documentation index, see [llms.txt](/llms.txt). For the full documentation snapshot, see [llms-full.txt](/llms-full.txt).

<Note title="This LaunchDarkly observability plugin is available for early access">
  This LaunchDarkly observability plugin is currently available in Early Access, and APIs are subject to change until a 1.x version is released.
</Note>

## Overview

This topic documents how to get started with the LaunchDarkly observability plugin for the iOS SDK.

The iOS SDK supports the **observability plugin** for error monitoring, logging, and tracing.

<Note title="SDK quick links">
  LaunchDarkly's SDKs are open source. In addition to this reference guide, we provide source and a sample application:
</Note>

| Resource | Location |
| - | - |
| GitHub repository | [swift-launchdarkly-observability](https://github.com/launchdarkly/swift-launchdarkly-observability) |
| Published module | [Swift Package Manager](https://swift.org/package-manager/) |

## Prerequisites and dependencies

This reference guide assumes that you are somewhat familiar with the LaunchDarkly [iOS SDK](/sdk/client-side/ios).

The observability plugin is compatible with the [iOS SDK](/sdk/client-side/ios), version 9.14.0 and later, and is only available if you are using Swift.

## Get started

Follow these steps to get started:

* [Install the plugin](#install-the-plugin)
* [Initialize the iOS SDK client](#initialize-the-client)
* [Configure the plugin options](#configure-the-plugin-options)
* [Explore supported features](#explore-supported-features)
* [Review observability data in LaunchDarkly](#review-observability-data-in-launchdarkly)

## Install the plugin

LaunchDarkly uses a plugin to the iOS SDK to provide observability.

The first step is to make both the SDK and the observability plugin available as dependencies.

Here's how:

<CodeGroup>
  ```swift title="Package.swift, using Swift Package Manager" theme={null}
      //...
          dependencies: [
              .package(url: "https://github.com/launchdarkly/ios-client-sdk.git", .upToNextMinor("9.0.0")),
              .package(url: "https://github.com/launchdarkly/swift-launchdarkly-observability.git", .upToNextMinor("1.0.0")),
          ],
          targets: [
              .target(
                  name: "YOUR_TARGET",
                  dependencies: ["LaunchDarkly"]
              )
          ],
      //...
  ```

  ```ruby title="Podfile, using CocoaPods" theme={null}
      use_frameworks!
      target 'YourTargetName' do
        pod 'LaunchDarkly', '~> 9.0'
        pod 'LaunchDarklyObservability', '~> 1.0'
      end
  ```

  ```swift title="Cartfile, using Carthage" theme={null}
      github "launchdarkly/ios-client" ~> 9.0
      github "launchdarkly/swift-launchdarkly-observability" ~> 1.0
  ```
</CodeGroup>

Then, import the plugin into your code:

<CodeGroup>
  ```swift title="Swift" theme={null}
      import LaunchDarkly
      import LaunchDarklyObservability
      import OpenTelemetryApi
  ```
</CodeGroup>

## Initialize the client

Next, initialize the SDK and the plugin.

To initialize, you need your LaunchDarkly environment's mobile key. This authorizes your application to connect to a particular environment within LaunchDarkly. To learn more, read [Initialize the client](/sdk/client-side/ios#initialize-the-client) in the Android SDK reference guide.

Here's how to initialize the SDK and plugin:

<CodeGroup>
  ```swift title="iOS SDK v9.14+ (Swift)" theme={null}
      let config = LDConfig(mobileKey: "example-mobile-key", autoEnvAttributes: .enabled)
      config.plugins = [Observability()]

      let contextBuilder = LDContextBuilder(key: "example-context-key")
      guard case .success(let context) = contextBuilder.build()
      else { return }

      LDClient.start(config: config, context: context, startWaitSeconds: 5) { timedOut in
          if timedOut {
              // Client may not have the most recent flags for the configured context
          } else {
              // Client has received flags for the configured context
          }
      }
  ```
</CodeGroup>

## Configure the plugin options

You can configure options for the observability plugin when you initialize the SDK. The plugin constructor takes an optional object with the configuration details.

Here is an example:

<CodeGroup>
  ```swift title="Plugin options, iOS SDK v9.14+" theme={null}
      // Create configuration with custom options
      let configuration = Configuration(
          serviceName: "MyApp",
          otlpEndpoint: "https://otel.observability.app.launchdarkly.com:4318",
          serviceVersion: "1.2.3",
          resourceAttributes: [
              "environment": .string("production"),
              "team": .string("mobile-team"),
              "app.version": .string("1.2.3")
          ],
          customHeaders: [("Custom-Header", "header-value")],
          sessionTimeout: 30 * 60, // 30 minutes in seconds
          isDebug: false,
          isErrorTrackingDisabled: false,
          isLogsDisabled: false,
          isTracesDisabled: false,
          isMetricsDisabled: false
      )

      // Create the observability plugin with configuration
      let observabilityPlugin = Observability(configuration: configuration)

      let config = LDConfig(mobileKey: "example-mobile-key", autoEnvAttributes: .enabled)
      config.plugins = [observabilityPlugin]
  ```
</CodeGroup>

### Configuration options

The `Configuration` struct provides the following parameters:

* **serviceName**: The service name for the application. Defaults to "App".
* **otlpEndpoint**: The endpoint URL for the OTLP exporter. Defaults to LaunchDarkly's endpoint.
* **serviceVersion**: The service version for the application. Defaults to "1.0.0".
* **resourceAttributes**: Additional OpenTelemetry resource attributes to include in telemetry data.
* **customHeaders**: Custom headers to include with OTLP exports as key-value tuples.
* **sessionTimeout**: Session timeout in seconds. Defaults to 30 minutes (1800 seconds).
* **isDebug**: Enables additional logging for debugging. Defaults to false.
* **isErrorTrackingDisabled**: Disables automatic error tracking if true. Defaults to false.
* **isLogsDisabled**: Disables automatic log collection if true. Defaults to false.
* **isTracesDisabled**: Disables automatic trace collection if true. Defaults to false.
* **isMetricsDisabled**: Disables automatic metric collection if true. Defaults to false.

For more information on plugin options, read [Configuration for client-side observability](/sdk/features/observability-config-client-side).

## Manual instrumentation

After initializing the observability plugin, you can use the `LDObserve` singleton to manually instrument your iOS application with custom metrics, logs, traces, and error reporting.

### Recording custom metrics

<CodeGroup>
  ```swift title="Record metrics" theme={null}
      // Record a point-in-time metric
      LDObserve.shared.recordMetric(metric: Metric(name: "response_time_ms", value: 250.0))

      // Record metrics with attributes
      let attributes: [String: AttributeValue] = [
          "endpoint": .string("/api/users"),
          "method": .string("GET")
      ]
      LDObserve.shared.recordMetric(metric: Metric(
          name: "api_call_duration", 
          value: 120.5, 
          attributes: attributes
      ))

      // Record different metric types
      LDObserve.shared.recordCount(metric: Metric(name: "button_clicks", value: 1.0))
      LDObserve.shared.recordIncr(metric: Metric(name: "page_views", value: 1.0))
      LDObserve.shared.recordHistogram(metric: Metric(name: "request_size_bytes", value: 1024.0))
      LDObserve.shared.recordUpDownCounter(metric: Metric(name: "active_connections", value: 5.0))
  ```
</CodeGroup>

### Recording custom logs

<CodeGroup>
  ```swift title="Record logs" theme={null}
      // Record a basic log message
      LDObserve.shared.recordLog(
          message: "User login successful", 
          severity: .info, 
          attributes: [:]
      )

      // Record logs with custom attributes
      let logAttributes: [String: AttributeValue] = [
          "user_id": .string("12345"),
          "action": .string("login")
      ]
      LDObserve.shared.recordLog(
          message: "Authentication completed", 
          severity: .info, 
          attributes: logAttributes
      )
  ```
</CodeGroup>

### Recording custom errors

<CodeGroup>
  ```swift title="Record errors" theme={null}
      do {
          // Some operation that might fail
          try performNetworkRequest()
      } catch {
          // Record the error with context
          let errorAttributes: [String: AttributeValue] = [
              "operation": .string("network_request"),
              "endpoint": .string("/api/data")
          ]
          LDObserve.shared.recordError(error: error, attributes: errorAttributes)
      }
  ```
</CodeGroup>

### Recording custom traces

<CodeGroup>
  ```swift title="Record traces" theme={null}
      // Start a span and end it manually
      let attributes: [String: AttributeValue] = [
          "table": .string("users"),
          "operation": .string("select")
      ]
      let span = LDObserve.shared.startSpan(name: "database_query", attributes: attributes)

      // Perform your operation
      performDatabaseQuery()

      // Optionally add more attributes during execution
      span.setAttribute(key: "rows_returned", value: .int(42))

      // Always end the span
      span.end()
  ```
</CodeGroup>

### Using span builder for advanced tracing

<CodeGroup>
  ```swift title="Advanced span usage" theme={null}
      // Get span builder for more control
      let spanBuilder = LDObserve.shared.spanBuilder(spanName: "complex_operation")
          .setSpanKind(spanKind: .client)

      spanBuilder.setAttribute(key: "user.id", value: .string("12345"))
      let span = spanBuilder.startSpan()

      // Make the span current for nested operations
      span.makeCurrent()

      // Perform work that might create child spans
      performComplexWork()

      span.end()
  ```
</CodeGroup>

## Session management

The observability plugin automatically manages sessions and handles application lifecycle events. Sessions are automatically ended when:

* The application is backgrounded for longer than the configured `sessionTimeout` (default: 30 minutes)
* The application is terminated
* A new session is explicitly started

### Session timeout configuration

You can configure how long the plugin waits before ending a session when the app goes to the background:

<CodeGroup>
  ```swift title="Configure session timeout" theme={null}
      let configuration = Configuration(
          sessionTimeout: 45 * 60 // 45 minutes in seconds
      )
  ```
</CodeGroup>

## Automatic instrumentation

The observability plugin automatically instruments your iOS application to collect:

* **Application lifecycle events**: App start, foreground, background, and termination
* **Session tracking**: Automatic session start and end events with timing
* **Network requests**: HTTP request/response data when enabled
* **LaunchDarkly SDK events**: Feature flag evaluations and SDK operations

## Session Replay

<Note title="Session Replay is in Early Access">
  Session Replay is available in Early Access. APIs are subject to change until a 1.x version is released.
</Note>

Session Replay captures user interactions and screen recordings to help you understand how users interact with your application. Session Replay works as an additional plugin that requires the observability plugin to be configured first.

### Install the Session Replay plugin

First, add the Session Replay package as a dependency alongside the observability plugin:

<CodeGroup>
  ```swift title="Package.swift, using Swift Package Manager" theme={null}
      //...
          dependencies: [
              .package(url: "https://github.com/launchdarkly/ios-client-sdk.git", .upToNextMinor("9.0.0")),
              .package(url: "https://github.com/launchdarkly/swift-launchdarkly-observability.git", .upToNextMinor("1.0.0")),
          ],
          targets: [
              .target(
                  name: "YOUR_TARGET",
                  dependencies: [
                      "LaunchDarkly",
                      .product(name: "LaunchDarklySessionReplay", package: "swift-launchdarkly-observability")
                  ]
              )
          ],
      //...
  ```

  ```ruby title="Podfile, using CocoaPods" theme={null}
      use_frameworks!
      target 'YourTargetName' do
        pod 'LaunchDarkly', '~> 9.0'
        pod 'LaunchDarklyObservability', '~> 1.0'
        pod 'LaunchDarklySessionReplay', '~> 1.0'
      end
  ```
</CodeGroup>

Then, import the Session Replay plugin into your code:

<CodeGroup>
  ```swift title="Swift" theme={null}
      import LaunchDarkly
      import LaunchDarklyObservability
      import LaunchDarklySessionReplay
  ```
</CodeGroup>

### Initialize Session Replay

To enable Session Replay, add the `SessionReplay` plugin to your SDK configuration alongside the `Observability` plugin. The `Observability` plugin must be added before the `SessionReplay` plugin:

<CodeGroup>
  ```swift title="iOS SDK v9.14+ with Session Replay" theme={null}
      let mobileKey = "example-mobile-key"
      let 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"]
              )
          ))
      ]

      let contextBuilder = LDContextBuilder(key: "example-context-key")
      guard case .success(let context) = contextBuilder.build()
      else { return }

      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
              }
          }
      )
  ```
</CodeGroup>

### Initialize the plugins after the SDK client

You can initialize the session replay plugin manually, after the SDK client is initialized.

This approach supports feature-flagged rollouts or dynamic initialization after end user consent. Set `isEnabled` to `false` in `SessionReplayOptions`, then call `LDReplay.shared.start()` when you're ready to begin recording.

First, configure the plugin with `isEnabled: false`:

<CodeGroup>
  ```swift title="Manual start configuration, iOS SDK v9.14+" theme={null}
      let config = LDConfig(
          mobileKey: "example-mobile-key",
          autoEnvAttributes: .enabled
      )
      config.plugins = [
          Observability(),
          SessionReplay(options: .init(
              isEnabled: false, // Don't start recording automatically
              privacy: .init(
                  maskTextInputs: true
              )
          ))
      ]

      let contextBuilder = LDContextBuilder(key: "example-context-key")
      guard case .success(let context) = contextBuilder.build()
      else { return }

      LDClient.start(config: config, context: context, startWaitSeconds: 5)
  ```
</CodeGroup>

Then, start the session replay plugin when appropriate, such as after receiving end user consent or when a feature flag enables session replay.

<CodeGroup>
  ```swift title="Start recording" theme={null}
      // Start recording after user consent or feature flag check
      LDReplay.shared.start()
  ```

  ```swift title="Start recording with feature flag" theme={null}
      // Start recording based on a feature flag
      let replayEnabled = LDClient.get()?.boolVariation(forKey: "enable-session-replay", defaultValue: false) ?? false
      if replayEnabled {
          LDReplay.shared.start()
      }
  ```

  ```swift title="Stop recording" theme={null}
      LDReplay.shared.stop()
  ```
</CodeGroup>

This approach allows you to:

* Feature-flag the rollout of session replay to a subset of end users
* Wait for end user consent before starting data collection
* Dynamically enable session replay based on runtime conditions
* Maintain compliance with privacy regulations

### Configure Session Replay privacy options

The Session Replay plugin provides privacy options to control what data is captured. Configure these options when initializing the plugin:

<CodeGroup>
  ```swift title="Session Replay privacy options" theme={null}
      SessionReplay(options: .init(
          isEnabled: true,
          serviceName: "my-swift-app",
          privacy: .init(
              maskTextInputs: true,
              maskWebViews: false,
              maskLabels: false,
              maskImages: false,
              maskUIViews: [SensitiveView.self],
              ignoreUIViews: [PublicView.self],
              maskAccessibilityIdentifiers: ["email-field", "password-field"],
              ignoreAccessibilityIdentifiers: ["public-label"],
              minimumAlpha: 0.02
          )
      ))
  ```
</CodeGroup>

#### Privacy configuration options

The `PrivacyOptions` struct provides the following parameters:

* **maskTextInputs**: Mask all text input fields. Defaults to `true`.
* **maskWebViews**: Mask the contents of web views (`WKWebView` and `UIWebView`). When this setting is enabled, web views are rendered as blank rectangles in session replays. Defaults to `false`.
* **maskLabels**: Mask all text labels. Defaults to `false`.
* **maskImages**: Mask all images. Defaults to `false`.
* **maskUIViews**: Array of `UIView` classes to automatically mask in recordings.
* **ignoreUIViews**: Array of `UIView` classes to exclude from masking rules.
* **maskAccessibilityIdentifiers**: Array of accessibility identifiers to mask. Use this to mask specific UI elements by their accessibility identifier.
* **ignoreAccessibilityIdentifiers**: Array of accessibility identifiers to exclude from masking rules.
* **minimumAlpha**: Minimum alpha value for view visibility in recordings. Views with alpha below this threshold are not captured. Defaults to `0.02`.

### Fine-grained masking control

You can override the default privacy settings on individual views using the `.ldPrivate()` and `.ldUnmask()` methods. This allows precise control over what is captured in session replays.

#### Swift

UI view masking

Use view modifiers to control masking for SwiftUI views:

<CodeGroup>
  ```swift title="SwiftUI masking control" theme={null}
      import SwiftUI
      import SessionReplay

      struct ContentView: View {
          @State private var email = ""
          @State private var shouldMaskEmail = true

          var body: some View {
              VStack {
                  // Mask this specific view
                  Text("Sensitive information")
                      .ldPrivate()

                  // 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)
              }
          }
      }
  ```
</CodeGroup>

#### UIKit view masking

Use the `.ldPrivate()` and `.ldUnmask()` methods on UIView instances:

<CodeGroup>
  ```swift title="UIKit masking control" theme={null}
      import UIKit
      import SessionReplay

      class CreditCardViewController: UIViewController {
          let cvvField = UITextField()
          let nameField = UITextField()
          let cardNumberField = UITextField()

          override func viewDidLoad() {
              super.viewDidLoad()

              // Mask the CVV field
              cvvField.ldPrivate()

              // Unmask the name field (even if text inputs are masked by default)
              nameField.ldUnmask()

              // Conditionally mask based on a flag
              cardNumberField.ldPrivate(isEnabled: true)
          }
      }
  ```
</CodeGroup>

## Explore supported features

The observability plugin supports the following features. After the SDK and plugins are initialized, you can access these from within your application:

* [Configuration for client-side observability](/sdk/features/observability-config-client-side)
* [Errors](/sdk/features/observability-errors#ios)
* [Logs](/sdk/features/observability-logs#ios)
* [Metrics](/sdk/features/observability-metrics#ios)
* [Tracing](/sdk/features/observability-traces#ios)

## Review observability data in LaunchDarkly

After you initialize the SDK and observability plugin, your application automatically starts sending observability data back to LaunchDarkly in the form of custom events and OpenTelemetry data. You can review this information in the LaunchDarkly user interface. To learn how, read [Observability](/home/observability).

The observability data collected includes:

* **Error monitoring**: Unhandled exceptions, crashes, and manually recorded errors with stack traces
* **Logs**: Application logs with configurable severity levels and custom attributes
* **Traces**: Distributed tracing data including span timing, nested operations, and custom instrumentation
* **Metrics**: Performance metrics, custom counters, histograms, and gauge measurements
* **Session data**: User session information including lifecycle events and timing

Specifically, the observability data includes events that LaunchDarkly uses to automatically create the following metrics:

* User error rate and crash frequency
* Application performance metrics such as launch time and session duration
* Feature flag evaluation context and timing
* Custom business metrics recorded through the SDK

To learn more about autogenerated metrics, read [Metrics autogenerated from observability events](/home/metrics/autogen/observability).
