Swan SDK
Changelog

iOS

Release history for the Swan iOS SDK.

[ios/1.5.1] — 2026-06-25

Distribution: Swift Package Manager and CocoaPods (pod 'SwanSDK', '~> 1.5') Deployment target: iOS 13.0+ · Swift 5.9+ · Xcode 15+

Changed

  • Device registration resolves the device identifier more robustly. The SDK now reliably obtains the device identifier when registering, so the identifier is consistently available for events and attribution.

[ios/1.5.0] — 2026-06-19

Distribution: Swift Package Manager and CocoaPods (pod 'SwanSDK', '~> 1.5') Deployment target: iOS 13.0+ · Swift 5.9+ · Xcode 15+

Fixed

  • login() now triggers the full backend pipeline. Previously a login() call suppressed the backend's downstream side-effects (cart, journey, and messaging triggers) in the same way identify() does. login() now runs the complete pipeline, so logging a user in fires those side-effects as expected. identify() is unchanged and continues to switch the profile without those side-effects.
  • Logging out now starts a fresh anonymous identity. When a user who first used the app anonymously and later signed in subsequently logs out, the SDK begins a new anonymous identity instead of reusing the one that had been merged into their account on sign-in. This prevents activity after logout from being attributed to the signed-out user.

[ios/1.4.0] — 2026-05-19

Distribution: Swift Package Manager and CocoaPods (pod 'SwanSDK', '~> 1.4') Deployment target: iOS 13.0+ · Swift 5.9+ · Xcode 15+

Added

  • Expanded Objective-C surface on SwanObjC. Mixed-language host apps can now drive most of the customer-facing SDK from Obj-C call sites without writing a Swift bridging file. The previous facade covered initialization, identity basics, custom event tracking, super-properties, and APNs token registration — about a fifth of the public surface. The new entries close the gap:

    • Async via completion handlers (Obj-C cannot call Swift async directly): loginWithCompletion, requestNotificationPermissionWithCompletion, hasNotificationPermissionWithCompletion, isPushEnabledWithCompletion. Each completes on the main thread.
    • Device + session state: getDeviceInfo (returns an NSDictionary mirror of SwanDeviceInfo, with nested location dictionary if a location has been supplied), getCurrentSessionId, getQueueSize.
    • Location: updateLocation:longitude:accuracy: (negative accuracy to omit), isLocationEnabled.
    • Push handling: handleDeepLink:, handleNotificationUserInfo: (and a messageId: overload), handleNotificationTap: (same), handlePushNotificationUserInfo:, unsubscribePush, ackPushDelivered:, ackPushClicked:type:linkId:, flushPendingAcks.
    • Notification categories + badge: createNotificationChannelWithId:name:importance:soundName:, deleteNotificationChannelWithId:, getNotificationChannelId, getBadgeCount, setBadgeCount:.
    • Lifecycle: addInitializedListener:.

    Listeners that emit typed Swift-struct payloads (addNotificationOpenedListener, addDeepLinkOpenedListener, the telemetry-event listeners) remain Swift-only — host apps that need them write a small Swift bridging file to adapt the payload into NSObjects. The expanded Objective-C host apps section of the iOS getting-started guide shows the full new surface.


[ios/1.3.2] — 2026-05-18

Distribution: Swift Package Manager and CocoaPods (pod 'SwanSDK', '~> 1.3') Deployment target: iOS 13.0+ · Swift 5.9+ · Xcode 15+

Fixed

  • Swift Package Manager resolution. Installing via SPM with .package(url: "https://github.com/SwanCX/swan-ios-sdk", from: "1.3.2") (or pinning by version range in Xcode → File → Add Packages…) now resolves a concrete version. The 1.3.0 and 1.3.1 mirrors used a non-SemVer tag scheme that SPM could not interpret as a version. From 1.3.2 forward, the distribution repo uses plain SemVer tags (1.3.2, …). CocoaPods installs (pod 'SwanSDK', '~> 1.3') were unaffected by this issue and continue to work — this release only changes how SPM discovers versions.

[ios/1.3.1] — 2026-05-18

Distribution: Swift Package Manager and CocoaPods (pod 'SwanSDK', '~> 1.3') Deployment target: iOS 13.0+ · Swift 5.9+ · Xcode 15+

Fixed

  • CocoaPods install now resolves. The 1.3.0 spec referenced a source location CocoaPods Trunk could not read. 1.3.1 ships identical SDK code with a corrected source reference. Customers on pod 'SwanSDK', '~> 1.3' will pick this up automatically on the next pod update.

[ios/1.3.0] — 2026-05-18

Distribution: Swift Package Manager and CocoaPods (pod 'SwanSDK', '~> 1.3') Deployment target: iOS 13.0+ · Swift 5.9+ · Xcode 15+

Fixed

  • Carousel notifications now render correctly on iOS 26. Long-pressing a carousel push on iOS 26 previously showed iOS's default banner instead of the swipeable image carousel. The custom carousel UI now displays on iOS 26 the same way it does on earlier iOS versions. Host apps integrating a UNNotificationContentExtension should follow the updated guide at platforms/ios/EXTENSIONS.md for the iOS 26 setup.
  • Tapping a carousel slide now opens that slide's deep link. Previously, tapping any individual slide opened the carousel's defaultRoute regardless of which slide was tapped. Per-item routes set on a carousel item now resolve correctly when that item is tapped.
  • Cold-start taps no longer fire the opened listener twice. Tapping a notification on a killed app now invokes addNotificationOpenedListener exactly once on launch.
  • Listeners registered before initialize(...) completes now fire reliably. Previously, listeners attached during app startup but before the SDK had finished initializing could miss events that arrived during the init window.
  • Click deduplication no longer breaks on whitespace. Message IDs with leading or trailing whitespace are now deduplicated correctly; previously they could bypass the 30-second dedup window.
  • Swan.shared.clearCachedCredentials() no longer wipes unrelated data when sharing an App Group with other SDKs. The reset is now scoped to keys this SDK actually owns.
  • The Notification Service Extension's delivery ACK on the very first push after install now surfaces a clear warning when credentials are not yet available, instead of silently dropping the ACK.
  • Tapping a second carousel notification immediately after the first now records the second tap's per-slide click data correctly. Previously, iOS reusing the Content Extension's view controller could cause the second tap's data to be dropped.
  • addDeepLinkOpenedListener / addNotificationOpenedListener un-registration is now deterministic. Calling the returned unregister function removes that specific listener and no others.
  • Session flushes no longer fire on transient app-state interruptions like Control Center swipes, Face ID prompts, or screenshots. Sessions now flush on actual app-to-background transitions only.
  • Tapping the same carousel notification twice in quick succession no longer routes the second tap to the first tap's slide when the host forwards userInfo without an explicit messageId argument.

Changed

  • The sample app's foreground-presentation handler now fires only the delivery ACK for foreground pushes; the "opened" listener fires only on an actual user tap. See EXTENSIONS.md §3.5 for the integration pattern host apps should follow.
  • Cold-start tap routing now follows iOS's canonical didReceive(response:) path; host apps no longer need a separate launchOptions[.remoteNotification] forwarding block.

Added

  • platforms/ios/EXTENSIONS.md — host-integration guide for Notification Service Extension + Notification Content Extension, covering iOS 26 setup requirements, foreground vs. tap semantics, per-slide deep-link wiring on carousels, and a troubleshooting matrix.
  • Sample Notification Content Extension target in SampleAppXcode/ — a working 3-image carousel reference implementation with page control and per-item tap routing that customers can copy as a starting point.

[ios/1.2.0] — 2026-05-17

Distribution: Swift Package Manager and CocoaPods (pod 'SwanSDK', '~> 1.2') Deployment target: iOS 13.0+ · Swift 5.9+ · Xcode 15+

Added

  • App Group support for Notification Service Extensions. New SwanConfig.appGroup: String? field lets the host app share credentials with its NSE process so the extension can fire delivered ACKs even when the host is killed. Pass the same App Group identifier into the new Templates.handleServiceRequest(request:content:appGroup:completion:) from inside your NSE — a single call fires the delivery ACK and renders the rich content. On first init with appGroup set, the SDK migrates existing credentials from the per-process suite to the App Group suite so upgrading apps don't need to re-register.
  • Templates.handleServiceRequest(request:content:appGroup:completion:) — new public NSE entrypoint that wraps delivery-ACK firing and content rendering in one call.
  • Swan.ackPushDeliveredColdStart(_:appGroup:) and Swan.ackPushClickedColdStart(_:appGroup:) — new appGroup: parameter on the existing cold-start ACK methods so NSE / app-extension callers can read credentials from the shared App Group.

Changed

  • The KeyValueStore now emits a warning when an App-Group-shaped suiteName (group.*) fails to open. Almost always a missing or mismatched entitlement.

[ios/1.1.0] — 2026-05-16

Distribution: Swift Package Manager and CocoaPods (pod 'SwanSDK', '~> 1.1') Deployment target: iOS 13.0+ · Swift 5.9+ · Xcode 15+

Added

  • CocoaPods distribution — install with pod 'SwanSDK', '~> 1.1' alongside the existing Swift Package Manager support.
  • SwanObjC Objective-C facade@objcMembers NSObject exposing the most-used operations (initialize, identify, logout, enrichProfile, track, screen, flush, super-properties, registerAPNsToken, getPushToken, isPushReady, enableLogs). Pure-Swift host apps continue to call Swan.shared directly; mixed-language apps #import <SwanSDK/SwanSDK-Swift.h> and call [SwanObjC shared] ....
  • Push token lifecycle listenersaddPushTokenRegisteredListener (fires on every successful APNs registration), addPushTokenRegistrationFailedListener (fires on subscribe POST failure), addPushTokenRefreshListener (fires only on token rotation, not initial registration). New PushTokenRegisteredPayload / PushTokenRegistrationFailedPayload / PushTokenRefreshPayload types.
  • Swan.shared.initialize(appId:) and Swan.shared.initialize(appId:config:) — new public overloads. Host apps no longer pass an infrastructure URL in the init snippet.

Changed

  • Swan.shared.handleDeepLink(_:) -> Bool — exposed as public (was internal in v1.0.0).

Known limitations

  • Objective-C facade covers the synchronous, listener-free surface. Async methods (login, requestNotificationPermission, isPushEnabled) and listener subscriptions with struct payloads remain Swift-only — write a small Swift bridge file if your Obj-C app needs them.
  • Source distribution only. SPM and CocoaPods both build from the tagged source; no XCFramework binary distribution.

[ios/1.0.0] — 2026-05-16

First production release of the Swan iOS SDK.

Distribution: Swift Package Manager Deployment target: iOS 13.0+ · Swift: 5.9+ · Xcode: 15+

Added

  • Core identitySwan.shared.initialize, identify, login (async, returns LoginResult), logout, enrichProfile, swanIdentifier, getDeviceInfo. Anonymous-to-identified profile merge on the backend.
  • Event trackingSwanEvents.screen, Swan.shared.track, customEvent, super-properties (setCountry / setCurrency / setBusinessUnit / setCurrentScreenName), session tracking, offline-first SQLite-backed queue with exponential-backoff retry.
  • E-commerce events — typed SwanEvents helpers covering the full catalog (productViewed, productAddedToAddTocart, checkoutStarted, checkoutCompleted, orderCompleted, etc.) — same wire names as Android for cross-platform parity.
  • Push notifications (APNs)registerAPNsToken(_:) / registerAPNsTokenHex(_:), basic + carousel notification templates rendered in a Notification Service Extension, 5 predefined UNNotificationCategory ids exposed via SwanNotificationChannels, foreground / cold-start notification routing, click acknowledgement.
  • Deep linkinghandleDeepLink(_:) for external URLs (Universal Links, custom URL schemes), handleNotificationUserInfo(_:messageId:) for cold-start, handleNotificationTap(_:messageId:) for warm-start, listener model via addNotificationOpenedListener + addDeepLinkOpenedListener, 30-second deduplication on messageId.
  • PermissionsrequestNotificationPermission() (async), hasNotificationPermission() (async), isPushEnabled() (async combined gate), graceful denied-permission handling.
  • LocationupdateLocation(latitude:longitude:accuracy:), isLocationEnabled(), opt-in via SwanConfig.location.enabled (default false). Host app supplies coordinates; the SDK does not acquire location itself.
  • Lifecycle listenersaddInitializedListener, addDeviceRegisteredListener, addDeviceRegistrationFailedListener, addNetworkStateChangedListener, addSwanIdentifierChangedListener, addNotificationOpenedListener, addDeepLinkOpenedListener, addPushPermissionListener.
  • Public parity typesSwanConfig, PushNotificationsConfig, LocationConfig, SwanDeviceInfo, SwanLocation, LoginResult, SwanIdentifierChangedPayload (+ Source enum), PushNotificationReceivedPayload, SwanNotificationChannels.
  • Log markers — six [SwanSDK] * log lines emit at key lifecycle points for integration verification.

Known limitations

  • Push token lifecycle listeners (addPushTokenRegisteredListener, addPushTokenRegistrationFailedListener, addPushTokenRefreshListener) are not available in v1.0.0 — see iOS v1.1.0.
  • Objective-C consumption — the SDK is Swift-only in this release. Pure-Obj-C host apps consume it via a Swift bridging header.

On this page