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 alogin()call suppressed the backend's downstream side-effects (cart, journey, and messaging triggers) in the same wayidentify()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
asyncdirectly):loginWithCompletion,requestNotificationPermissionWithCompletion,hasNotificationPermissionWithCompletion,isPushEnabledWithCompletion. Each completes on the main thread. - Device + session state:
getDeviceInfo(returns anNSDictionarymirror ofSwanDeviceInfo, with nested location dictionary if a location has been supplied),getCurrentSessionId,getQueueSize. - Location:
updateLocation:longitude:accuracy:(negativeaccuracyto omit),isLocationEnabled. - Push handling:
handleDeepLink:,handleNotificationUserInfo:(and amessageId: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 expandedObjective-C host appssection of the iOS getting-started guide shows the full new surface. - Async via completion handlers (Obj-C cannot call Swift
[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 nextpod 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
UNNotificationContentExtensionshould follow the updated guide atplatforms/ios/EXTENSIONS.mdfor the iOS 26 setup. - Tapping a carousel slide now opens that slide's deep link. Previously, tapping any individual slide opened the carousel's
defaultRouteregardless 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
addNotificationOpenedListenerexactly 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/addNotificationOpenedListenerun-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
userInfowithout an explicitmessageIdargument.
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.5for 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 separatelaunchOptions[.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 firedeliveredACKs even when the host is killed. Pass the same App Group identifier into the newTemplates.handleServiceRequest(request:content:appGroup:completion:)from inside your NSE — a single call fires the delivery ACK and renders the rich content. On first init withappGroupset, 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:)andSwan.ackPushClickedColdStart(_:appGroup:)— newappGroup:parameter on the existing cold-start ACK methods so NSE / app-extension callers can read credentials from the shared App Group.
Changed
- The
KeyValueStorenow emits a warning when an App-Group-shapedsuiteName(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. SwanObjCObjective-C facade —@objcMembers NSObjectexposing the most-used operations (initialize,identify,logout,enrichProfile,track,screen,flush, super-properties,registerAPNsToken,getPushToken,isPushReady,enableLogs). Pure-Swift host apps continue to callSwan.shareddirectly; mixed-language apps#import <SwanSDK/SwanSDK-Swift.h>and call[SwanObjC shared] ....- Push token lifecycle listeners —
addPushTokenRegisteredListener(fires on every successful APNs registration),addPushTokenRegistrationFailedListener(fires on subscribe POST failure),addPushTokenRefreshListener(fires only on token rotation, not initial registration). NewPushTokenRegisteredPayload/PushTokenRegistrationFailedPayload/PushTokenRefreshPayloadtypes. Swan.shared.initialize(appId:)andSwan.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 identity —
Swan.shared.initialize,identify,login(async, returnsLoginResult),logout,enrichProfile,swanIdentifier,getDeviceInfo. Anonymous-to-identified profile merge on the backend. - Event tracking —
SwanEvents.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
SwanEventshelpers 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 aNotification Service Extension, 5 predefinedUNNotificationCategoryids exposed viaSwanNotificationChannels, foreground / cold-start notification routing, click acknowledgement. - Deep linking —
handleDeepLink(_:)for external URLs (Universal Links, custom URL schemes),handleNotificationUserInfo(_:messageId:)for cold-start,handleNotificationTap(_:messageId:)for warm-start, listener model viaaddNotificationOpenedListener+addDeepLinkOpenedListener, 30-second deduplication onmessageId. - Permissions —
requestNotificationPermission()(async),hasNotificationPermission()(async),isPushEnabled()(async combined gate), graceful denied-permission handling. - Location —
updateLocation(latitude:longitude:accuracy:),isLocationEnabled(), opt-in viaSwanConfig.location.enabled(defaultfalse). Host app supplies coordinates; the SDK does not acquire location itself. - Lifecycle listeners —
addInitializedListener,addDeviceRegisteredListener,addDeviceRegistrationFailedListener,addNetworkStateChangedListener,addSwanIdentifierChangedListener,addNotificationOpenedListener,addDeepLinkOpenedListener,addPushPermissionListener. - Public parity types —
SwanConfig,PushNotificationsConfig,LocationConfig,SwanDeviceInfo,SwanLocation,LoginResult,SwanIdentifierChangedPayload(+Sourceenum),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.