Swift / iOS SDK
Native iOS analytics — SwiftUI and UIKit screen tracking, deep links, crash capture, push notifications, and A/B experiments.
The Swift SDK uses FlashAnalytics.configureShared() at app launch and provides FlashAnalytics.shared as the global instance. It supports both SwiftUI (via view modifiers) and UIKit (via automatic swizzling).
Prerequisites
iOS 15.0 / macOS 12.0https://github.com/NextGenCreativeSolutions/flashanalytics-swift.gitfrom: "1.1.1"Installation
Add the Swift Package
In Xcode: File → Add Package Dependencies and paste the repository URL. Or add to your Package.swift:
.package(
url: "https://github.com/NextGenCreativeSolutions/flashanalytics-swift.git",
from: "1.1.1"
)Initialize in your App entry point
import SwiftUI
import FlashAnalytics
@main
struct MyApp: App {
init() {
_ = FlashAnalytics.configureShared(
options: FlashAnalyticsOptions(
appId: "YOUR_APP_ID", // UUID from your Flash dashboard
captureAppLifecycle: true,
captureScreenViews: true,
captureDeepLinks: true,
captureInstallAttribution: true,
captureNativeCrashes: true,
captureVariants: CaptureVariantsOptions(),
batchEnable: true,
batchSize: 10,
batchTimeoutMs: 5_000
)
)
}
var body: some Scene {
WindowGroup { ContentView() }
}
}appId in a configuration file or environment variable. Never hardcode secrets in source code.Identify users and track events
let analytics = FlashAnalytics.shared
// After login
analytics.identify(
IdentifyPayload(
profileId: "user_123",
email: "user@example.com"
)
)
// Track a product event
analytics.track("subscription_started", properties: [
"plan": "pro",
"price": 49.99
])SwiftUI screen tracking
Add the .flashAnalyticsScreen modifier to any SwiftUI view to track screen views automatically.
import FlashAnalytics
struct ProductView: View {
let productId: String
var body: some View {
ProductDetails(productId: productId)
.flashAnalyticsScreen(FlashAnalytics.shared, path: "/product/(productId)")
}
}Auto-capture flags
captureAppLifecycleapp_opened, app_foregrounded, app_backgrounded
captureScreenViewsUIKit screens auto-swizzled, SwiftUI via modifier
captureDeepLinksdeep_link_opened via handleOpenURL / handleUserActivity
captureInstallAttributionInstall / update attribution
captureNativeCrashesnative_crash report on next launch
captureViewInteractionsUIKit control interaction tracking
capturePushLifecyclePush notification lifecycle helpers
captureVariantsA/B experiment auto-assignment
Deep links
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL { url in
FlashAnalytics.shared.handleOpenURL(url)
}
.onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { activity in
FlashAnalytics.shared.handleUserActivity(activity)
}
}
}
}Error tracking
do {
try riskyOperation()
} catch {
analytics.trackError(
error,
eventName: "payment_failed",
properties: ["orderId": orderId]
)
}
// Track a string error
analytics.trackError("Unexpected nil in user profile")Experiments
// All cached assignments — no API call
let all = analytics.getAllExperiments()
// Cache first, API fallback (async)
let assignment = await analytics.getExperimentById(experimentId: "checkout-cta")
print(assignment?.variantName ?? "nil")
// Manual refresh
let assignments = await analytics.autoAssignExperiments()Global properties
analytics.setGlobalProperties([
"appVersion": "1.2.0",
"plan": "pro",
])Push notifications
Push tracking is split across two native targets. The table below shows what each event requires:
| Event | Where | Requires |
|---|---|---|
notification_delivered | Service Extension | UNNotificationServiceExtension + mutable-content: 1 |
notification_expired | Service Extension | UNNotificationServiceExtension timeout callback |
notification_opened | App target | UNUserNotificationCenterDelegate |
notification_dismissed | App target | UNUserNotificationCenterDelegate |
notification_action_clicked | App target | UNUserNotificationCenterDelegate + category actions |
Step 1 — Push payload
Your push payload must include mutable-content: 1 so iOS invokes the service extension before delivery.
{
"aps": {
"alert": { "title": "Your order shipped!", "body": "Tap to track." },
"mutable-content": 1
},
"notificationId": "notif_abc123",
"messageId": "msg_xyz",
"provider": "apns"
}Step 2 — Notification Service Extension (delivery + expiry)
In Xcode: File → New → Target → Notification Service Extension. Add the Flash Analytics package to the extension target, then implement:
import UserNotifications
import FlashAnalytics
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
var extensionAnalytics: FlashAnalytics?
override func didReceive(
_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
) {
self.contentHandler = contentHandler
bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContent
// Initialize a dedicated instance for the extension — it runs in a
// separate process from the app target and cannot share the shared instance.
extensionAnalytics = FlashAnalytics.configureShared(
options: FlashAnalyticsOptions(
appId: "YOUR_APP_ID",
endpoint: "https://api.flashanalytics.app",
capturePushLifecycle: true
)
)
// Track delivery — fires notification_delivered
extensionAnalytics?.trackNotificationDelivered(request: request)
contentHandler(bestAttemptContent ?? request.content)
}
override func serviceExtensionTimeWillExpire() {
// Fires notification_expired when the extension's processing time runs out
if let request = bestAttemptContent {
extensionAnalytics?.trackNotificationExpired(content: request)
}
contentHandler?(bestAttemptContent ?? UNNotificationContent())
}
}Step 3 — UNUserNotificationCenterDelegate (open + dismiss + action)
In your app target, set the delegate and forward responses to the SDK. With SwiftUI use @UIApplicationDelegateAdaptor.
import UIKit
import UserNotifications
import FlashAnalytics
class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
UNUserNotificationCenter.current().delegate = self
// Request permission
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { _, _ in }
application.registerForRemoteNotifications()
return true
}
// Fires notification_opened, notification_dismissed, or notification_action_clicked
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
FlashAnalytics.shared.trackNotificationResponse(response)
completionHandler()
}
// Controls how notifications appear when the app is in the foreground
func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
completionHandler([.banner, .sound, .badge])
}
}import SwiftUI
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
var body: some Scene {
WindowGroup { ContentView() }
}
}Manual batch flush
// Flush before app background if needed
Task {
await FlashAnalytics.shared.flushBatch()
}