AutoMobile iOS SDK¶
✅ Implemented 🧪 Tested
Current state:
ios/auto-mobile-sdk/is a Swift Package providing in-app telemetry for iOS applications. Includes navigation tracking (SwiftUI adapter), crash and signal handler capture, main-thread hang detection, handled exception recording, SwiftUI view body evaluation tracking, network request/response monitoring via URLProtocol, log filtering, tap interaction tracking, UserDefaults inspection, SQLite database inspection, biometric test injection, local notification posting, and OS-level event observation. Events flow through a disk-first persistence pipeline with retry delivery to CtrlProxy. 139 unit tests across 19 test files.
See the Status Glossary for chip definitions.
Architecture¶
flowchart TB
subgraph "Host App"
AppInit["App Init"]
end
subgraph "AutoMobileSDK"
SDK["AutoMobileSDK.shared"]
Config["AutoMobileConfiguration"]
Context["SdkContext"]
Session["SessionTracker"]
end
subgraph "Event Pipeline"
Buffer["SdkEventBuffer"]
Drop["DropCounter"]
Broadcaster["SdkEventBroadcaster"]
Persistence["FileEventPersistence"]
end
subgraph "Subsystems"
Nav["Navigation"]
Crashes["AutoMobileCrashes"]
Hangs["AutoMobileHangs"]
Failures["AutoMobileFailures"]
Net["AutoMobileNetwork"]
Log["AutoMobileLog"]
Interact["InteractionTracker"]
ViewBody["ViewBodyTracker"]
Storage["UserDefaults / Database"]
OsEvents["OsEvents / NotificationObserver"]
end
subgraph "Test Hooks"
Bio["AutoMobileBiometrics"]
Notif["AutoMobileNotifications"]
end
subgraph "Delivery"
NC["NotificationCenter"]
HTTP["CtrlProxy HTTP"]
end
AppInit -->|"initialize()"| SDK
SDK --> Config
SDK --> Context
SDK --> Session
SDK --> Buffer
Nav --> Buffer
Crashes --> Buffer
Hangs --> Buffer
Failures --> Buffer
Net --> Buffer
Log --> Buffer
Interact --> Buffer
ViewBody --> Buffer
Storage --> Buffer
OsEvents --> Buffer
Notif -.->|"action events"| Buffer
Buffer -->|"capacity or timer"| Broadcaster
Drop -.->|"overflow/error counts"| Buffer
Broadcaster --> Persistence
Broadcaster --> NC
Broadcaster -->|"POST /sdk-events"| HTTP
Persistence -.->|"replay on launch"| Broadcaster
style SDK fill:#CC2200,color:#fff
style Buffer fill:#525FE1,color:#fff
style Broadcaster fill:#525FE1,color:#fff
style Persistence fill:#525FE1,color:#fff
style HTTP fill:#007A3D,color:#fff
style NC fill:#007A3D,color:#fff
Components¶
| Component | Description | Status |
|---|---|---|
AutoMobileSDK |
Singleton entry point; initializes all subsystems and manages navigation listeners. | ✅ Implemented 🧪 Tested |
AutoMobileConfiguration |
Sendable struct with bufferSize, flushIntervalMs, maxBreadcrumbs, sessionTimeoutMs. Static .default property. |
✅ Implemented 🧪 Tested |
SdkContext |
Thread-safe mutable context (@unchecked Sendable) holding sessionId, userId, appVersion, and tags. Produces immutable SdkContextSnapshot. |
✅ Implemented 🧪 Tested |
SessionTracker |
Lifecycle-driven session management with configurable timeout. Uses TimerScheduling protocol for testability. |
✅ Implemented 🧪 Tested |
SdkEventBuffer |
Thread-safe ring buffer with capacity-triggered and timer-triggered flush. Integrates DropCounting. |
✅ Implemented 🧪 Tested |
SdkEventBroadcaster |
Disk-first broadcaster: persists events, posts to NotificationCenter, delivers via HTTP to CtrlProxy (debug builds). |
✅ Implemented 🧪 Tested |
DropCounter |
Tracks events lost due to disabled, shutdown, or flushError reasons. |
✅ Implemented 🧪 Tested |
FileEventPersistence |
JSON file-backed event persistence with FIFO replay, batch removal, and age-based cleanup. | ✅ Implemented 🧪 Tested |
BreadcrumbTrail |
Thread-safe ring buffer with configurable max size and disk persistence for crash recovery. | ✅ Implemented 🧪 Tested |
AutoMobileCrashes |
Installs NSSetUncaughtExceptionHandler and optional signal handlers (SIGABRT, SIGSEGV, SIGBUS, SIGFPE, SIGILL). Chains to previous handlers. Signal crash persistence via file marker. |
✅ Implemented 🧪 Tested |
AutoMobileHangs |
Main-thread hang detection via watchdog thread + DispatchSemaphore. Configurable threshold (default 2000ms) and poll interval (default 500ms). |
✅ Implemented 🧪 Tested |
AutoMobileFailures |
Records handled (non-fatal) exceptions with stack traces, device info, and screen context. Keeps last 100 events in memory. | ✅ Implemented 🧪 Tested |
NavigationEvent / NavigationListener |
Navigation event model with destination, source, arguments, metadata. Protocol + closure-based listener. |
✅ Implemented 🧪 Tested |
SwiftUINavigationAdapter |
NavigationFrameworkAdapter for SwiftUI NavigationStack. Includes .trackNavigation() ViewModifier. |
✅ Implemented 🧪 Tested |
AutoMobileNetwork |
URLSession monitoring via AutoMobileURLProtocol. Supports header/body capture (debug only for bodies), manual recording, and WebSocket frame tracking. |
✅ Implemented 🧪 Tested |
RetryPolicy |
Exponential backoff with jitter for failed HTTP delivery. Retries on network failure, 408, 429, 5xx. | ✅ Implemented 🧪 Tested |
AutoMobileLog |
Log filter engine with regex-based tag/message matching and minimum log level. Methods: v, d, i, w, e, fault. |
✅ Implemented 🧪 Tested |
AutoMobileInteractionTracker |
Tap tracking with 100ms debounce. Records coordinates, accessibility label/identifier, view type, and text. UIKit UITapGestureRecognizer convenience method. |
✅ Implemented 🧪 Tested |
ViewBodyTracker |
Tracks SwiftUI view body evaluations with rolling average and duration measurement. Includes .trackViewBody() modifier and MeasureViewBody wrapper. Periodic snapshot broadcast (1s). |
✅ Implemented 🧪 Tested |
UserDefaultsInspector |
Read/write UserDefaults with suite support, change listeners via UserDefaults.didChangeNotification, and SdkStorageChangedEvent emission. |
✅ Implemented 🧪 Tested |
DatabaseInspector / SQLiteDatabaseDriver |
SQLite database discovery, table listing, paginated data retrieval, schema inspection, and raw SQL execution via the SQLite3 C API. | ✅ Implemented 🧪 Tested |
AutoMobileBiometrics |
Test hook for injecting deterministic biometric authentication results with TTL-based expiry. | ✅ Implemented 🧪 Tested |
AutoMobileNotifications |
Posts local notifications via UNUserNotificationCenter with action buttons, image attachments, and delegate chaining for action tracking. |
✅ Implemented |
AutoMobileOsEvents |
Tracks lifecycle (foreground/background/inactive/terminated), battery level/charging, screen brightness, and network connectivity via NWPathMonitor. |
✅ Implemented 🧪 Tested |
AutoMobileNotificationObserver |
Monitors system notifications: locale change, timezone change, power state, memory warning, significant time change, screenshot taken. | ✅ Implemented 🧪 Tested |
Configuration¶
AutoMobileConfiguration is a Sendable struct with a static .default property:
swift
AutoMobileConfiguration(
bufferSize: 50, // Max events before flush
flushIntervalMs: 500, // Timer-based flush interval
maxBreadcrumbs: 100, // Ring buffer capacity
sessionTimeoutMs: 30_000 // Background timeout before new session
)
All values are clamped to a minimum of 1.
Initialization¶
```swift // Default configuration AutoMobileSDK.shared.initialize(bundleId: “com.example.app”)
// Custom configuration AutoMobileSDK.shared.initialize( bundleId: “com.example.app”, configuration: AutoMobileConfiguration(bufferSize: 100, flushIntervalMs: 1000) ) ```
initialize() is idempotent – subsequent calls are no-ops. It sets up:
SdkContextwithappVersionfromCFBundleShortVersionStringFileEventPersistenceinLibrary/Caches/automobile_events/SdkEventBufferwith configured capacity and flush interval- All subsystems (Log, Network, Failures, Crashes, Hangs, OsEvents, NotificationObserver, InteractionTracker, ViewBodyTracker, UserDefaultsInspector, DatabaseInspector)
SwiftUINavigationAdapterauto-startedSessionTrackerwithUIApplicationnotification observers for foreground/background transitions- Replay of pending event batches from previous sessions; cleanup of batches older than 7 days
Event Pipeline¶
Events flow through a three-stage pipeline:
-
Buffer –
SdkEventBuffercollects events from all subsystems. Flushes when capacity is reached or on a periodic timer. When the buffer is disabled or a flush fails, theDropCounterrecords the loss reason. -
Persist –
SdkEventBroadcasterwrites each batch to disk viaFileEventPersistencebefore attempting delivery. On next app launch,replayPending()retries any unsent batches. -
Deliver – Batches are posted to
NotificationCenter(in-process observers) and, in debug builds, sent via HTTP POST tohttp://localhost:8765/sdk-events(CtrlProxy). Failed HTTP deliveries are retried with exponential backoff perRetryPolicy.
Event types¶
All events conform to SdkEvent (Codable + Sendable) with a SdkEventType discriminator and millisecond-epoch timestamp. Supported types: navigation, handled_exception, crash, hang, network_request, websocket_frame, log, lifecycle, custom, notification_action, view_body_snapshot, broadcast, interaction, storage_changed.
Context¶
SdkContext is a thread-safe mutable store (@unchecked Sendable with NSLock) holding ambient state that can be attached to events:
sessionId– set automatically bySessionTrackeruserId– set viaAutoMobileSDK.shared.setUserId(_:)appVersion– set automatically fromBundle.main- Tags – arbitrary string key-value pairs via
setTag(_:value:)/removeTag(_:)
snapshot() produces an immutable SdkContextSnapshot (Codable, Sendable, Equatable).
Session Tracking¶
SessionTracker manages session lifecycle based on app foreground/background transitions:
- First foreground – generates a new session ID via
UUID().uuidString - Background – starts a timeout timer (default 30s, configurable via
sessionTimeoutMs) - Foreground while backgrounded – cancels timeout, resumes existing session
- Timeout fires while backgrounded – ends session, clears session ID
The TimerScheduling protocol (with GCDTimer default) allows injecting FakeTimer in tests.
Breadcrumbs¶
BreadcrumbTrail is a thread-safe ring buffer of Breadcrumb entries with categories: navigation, tap, lifecycle, network, log, custom.
Supports disk persistence for crash recovery:
- writeToDisk() serializes breadcrumbs to Library/Caches/automobile_breadcrumbs.json
- loadFromDisk() restores breadcrumbs from a previous session
- clearDisk() removes the persisted file
Crash and Hang Detection¶
AutoMobileCrashes¶
Captures unhandled crashes via two mechanisms:
-
NSSetUncaughtExceptionHandler – installed during
initialize(). Captures exception name, reason, and call stack symbols. Chains to any previously installed handler. -
Signal handlers (opt-in via
enableSignalHandlers()) – interceptsSIGABRT,SIGSEGV,SIGBUS,SIGFPE,SIGILL. The signal handler writes the signal number to a file using only async-signal-safe POSIX calls (open,write,close). On next launch,checkPreviousSignalCrash()reads the file and emits aSdkCrashEvent.SIGTRAPis excluded because it interferes with debuggers.
AutoMobileHangs¶
Main-thread hang detection (iOS equivalent of Android ANR detection):
- A background watchdog thread dispatches a semaphore signal to the main queue
- If the main queue does not respond within
hangThresholdMs(default 2000ms), a hang event is recorded - Polls every
pollIntervalMs(default 500ms) - Captures the watchdog thread’s stack as a diagnostic marker (iOS does not expose a public API for capturing another thread’s stack; MetricKit
MXHangDiagnosticis recommended for production)
AutoMobileFailures¶
Records non-fatal handled exceptions via recordHandledException(_:message:currentScreen:). Captures NSError domain, localized description, call stack symbols, device info, and optional screen context. Keeps the last 100 events in memory.
Navigation¶
Event model¶
NavigationEvent contains destination, source (NavigationSource enum: swiftUINavigation, uiKitNavigation, deepLink, custom), millisecond timestamp, string arguments, and metadata.
Listener pattern¶
NavigationListener is a protocol with onNavigationEvent(_:). BlockNavigationListener provides closure-based convenience. Listeners are registered on AutoMobileSDK.shared.
SwiftUI adapter¶
SwiftUINavigationAdapter implements NavigationFrameworkAdapter and provides:
trackNavigation(destination:arguments:metadata:)for manual tracking.trackNavigation(destination:)SwiftUIViewModifierthat fires ononAppear
Network Monitoring¶
AutoMobileNetwork provides two approaches:
-
URLProtocol –
AutoMobileURLProtocolintercepts all HTTP/HTTPS requests when registered viaURLSessionConfiguration.protocolClasses. Captures URL, method, status code, headers (opt-in), request/response bodies (debug-only, configurable max 32KB), duration, and content type. Uses ephemeral URLSession to avoid infinite recursion. -
Manual recording –
recordRequest(url:method:...)for cases where URLProtocol cannot be used.recordWebSocketFrame(url:direction:frameType:payloadSize:)for WebSocket tracking.
Body capture is always disabled in release builds to prevent leaking credentials or PII.
Logging¶
AutoMobileLog provides filter-based log capture. Filters are registered by name with optional regex patterns for tag and message, plus a minimum LogLevel (verbose, debug, info, warning, error, fault).
Log methods mirror standard log levels: v(), d(), i(), w(), e(), fault(). Only entries matching at least one active filter are buffered as SdkLogEvent.
Interaction Tracking¶
AutoMobileInteractionTracker records tap events with 100ms debounce. Each event captures:
- Screen coordinates (x, y)
- accessibilityLabel, accessibilityIdentifier
- viewType (class name)
- text (UILabel/UIButton text)
UIKit convenience: recordTap(from:in:) accepts a UITapGestureRecognizer and automatically inspects the view hierarchy via hitTest.
View Body Tracking¶
ViewBodyTracker tracks SwiftUI view body evaluations (iOS equivalent of Android’s RecompositionTracker for Compose):
recordBodyEvaluation(id:viewName:)increments evaluation count and maintains a 1-second rolling window for average calculationsrecordDuration(id:durationMs:)records composition duration.trackViewBody(id:)SwiftUIViewModifierfor automatic trackingMeasureViewBodywrapper view that measuresbodyevaluation duration viaCFAbsoluteTimeGetCurrent()- Periodic snapshot broadcast every 1 second when enabled
Storage Inspection¶
UserDefaultsInspector¶
Debug-time UserDefaults inspection (iOS equivalent of Android’s SharedPreferencesInspector):
UserDefaultsDriverprotocol withgetSuites(),getValues(),getValue(),setValue(),removeValue(),clear()- Write/clear operations are
#if DEBUGguarded - Change listeners via
UserDefaults.didChangeNotification - Emits
SdkStorageChangedEventwith sequence numbers on changes
DatabaseInspector / SQLiteDatabaseDriver¶
SQLite database inspection using the sqlite3 C API directly:
- Discovers
.sqlite,.db,.sqlite3files in Documents, Library, and Application Support directories - Table listing, paginated data retrieval, schema inspection via
PRAGMA table_info - Raw SQL execution with read/write detection (SELECT/EXPLAIN/PRAGMA = read-only, opens with
SQLITE_OPEN_READONLY) - Connection caching, 5-second busy timeout, identifier sanitization
Other Subsystems¶
AutoMobileBiometrics¶
Test hook for deterministic biometric testing. overrideResult(_:ttlMs:) sets a one-shot result (success, failure, cancel, or error) with TTL-based expiry (default 5s). consumeOverride() returns and clears the override atomically. Posts Notification.Name when override is set.
AutoMobileNotifications¶
Posts local notifications via UNUserNotificationCenter:
- Supports title, body, image attachments (local files or remote URLs downloaded to temp), and action buttons
- Registers UNNotificationCategory for actions, merging with existing app categories
- installActionHandler(on:) installs a UNUserNotificationCenterDelegate that chains to any previous delegate and emits SdkNotificationActionEvent on action taps
AutoMobileOsEvents¶
Tracks OS-level lifecycle events:
- App lifecycle: foreground, background, inactive, terminated (via UIApplication notifications)
- Battery: level and charging state changes (via UIDevice.batteryLevelDidChangeNotification)
- Screen: brightness changes (via UIScreen.brightnessDidChangeNotification)
- Connectivity: network status and transport type (via NWPathMonitor)
AutoMobileNotificationObserver¶
Monitors system NotificationCenter broadcasts:
- NSLocale.currentLocaleDidChangeNotification
- .NSSystemTimeZoneDidChange
- .NSProcessInfoPowerStateDidChange
- UIApplication.didReceiveMemoryWarningNotification
- UIApplication.significantTimeChangeNotification
- UIApplication.userDidTakeScreenshotNotification
Records SdkBroadcastEvent with notification action name and user info key types (not values, to avoid leaking sensitive data).
Thread Safety¶
All public classes use @unchecked Sendable with NSLock for thread safety. The pattern is consistent across the SDK:
```swift public final class Component: @unchecked Sendable { private let lock = NSLock() private var _state: State
public var state: State {
lock.lock()
defer { lock.unlock() }
return _state
}
} ```
All protocols extend AnyObject and Sendable to enable faking in tests while maintaining concurrency safety.
Testing¶
The SDK uses protocol + fake pattern throughout for testability:
EventBufferingprotocol with fake forSdkEventBufferTimerSchedulingprotocol withGCDTimer(production) and injectable fake timersSessionTrackingprotocol with injectable UUID provider and timer factoryDropCountingprotocol withDefaultDropCounterEventPersistingprotocol withFileEventPersistenceUserDefaultsDriverandDatabaseDriverprotocols with fakeable implementations
139 tests across 19 test files covering all subsystems. Test distribution: SdkContext (16), RetryPolicy (17), DropCounter (9), BreadcrumbTrail (9), Configuration (8), SDK integration (8), Navigation (8), Log (7), Biometrics (7), Storage (7), SessionTracker (6), EventBuffer (5), InteractionTracker (5), ViewBodyTracker (5), OsEvents (4), Concurrency (3), Failures (3), Network (3), EventPersistence (9).
Cross-Platform Parity¶
| Feature | Android | iOS | Notes |
|---|---|---|---|
| Singleton SDK | AutoMobileSDK |
AutoMobileSDK.shared |
Same pattern |
| Configuration | AutoMobileConfiguration |
AutoMobileConfiguration |
Same fields |
| Event buffer | SdkEventBuffer |
SdkEventBuffer |
Same capacity/timer design |
| Event persistence | FileEventPersistence |
FileEventPersistence |
Disk-first, replay on launch |
| Drop counter | DropCounter |
DefaultDropCounter |
Same reasons |
| Context | SdkContext |
SdkContext |
Same snapshot pattern |
| Session tracking | SessionTracker |
SessionTracker |
Lifecycle-based with timeout |
| Breadcrumbs | BreadcrumbTrail |
BreadcrumbTrail |
Ring buffer + disk |
| Navigation | Navigation3Adapter, CircuitAdapter | SwiftUINavigationAdapter | Framework-specific adapters |
| Crash detection | AutoMobileCrashes (UncaughtExceptionHandler) |
AutoMobileCrashes (NSSetUncaughtExceptionHandler + signals) |
iOS adds signal handler support |
| ANR / Hang detection | AutoMobileAnr |
AutoMobileHangs |
Watchdog thread approach |
| Handled exceptions | AutoMobileFailures |
AutoMobileFailures |
Same API shape |
| Network monitoring | OkHttp Interceptor | URLProtocol | Platform-native interception |
| WebSocket tracking | AutoMobileWebSocketListener |
recordWebSocketFrame() |
Manual on iOS |
| Log filtering | AutoMobileLog |
AutoMobileLog |
Regex filters |
| Interaction tracking | AutoMobileClickTracker |
AutoMobileInteractionTracker |
Tap with debounce |
| View rendering tracking | RecompositionTracker (Compose) |
ViewBodyTracker (SwiftUI) |
Platform-specific UI framework |
| Key-value storage | SharedPreferencesInspector |
UserDefaultsInspector |
Platform-native storage |
| Database inspection | DatabaseInspector / SQLiteDatabaseDriver |
DatabaseInspector / SQLiteDatabaseDriver |
Same schema |
| Biometric injection | AutoMobileBiometrics |
AutoMobileBiometrics |
Same override + TTL pattern |
| Notifications | AutoMobileNotifications |
AutoMobileNotifications |
Platform notification APIs |
| OS events | AutoMobileOsEvents |
AutoMobileOsEvents |
Lifecycle, battery, connectivity |
| Broadcast / System events | AutoMobileBroadcastInterceptor |
AutoMobileNotificationObserver |
BroadcastReceiver vs NotificationCenter |
| Network mock rules | NetworkMockRuleStore |
– | Android only |