Migration Guide
This guide takes an existing integration to the current iOS SDK version, 1.3.0. Start at the section for the version your app uses now and follow the sections below it in order. Full list of changes: Changelog.
| Your version | Start at |
|---|---|
| 0.7.x | 0.7.x to 1.0.0 |
| 1.0.0 | 1.0.0 to 1.1.x |
| 1.1.0, 1.1.1 | 1.1.x to 1.2.0 |
| 1.2.0 | 1.2.x to 1.3.0 |
To update, change the version to 1.3.0 in Xcode (Package Dependencies, Exact Version) or in your Podfile (:tag => '1.3.0'), then run pod update UserSDK for CocoaPods.
0.7.x to 1.0.0
Version 1.0.0 removes Firebase and Gifu from the SDK. Your app owns Firebase and passes the FCM token to the SDK. This is a breaking change.
1. Dependencies
- Swift Package Manager: remove
firebase-ios-sdkandGifuif you added them only for the SDK. Keep Firebase Messaging if your app uses it, which it needs for push notifications. - CocoaPods: the pod no longer pulls in Firebase. Add it to your own
Podfile:
pod 'UserSDK', :git => 'https://github.com/UserEngage/iOS-SDK.git', :tag => '1.3.0'
pod 'FirebaseMessaging'
- Import: use
import UserComSDK.
2. FCM token
The SDK no longer configures Firebase or fetches the token. Configure Firebase in your app and pass every token to the SDK:
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
guard let fcmToken else { return }
UserSDK.default?.setFcmToken(fcmToken)
}
Details and alternatives: Firebase and FCM Token.
3. Notification handling
The SDK no longer calls FirebaseApp.configure() and is no longer the delegate of Firebase Messaging. Your app configures Firebase and sets its own MessagingDelegate, as shown in step 2. Notification permission and the notification center delegate work as before: the SDK handles them only when you call registerForRemoteNotifications(options:notificationDelegate:).
- If your app has its own notification code, keep your delegate and forward notifications to the SDK: from 1.3.0, taps to
handleNotificationClick(_:)and received notifications tohandleNotification(_:). See Push Notifications, Option A. - If not, call
registerForRemoteNotifications(options:notificationDelegate:), which requests permission and sets the SDK as the delegate.
In both cases, forward application(_:didReceiveRemoteNotification:fetchCompletionHandler:) to the SDK, otherwise in-app messages are not shown.
4. Logout
logout takes the token to remove from the contact who is leaving:
| 0.7.x | 1.0.0 and later |
|---|---|
logout { success, error in } | logout(fcmToken: token) { success, error in } |
Pass nil to skip the removal. Up to 1.2.x the anonymous ping after logout carried no token. From 1.3.0 it carries the same token, see 1.2.x to 1.3.0.
API summary
| 0.7.x | 1.0.0 and later |
|---|---|
UserSDK(application:apiKey:baseURL:shouldTrackActivities:) | Same, plus an optional fcmToken: |
| Token fetched by the SDK | setFcmToken(_:), ping(fcmToken:_:), fcmToken: in the initializer |
logout(_:) | logout(fcmToken:_:) |
Objective-C pingWithCompletion: | pingWithFcmToken:completion:, or ping without a completion |
Objective-C logoutWithCompletion: | logoutWithFcmToken:completion: |
| Firebase and Gifu bundled | No external dependencies |
Everything else keeps its name and signature.
1.0.0 to 1.1.x
No API changes. One behavior change to check.
sendEvent, trackScreen, sendProductEvent, setUserData and setCustomUserData call completion(false, error) when the backend rejects a request with a 4xx status, for example 400 or 401. Before 1.1.0 they called completion(true, nil) regardless of the response. Server errors (5xx) and network errors are retried before the completion reports a failure.
What to do:
- Find code that treats
trueas proof of delivery, for example code that removes data from your own retry storage. - Make sure a
falseresult does not trigger an endless retry loop in your app. The SDK already retries what can be retried.
Other changes in 1.1.x that need no action:
- Failed requests retry with a growing backoff and no longer block other queued events.
- In-app messages are presented through the active foreground scene, which fixes apps with multiple scenes.
- In-app button clicks are always reported, also for links handled by your
inAppNotificationClickDelegate. In 1.1.1, clicks are counted in the panel again. shouldTrackActivitiesis deprecated. It never enabled automatic screen tracking.
1.1.x to 1.2.0
No breaking API changes. Two behavior changes to check.
sendProductEvent sends each param as a separate product attribute. Before 1.2.0, params were nested under custom_data and stored as one JSON attribute.
What to do:
- Rebuild segments and automations that use the
custom_dataattribute on the individual attributes. - Use a fixed set of param keys with stable value types, because every new key creates a new product attribute.
logout records a logout event for the contact who is leaving.
What to do: if your app sends its own logout event, remove it to avoid counting logouts twice.
Also worth knowing:
setFcmToken(_:)sends a ping on every call, also with an unchanged token.- In-app messages are shown in their own window, and a message received in the background is shown on the next app open, within 6 hours.
- In-app "displayed" is reported only when the message is on screen. Displayed numbers in the panel can drop after the upgrade and become accurate.
setUserDataandsetCustomUserDataaccept an optionalfcmToken. Use it at login to send identity and token in one request.handleNotification(userInfo:)is safe to call from any thread, aTaskor an actor.
1.2.x to 1.3.0
Existing code compiles. Renamed methods keep working and show a deprecation warning that names the new one. They stay until 2.0. The behavior changes below are worth checking.
Renamed API
| 1.2.x (deprecated) | 1.3.0 |
|---|---|
sendEvent(with:params:_:) | sendEvent(name:attributes:_:) |
trackScreen(with:_:) | trackScreen(name:_:) |
sendProductEvent(_:eventType:name:productURL:params:_:) | sendProductEvent(_:eventType:attributes:_:), with name and product_url in attributes |
EventType | ProductEventType |
setUserData(_:fcmToken:_:), setCustomUserData(_:fcmToken:_:) | updateContact(_:fcmToken:_:) with a Contact |
userId | userKey |
inAppNotificationClickDelegate | inAppMessageClickHandler |
handleNotification(userInfo:) | handleNotification(_:) for received notifications, handleNotificationClick(_:) for taps |
New in 1.3.0:
handleEmailLinkcounts clicks on links from Positive User emails that open your app, and returns the target of the link.UserSDK.errorHandlerreceives every SDK error in one place, for example to log it.
Before 1.3.0 every handleNotification(userInfo:) call counted a click, also for a notification that was only received. From 1.3.0 handleNotification(_:) handles a received notification (in-app messages) and never counts a click. handleNotificationClick(_:) counts the click.
What to do:
- In
userNotificationCenter(_:didReceive:withCompletionHandler:), callhandleNotificationClick(_:). - In
application(_:didReceiveRemoteNotification:fetchCompletionHandler:), callhandleNotification(_:). - Until you switch, the old method counts clicks as before.
If the SDK is your notification center delegate (you call registerForRemoteNotifications(options:notificationDelegate:)), notifications received in the foreground are now shown as banners. Before, they only played their sound.
Before 1.3.0 setUserData and setCustomUserData stored the data and every later ping sent it again. From 1.3.0 updateContact sends the data once and the backend adds it to the contact. Later pings do not repeat it. The first updateContact call also clears what the old methods stored.
What to do: replace setUserData and setCustomUserData with updateContact, at login with the FCM token. The phone number is sent as phone_number instead of phone, and the backend accepts both. A nil value never removed a field on the backend, and it still does not.
Before 1.3.0 the anonymous contact created after logout had no push token, so the device received no push notifications until the next login or setFcmToken(_:). From 1.3.0 the anonymous contact gets the same token. A push notification sent to the logged out contact does not reach the device.
What to do: remove a setFcmToken(_:) call after logout if you added it only for this. Turning push notifications off after logout is up to your app.
Events collected offline before logout are still sent, for the user who logged out. Contact updates of that user still waiting in the queue are dropped and end with loggedOut.
- Every failure is a
UserSDKError. In Objective-C it is anNSErrorwith the domaincom.user.sdk. - Completions always run on the main thread, and never before the call returns.
- Values JSON cannot hold (
Date,URL, NaN) in attributes end withinvalidData. Before 1.3.0 they crashed the app. - An empty, too long or HTML event name, a screen name over 128 characters and an empty product ID end with
invalidNamewithout a request. - A ping answered with
{"success": false}(the app does not collect data) ends withnotCollectingData. Before, it reported success. - 429 (rate limit) is retried after the time the server asks for. Before, the request was dropped.
What to do: if your completions switch to the main queue, they keep working. Check code that expects a specific error type or message. See Errors.
Pings while the phone is locked
Before 1.3.0, when the app was woken in the background on a locked phone (a silent push, a token refresh), the SDK could not read the contact key. A ping sent then created a new anonymous contact and moved the push token to it, so push notifications to the real contact stopped reaching the device until the next ping. From 1.3.0 the contact key stays readable after the first unlock since the phone started. Right after a restart, before the first unlock, a ping waits until the phone is unlocked. Nothing to do in your app. See Duplicate anonymous contacts from one iOS device.
Device data
The SDK no longer sends the app version, Wi-Fi, radio and carrier data or the advertising identifier: Positive User never stored them. The SDK no longer links AdSupport and CoreTelephony and no longer reads the advertising identifier.
After updating, run the steps in Test your integration.