iOS
Install, initialize, and verify your first event on iOS.
iOS 13.0+ · Swift 5.9+ · Distribution: Swift Package Manager and CocoaPods · Latest: ios/1.4.0
Install (CocoaPods)
Add the pod to your Podfile:
target 'YourApp' do
pod 'SwanSDK', '~> 1.3'
endThen install:
pod installOpen the generated .xcworkspace (not the .xcodeproj) in Xcode going forward.
Swift Package Manager
The SDK is published on Swift Package Manager. In Xcode → File → Add Packages…, paste:
https://github.com/SwanCX/swan-ios-sdkPin to the latest version (currently 1.4.0) and add the SwanSDK product to your app target.
Or in Package.swift:
dependencies: [
.package(url: "https://github.com/SwanCX/swan-ios-sdk", from: "1.4.0"),
]Initialize
In your @main SwiftUI App:
import SwanSDK
@main
struct MyApp: App {
init() {
Swan.shared.initialize(
appId: "your-app-id",
config: SwanConfig(debug: false, production: true)
)
}
var body: some Scene { /* ... */ }
}UIKit equivalent:
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Swan.shared.initialize(appId: "your-app-id")
return true
}
}Swan.shared.initialize(...) returns immediately. Device registration runs in the background; subscribe to addInitializedListener { } if you need to know when init resolves.
Push setup (APNs)
Rich pushes (image / carousel) require a Notification Service Extension. Without it, iOS will show title + body only — images and carousel items will not render. See iOS — Rich pushes and extensions for the step-by-step extension setup. The basic APNs token wire-up below is the minimum for plain pushes.
In your AppDelegate:
import UserNotifications
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Swan.shared.initialize(appId: "your-app-id")
UNUserNotificationCenter.current().delegate = self
application.registerForRemoteNotifications()
return true
}
func application(_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Swan.shared.registerAPNsToken(deviceToken)
}
}
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
Swan.shared.handleNotificationUserInfo(response.notification.request.content.userInfo)
completionHandler()
}
}Objective-C host apps
Mixed-language host apps drive the SDK through the SwanObjC facade —
a @objcMembers singleton that mirrors the customer-facing operations
on Swan through Obj-C-callable selectors.
#import <SwanSDK/SwanSDK-Swift.h>Initialize and identity
[[SwanObjC shared] initializeWithAppId:@"your-app-id"];
[[SwanObjC shared] addInitializedListener:^{
NSLog(@"SDK ready, swanIdentifier=%@", [SwanObjC shared].swanIdentifier);
}];
[[SwanObjC shared] identifyWithIdentifier:@"user-123"
attributes:@{ @"email": @"jane@example.com" }];
[[SwanObjC shared] enrichProfile:@{ @"plan": @"gold" }];
// Async login — completion fires on the main thread.
[[SwanObjC shared] loginWithCompletionWithIdentifier:@"user-123"
attributes:nil
completion:^(NSDictionary *result) {
NSLog(@"cdid=%@, profileSwitched=%@",
result[@"cdid"], result[@"profileSwitched"]);
}];
[[SwanObjC shared] logout];Event tracking and super-properties
[[SwanObjC shared] trackWithName:@"productViewed"
attributes:@{ @"id": @"SKU-100", @"price": @9.99 }];
[[SwanObjC shared] screenWithName:@"Home" attributes:nil];
[[SwanObjC shared] setCountry:@"AE"];
[[SwanObjC shared] setCurrency:@"AED"];
[[SwanObjC shared] setBusinessUnit:@"retail"];
[[SwanObjC shared] setCurrentScreenName:@"Home"];
[[SwanObjC shared] flush]; // force-drain the queue
NSInteger pending = [[SwanObjC shared] getQueueSize];Device info and location
NSDictionary *info = [[SwanObjC shared] getDeviceInfo];
NSLog(@"platform=%@ device=%@ cdid=%@",
info[@"platform"], info[@"deviceModal"], info[@"generatedCDID"]);
// Pass a negative number for accuracy to omit it.
[[SwanObjC shared] updateLocationWithLatitude:25.276987
longitude:55.296249
accuracy:10.0];
BOOL hasLocation = [[SwanObjC shared] isLocationEnabled];Push notifications
// In didFinishLaunchingWithOptions:
[application registerForRemoteNotifications];
// In didRegisterForRemoteNotificationsWithDeviceToken:
[[SwanObjC shared] registerAPNsToken:deviceToken];
// In didReceiveNotificationResponse: (user tapped a notification)
[[SwanObjC shared] handleNotificationUserInfo:response.notification.request.content.userInfo];
// In didReceiveRemoteNotification: (silent / data-only push)
[[SwanObjC shared] handlePushNotificationUserInfo:userInfo];
// Deep links from your scene delegate or URL handler:
BOOL handled = [[SwanObjC shared] handleDeepLink:url.absoluteString];
// Opt-out / re-enrol:
[[SwanObjC shared] unsubscribePush];Permissions (async → completion)
[[SwanObjC shared] requestNotificationPermissionWithCompletion:^(BOOL granted) {
NSLog(@"permission granted=%d", granted);
}];
[[SwanObjC shared] hasNotificationPermissionWithCompletion:^(BOOL granted) { /* … */ }];
[[SwanObjC shared] isPushEnabledWithCompletion:^(BOOL ready) { /* … */ }];Notification categories and badge
[[SwanObjC shared] createNotificationChannelWithId:@"swan_default"
name:@"General Notifications"
importance:4
soundName:@""]; // @"" = default sound
NSInteger badge = [[SwanObjC shared] getBadgeCount];
[[SwanObjC shared] setBadgeCount:0];
NSString *defaultId = [[SwanObjC shared] getNotificationChannelId];What stays Swift-only
The following surfaces remain Swift-only — write a small Swift bridging file in your host app to adapt them to NSObjects:
- Listeners that emit typed-struct payloads:
addNotificationOpenedListener,addDeepLinkOpenedListener,addPushNotificationReceivedListener,addSwanIdentifierChangedListener,addDeviceRegisteredListener,addPushTokenRegisteredListener, and the rest of the telemetry-event listeners. - The
SwanConfigvalue type (use theinitializeWithAppId:debug:production:overload from Obj-C instead). - The typed e-commerce helpers on
SwanEvents. Re-use the generictrackWithName:attributes:selector and pass the event-name string from the Track events catalogue. registrationStateStream(AsyncStream).
Verify the integration
Six [SwanSDK] * log markers emit at key lifecycle points:
| Marker | Means |
|---|---|
[SwanSDK] Starting SDK initialization... | initialize(...) called |
[SwanSDK] SDK initialization completed successfully | init resolved |
[SwanSDK] Device registered successfully: <uuid> | device-register HTTP call succeeded; CDID granted |
[SwanSDK] Foreground notification received: <messageId> | push arrived while app foreground |
[SwanSDK] Notification displayed successfully with ID: <messageId> | notification posted to OS |
[SwanSDK] Notification ACK queued | click ACK queued for backend |
Visible in the Xcode debug console or via log show on a real device.
Track your first event
SwanEvents.productViewed(attributes: ["id": "SKU-100", "name": "Test product", "price": 9.99])Next steps
Identify users
Sign-in flow, anonymous-to-identified merge, profile enrichment, logout.
Track events
Screen tracking, typed e-commerce helpers, custom events, super-properties.
Push notifications
APNs wire-up details, categories, cold-start deep linking.
iOS — Rich pushes and extensions
Add a Notification Service Extension to render image and carousel pushes. Required for any non-text push on iOS.