Swan SDK
Getting Started

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'
end

Then install:

pod install

Open 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-sdk

Pin 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 SwanConfig value type (use the initializeWithAppId:debug:production: overload from Obj-C instead).
  • The typed e-commerce helpers on SwanEvents. Re-use the generic trackWithName: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:

MarkerMeans
[SwanSDK] Starting SDK initialization...initialize(...) called
[SwanSDK] SDK initialization completed successfullyinit 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 queuedclick 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

On this page