Abrova
☰ Docs menu · iOS SDK

iOS SDK

Native Swift SDK for iOS, macOS, and tvOS with crash detection, ANR detection, URLSession tracking, and performance monitoring.

iOS 0.2.4iOS 12+macOS 10.14+Swift 5.10Last updated: 27 Oct 2026
Native Swift SDK

Built with Swift, supports iOS 12+, macOS 10.14+, tvOS 12+ (tvOS via Swift Package Manager only)

Automatic Crash Detection

Catches uncaught exceptions, plus native signal crashes (SIGSEGV, SIGABRT, etc.) and Mach exceptions via PLCrashReporter

ANR Detection

Detect Application Not Responding events (main thread blocked)

Breadcrumbs & Navigation

Track user journey before errors

HTTP Tracking

Automatic HTTP performance spans and breadcrumbs

Custom Messages

Send debug, info, warning, and error messages

Performance Monitoring

Track API latency with URLSession and Alamofire

Rich Device Context

Automatic device, OS, and app info collection

Installation#

Swift Package Manager (Recommended)

In Xcode: File → Add Packages → enter the Abrova Center address:

text
https://center.abrova.ir/abrova/abrova-trace-ios-sdk.git

Select version 0.2.4 or later.

Or add to your Package.swift:

swift
dependencies: [
    .package(url: "https://center.abrova.ir/abrova/abrova-trace-ios-sdk.git", from: "0.2.4")
]

CocoaPods

Add to your Podfile:

ruby
pod 'AbrovaTrace', :git => 'https://center.abrova.ir/abrova/abrova-trace-ios-sdk.git', :tag => '0.2.4'

Then run:

bash
pod install
Abrova Center and GitHub.

Releases are installed from Abrova Center, which works inside Iran. The source code is on GitHub (Abrova-co/abrova-trace-ios-sdk) with the same version tags, so https://github.com/Abrova-co/abrova-trace-ios-sdk.git also works where GitHub is reachable.

Quick Start#

UIKit Initialization

Initialize AbrovaTrace in your AppDelegate:

swift
import UIKit
import AbrovaTrace

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        let config = AbrovaTraceConfigBuilder(apiKey: "ab_live_YOUR_API_KEY")
            .environment("production")
            .release(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String)
            .debug(false)
            .captureUncaughtExceptions(true)
            .captureSignalCrashes(true)
            .captureAnr(true)
            .anrTimeoutMs(5000)
            .build()

        AbrovaTrace.shared.initialize(config: config)

        return true
    }

    func applicationWillTerminate(_ application: UIApplication) {
        AbrovaTrace.shared.close()  // Optional: the SDK also does this on terminate
    }
}

SwiftUI Initialization

swift
import SwiftUI
import AbrovaTrace

@main
struct MyApp: App {
    init() {
        let config = AbrovaTraceConfigBuilder(apiKey: "ab_live_YOUR_API_KEY")
            .environment("production")
            .release(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String)
            .debug(false)
            .captureUncaughtExceptions(true)
            .captureSignalCrashes(true)
            .build()

        AbrovaTrace.shared.initialize(config: config)
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

Capture Errors#

Capture Swift Errors

swift
do {
    try riskyOperation()
} catch {
    AbrovaTrace.shared.captureError(error)
}

Use captureError(_:) for Swift Error values and captureException(_:) for an NSException.

Capture with Context

swift
AbrovaTrace.shared.captureError(
    error,
    message: "Failed to load user profile",
    extra: ["user_id": userId, "screen": "ProfileView"],
    tags: ["feature": "user_profile"]
)

Capture Messages

swift
// Info message
AbrovaTrace.shared.captureMessage("User completed onboarding", level: .info)

// Warning message
AbrovaTrace.shared.captureMessage("API rate limit approaching", level: .warning)

// Error message with extra data
AbrovaTrace.shared.captureMessage(
    "Payment failed",
    level: .error,
    extra: ["amount": 99.99]
)
Where messages show up.

Messages appear in the Console under Abrova Trace → Messages, with their level, platform, extra data and breadcrumbs. They never create an issue. Levels: .debug, .info (default), .warning, .error. .fatal is accepted and stored as error. Use messages for things your app reports on purpose ("checkout started", "sync finished"); for high-volume diagnostics use Logs.

swift
// User action
AbrovaTrace.shared.addUserBreadcrumb("Added item to cart", data: ["product_id": "abc"])

// Navigation
AbrovaTrace.shared.addNavigationBreadcrumb(from: "HomeView", to: "ProductDetailView")

// HTTP request
AbrovaTrace.shared.addHttpBreadcrumb(
    method: "POST",
    url: "https://api.example.com/orders",
    statusCode: 201,
    duration: 0.342  // seconds (TimeInterval)
)

// Custom breadcrumb
AbrovaTrace.shared.addBreadcrumb("Order placed", type: .info, data: [
    "order_id": "12345",
    "total": 99.99
])

Automatic HTTP Tracking#

Global HTTP Tracking

Enable automatic tracking for URLSession requests. Each request is reported as a performance span and added as an HTTP breadcrumb:

swift
// Registers AbrovaTraceURLProtocol globally (URLSession.shared and default-configured sessions)
AbrovaTrace.shared.enablePerformanceTracking()

// Requests are now tracked automatically
let (data, response) = try await URLSession.shared.data(from: url)

Session-Based Tracking

swift
// Create a tracked URLSession
let config = AbrovaTrace.shared.performanceSessionConfiguration()
// Equivalent to:
// config.protocolClasses = [AbrovaTraceURLProtocol.self] + (config.protocolClasses ?? [])
let session = URLSession(configuration: config)

// Requests through this session are tracked
let task = session.dataTask(with: url) { data, response, error in
    // Handle response
}
task.resume()
Privacy.

Performance spans from enablePerformanceTracking() record the full request URL, including query strings, so avoid putting secrets in URLs or exclude those hosts with setPerformanceExcludedHosts(_:). The separate AbrovaTraceHttpBreadcrumbProtocol.enable() adds HTTP breadcrumbs with sensitive query parameters (token, api_key, apikey, key, secret, password, pwd, auth, authorization, access_token, refresh_token, session) redacted, and reports 5xx responses and failed requests as messages.

Crash Detection#

AbrovaTrace captures crashes with a vendored copy of PLCrashReporter:

  • Uncaught NSExceptions — Sent immediately, before the app exits (captureUncaughtExceptions)
  • ANR Events — Main thread blocked longer than anrTimeoutMs (captureAnr)
  • Native Crashes — POSIX signals (SIGSEGV, SIGABRT, SIGBUS, SIGILL, SIGFPE, SIGTRAP) and Mach exceptions (captureSignalCrashes)
  1. On initialization, AbrovaTrace installs PLCrashReporter's async-signal-safe handlers
  2. When the app crashes, a crash report is written to disk inside the app sandbox
  3. On the next launch, initialize reads the pending report and sends it to Abrova Trace in the background, so app launch never waits for the network. The report is removed only after it was delivered; if the server or the network fails it is kept and sent on a later launch. Requires version 0.2.3 or newer.
Important.

Native crashes are reported on the next launch, so they only arrive once the user opens the app again. The SDK calls AbrovaTrace.shared.close() itself when it receives the app's will-terminate notification; calling it yourself is optional and flushes buffered logs.

Performance Monitoring (APM)#

Track API latency and HTTP request performance.

Automatic HTTP Tracking with URLSession

swift
// Enable performance tracking globally
AbrovaTrace.shared.enablePerformanceTracking()

// All URLSession requests are now tracked with timing data
let (data, response) = try await URLSession.shared.data(from: url)
// Method, URL, status code, and duration are automatically reported

Using with Alamofire

swift
import Alamofire

// Create a session with AbrovaTrace tracking
let configuration = URLSessionConfiguration.default
configuration.protocolClasses = [AbrovaTraceURLProtocol.self] + (configuration.protocolClasses ?? [])

let session = Session(configuration: configuration)

// All requests through this session are tracked
session.request("https://api.example.com/users").response { response in
    // Handle response
}

Track Custom Operations

swift
// Async (iOS 13+); use PerformanceTracker.track(_:) for synchronous work
let result = try await PerformanceTracker.trackAsync("load_user_profile") {
    let user = try await fetchUser()
    try await cacheUser(user)
    return user
}

// Or time it manually
let tracker = PerformanceTracker(operation: "image_processing")
// ... do work
tracker.finish(status: .ok)

Manual Span Reporting

swift
// HTTP request span
AbrovaTrace.shared.reportPerformanceSpan(
    PerformanceSpan.fromHttpRequest(
        method: "GET",
        url: "https://api.example.com/products",
        statusCode: 200,
        durationMs: 245,
        startTime: Date().addingTimeInterval(-0.245)
    )
)

// Database query span
AbrovaTrace.shared.reportPerformanceSpan(
    PerformanceSpan.forDbQuery(
        queryType: "SELECT",
        tableName: "products",
        durationMs: 15,
        startTime: Date().addingTimeInterval(-0.015),
        rowsAffected: 50
    )
)

// Custom operation span
AbrovaTrace.shared.reportPerformanceSpan(
    PerformanceSpan.custom(
        operation: "image_processing",
        durationMs: 1250,
        startTime: Date().addingTimeInterval(-1.25),
        tags: ["format": "png"]
    )
)

Logging#

Structured log ingestion with automatic batching and retry. Enable it once, then use the level helpers from anywhere in your app:

swift
// Enable logging after initialization
AbrovaTrace.shared.enableLogging(sourceId: "my-ios-app")

AbrovaTrace.shared.info("User login successful", metadata: ["user_id": "user_123"])

See iOS SDK Logging for the full pipeline — batching, log levels, lifecycle handling, manual flush, and configuration.

Configuration Options#

OptionTypeRequiredDescription
apiKeyStringYesYour project API key (must start with ab_live_)
environmentStringNoEnvironment name (default: "production")
releaseString?NoApp version identifier (default: CFBundleShortVersionString)
debugBoolNoEnable debug logging (default: false)
enabledBoolNoEnable/disable SDK (default: true)
sampleRateDoubleNoError capture sample rate 0.0-1.0 (default: 1.0)
maxBreadcrumbsIntNoMax breadcrumbs to keep (default: 20)
httpTimeoutTimeIntervalNoHTTP request timeout in seconds (default: 30)
enableOfflineStorageBoolNoKeep errors that could not be sent because the device is offline on disk (up to 100, oldest dropped first) and send them on the next launch or as soon as another error is sent successfully. Errors only: messages, logs and performance data are not stored. Set to false to keep nothing on disk (default: true)
captureUncaughtExceptionsBoolNoCatch uncaught exceptions (default: true)
captureSignalCrashesBoolNoDetect signal crashes (default: true)
captureAnrBoolNoDetect main thread hangs (default: true)
anrTimeoutMsInt64NoANR detection timeout in ms (default: 5000)
apiUrlStringNoAPI base URL, for self-hosted deployments (default: "https://trace.abrova.ir")

Upload Debug Symbols (dSYM)#

To see file names and line numbers in your iOS stack traces, upload your app's dSYM files. Each build produces unique dSYMs — upload a new one for every release version.

1. Find your dSYM files

After building your app in Xcode, dSYMs are located in your build products:

bash
# For debug builds (simulator)
~/Library/Developer/Xcode/DerivedData/YourApp-*/Build/Products/Debug-iphonesimulator/YourApp.app.dSYM

# For release/archive builds
~/Library/Developer/Xcode/Archives/*.xcarchive/dSYMs/

2. Zip the dSYM bundle

bash
# Zip the .dSYM bundle
cd ~/Library/Developer/Xcode/DerivedData/YourApp-*/Build/Products/Debug-iphonesimulator/
zip -r YourApp.dSYM.zip YourApp.app.dSYM

3. Upload via Dashboard

In the Abrova Console, go to Abrova Trace → Source maps and open the iOS dSYMs tab. Drop the .zip file, optionally enter the release version, and click Upload. Uploading needs the member role or higher; deleting uploaded dSYMs needs admin or owner — see Roles & Permissions.

The dSYM upload endpoint accepts only a Console login session (no API key or personal access token), and @abrova-trace/cli uploads source maps only, so upload dSYMs from the Console.

Important.

Each time you rebuild your app, the dSYM UUIDs change. If stack traces show function names but no file/line info, you need to upload the dSYM matching that build.

Platform Support#

  • iOS — 12.0+
  • macOS — 10.14+
  • tvOS — 12.0+ (Swift Package Manager only; the CocoaPods spec covers iOS and macOS)

watchOS is not supported. Building the SDK requires Swift 5.10 or later (Xcode 15.3+).

Context & Tags#

Attach persistent metadata to every error and message captured by the SDK (native crash and ANR reports, logs and performance spans do not carry them). Useful for filtering by team, region, feature flag, or experiment cohort in the Abrova Console.

Global extras

Extras are arbitrary key/value pairs (string keys, any serializable value) automatically merged into every event:

swift
// Set individual extras
AbrovaTrace.shared.setExtra("organizationId", value: "org-123")
AbrovaTrace.shared.setExtra("feature", value: "checkout-v2")

// Set multiple at once
AbrovaTrace.shared.setExtras([
    "organizationId": "org-123",
    "feature": "checkout-v2",
    "experiment": "new-flow"
])

// Clear when no longer needed (e.g. on logout)
AbrovaTrace.shared.clearExtras()

Tags

Tags are string-only key/value pairs for categorizing events:

swift
AbrovaTrace.shared.setTag("team", value: "payments")
AbrovaTrace.shared.setTag("region", value: "us-east")

AbrovaTrace.shared.setTags(["team": "payments", "version": "2.1"])

AbrovaTrace.shared.clearTags()
Tags vs Extras.

Use tags for low-cardinality categorical filters (team, region, build flavor). Use extras for richer per-event context (user IDs, request bodies, computed values).

SwiftUI Integration#

The SDK works seamlessly with the SwiftUI @main app entry point. Initialize from the App struct's init() so errors are captured from the moment your view hierarchy is constructed:

swift
import SwiftUI
import AbrovaTrace

@main
struct MyApp: App {

    init() {
        let config = AbrovaTraceConfigBuilder(apiKey: "ab_live_your_api_key")
            .environment("production")
            .build()
        AbrovaTrace.shared.initialize(config: config)
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
UIKit?

For UIKit apps, initialize from application(_:didFinishLaunchingWithOptions:) in your AppDelegate — see the Complete Example below.

API Reference#

Full method surface of AbrovaTrace.shared and PerformanceSpan. Selectors use the Swift convention; optional parameters end with ? in the description.

AbrovaTrace.shared — Initialization & lifecycle

OptionTypeRequiredDescription
initialize(config:)--Initialize SDK with a full AbrovaTraceConfig
initialize(apiKey:)--Initialize SDK with just an API key (uses defaults)
isReady--True once the SDK is initialized
close()--Cleanup SDK, flush pending data

Error capture

OptionTypeRequiredDescription
captureError(_:message:extra:tags:completion:)--Capture a Swift Error
captureException(_:message:extra:tags:completion:)--Capture an NSException
captureMessage(_:level:extra:tags:completion:)--Send a message to Abrova Trace → Messages (not an issue)

User & session

OptionTypeRequiredDescription
setUserId(_:)--Set the current user identifier
getUserId()--Get the current user identifier

Global context (extras)

OptionTypeRequiredDescription
setExtra(_:value:)--Set a single extra
setExtras(_:)--Set multiple extras at once
clearExtras()--Clear all extras

Tags

OptionTypeRequiredDescription
setTag(_:value:)--Set a single tag
setTags(_:)--Set multiple tags at once
clearTags()--Clear all tags

Breadcrumbs

OptionTypeRequiredDescription
addBreadcrumb(_:type:data:)--Add a generic breadcrumb
addNavigationBreadcrumb(from:to:)--Add a navigation breadcrumb
addUserBreadcrumb(_:data:)--Add a user-action breadcrumb
addHttpBreadcrumb(method:url:statusCode:duration:)--Add an HTTP breadcrumb
clearBreadcrumbs()--Clear all breadcrumbs

Performance monitoring

OptionTypeRequiredDescription
reportPerformanceSpan(_:completion:)--Report a constructed PerformanceSpan
reportPerformanceSpanBatch(_:completion:)--Report multiple spans at once
trackHttpRequest(request:response:startTime:error:)--Track a one-off HTTP request
enablePerformanceTracking()--Turn on automatic HTTP tracking
disablePerformanceTracking()--Turn off automatic HTTP tracking
performanceSessionConfiguration(baseConfiguration:)--Return a URLSessionConfiguration with tracking attached
setPerformanceExcludedHosts(_:)--Exclude hosts from automatic tracking
setPerformanceMinDuration(_:)--Set minimum span duration threshold

Logging

OptionTypeRequiredDescription
enableLogging(sourceId:sourceName:batchSize:flushInterval:)--Turn on the log pipeline
log(_:level:metadata:)--Log at an explicit level
trace(_:metadata:)--Log at trace level
logDebugMessage(_:metadata:)--Log at debug level
info(_:metadata:)--Log at info level
warn(_:metadata:)--Log at warn level
logErrorMessage(_:metadata:)--Log at error level
fatal(_:metadata:)--Log at fatal level
flushLogs(completion:)--Flush buffered logs immediately
pendingLogCount--Number of logs still in the buffer

PerformanceSpan factories

OptionTypeRequiredDescription
PerformanceSpan.fromHttpRequest(...)--Construct a span from an HTTP request/response
PerformanceSpan.forDbQuery(...)--Construct a span for a database query
PerformanceSpan.custom(...)--Construct a fully custom span
PerformanceSpan.generateTraceId()--Generate a random trace ID
PerformanceSpan.generateSpanId()--Generate a random span ID

Complete Example#

swift
import UIKit
import AbrovaTrace

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        let config = AbrovaTraceConfigBuilder(apiKey: "ab_live_YOUR_API_KEY")
            .environment("production")
            .release(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String)
            .debug(false)
            .captureUncaughtExceptions(true)
            .captureSignalCrashes(true)
            .captureAnr(true)
            .build()

        AbrovaTrace.shared.initialize(config: config)
        AbrovaTrace.shared.enablePerformanceTracking()

        return true
    }

    func applicationWillTerminate(_ application: UIApplication) {
        AbrovaTrace.shared.close()
    }
}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        AbrovaTrace.shared.setUserId("user_123")
        AbrovaTrace.shared.addNavigationBreadcrumb(from: "Launch", to: "HomeView")
    }

    func handlePurchase() {
        AbrovaTrace.shared.addUserBreadcrumb("Initiated purchase", data: ["product": "Premium"])

        do {
            try processPurchase()
            AbrovaTrace.shared.captureMessage("Purchase successful", level: .info, extra: [
                "amount": 99.99
            ])
        } catch {
            AbrovaTrace.shared.captureError(
                error,
                message: "Purchase failed",
                extra: ["user_id": "user_123", "screen": "HomeView"]
            )
        }
    }

    func processPurchase() throws { /* ... */ }
}

Best Practices#

Initialize in AppDelegate or App init

Initialize AbrovaTrace as early as possible to catch all errors from app start

close() on App Termination

The SDK calls close() on the will-terminate notification to flush logs; call it yourself only for custom shutdown paths

Enable HTTP Tracking

Call enablePerformanceTracking() for automatic HTTP breadcrumbs and performance monitoring

Add Context to Errors

Include user IDs, screen names, and relevant data in the extra parameter

Use Different Environments

Separate production, staging, and development errors using the environment config

Enable Debug Mode in Development

Set debug: true during development to see SDK logs and verify integration

Enable Logging Early

Call enableLogging() right after initialization to capture all application events

Was this page helpful?
Report a problem