iOS SDK Setup

The Reteno iOS SDK for Mobile Customer Engagement and Analytics solutions

See the video manual on iOS SDK setup:

Overview

Reteno is a lightweight SDK for iOS that helps mobile teams integrate Reteno into their mobile apps. The server-side library makes it easy to call the Reteno API.

Minimum SDK Requirements

  • iOS: 14.0+
  • Xcode: 15.0+
  • Swift: 5.7+
📘

Note

Although the SDK binary technically supports iOS 12+, we recommend targeting iOS 14+ to ensure full compatibility with modern APIs and features.

Privacy Manifest and Data Collection

The Reteno iOS SDK ships with a privacy manifest that describes what data is collected and which APIs require a declared reason, in line with Apple’s privacy requirements.

Data collected by the SDK

Data typePurposeLinked to the user
Device IDAnalytics, push deliveryNo
Push tokenPush notificationsYes
User IDPersonalizationYes

APIs with required reasons

The SDK accesses the following APIs that have mandatory “required reasons” declared in the privacy manifest:

  • NSUserDefaults — App functionality (C56D.1)
  • systemUptime — Analytics timestamping (35F9.1)

Getting Started with Reteno SDK for iOS

You can install the Reteno SDK for iOS via

  • Swift Package Manager
  • CocoaPods
License

Reteno iOS SDK is released under the MIT license. See LICENSE for details.

Installing the SDK via Swift Package Manager

The Swift Package Manager is a tool for automating the distribution of Swift code and is integrated into the swift compiler.

Once you have your Swift package set up, adding Reteno as a dependency is as easy as adding it to the dependencies value of your Package.swift.

dependencies: [
    .package(url: "https://github.com/reteno-com/reteno-mobile-ios-sdk.git", .upToNextMajor(from: "2.5.14"))
]

Installation process:

1. Go to the project settings and select Package Dependencies:

2. Press "+" to add a new package to the project. In the opened window enter in a search field reteno-ios-sdk or paste a full URL https://github.com/reteno-com/reteno-mobile-ios-sdk.git. Select found package and fill required settings (dependency rule and project you are adding package).

3. Add the Reteno package to the main target.

and don't forget to add it to the NotificationServiceExtension.

Installing the SDK via CocoaPods

CocoaPods is a dependency manager for Cocoa projects. For usage and installation instructions, visit their website.

To integrate Reteno into your Xcode project using CocoaPods, specify it in your Podfile:

pod 'Reteno', '2.5.14'

Installation process:

1. If you don't have CocoaPods installed, run the following command in Terminal: sudo gem install cocoapods.

2. In your project directory, run: pod init.

3. Open the Podfile in your project with your code editor.

4. Add the Reteno dependency under your app target and the NotificationServiceExtension target as below:

platform :ios, '14.0'

target 'RetenoExample' do
  use_frameworks!

  pod 'Reteno'
  
  target 'NotificationServiceExtension' do
    use_frameworks!

    pod 'Reteno'
  end
end

5. Run the following command in your terminal, in your project directory: pod install

6. Open the newly created <project-name>.xcworkspace file.

Setting Up the SDK

Follow our setup guide to integrate the Reteno SDK with your app.

Step 1: Add the Notification Service Extension

The NotificationServiceExtension allows your iOS application to receive rich notifications with images, design the buttons in notifications, and get message statuses (delivered, clicked).

📘

Note

If you already have Notification Service Extension in your app, follow Step 1.6.

1.1 In Xcode, Select FileNewTarget...

1.2 Select Notification Service Extension , then press Next.

1.3 Enter the product name as NotificationServiceExtension and press Finish.

📘

Note

Do not select Activate on the dialog that is shown after selecting Finish.

1.4 Press Cancel in the Activate scheme prompt.

By canceling, you keep Xcode debugging your app instead of the extension you’ve just created.

If you activate the prompt by accident, you can switch back to debugging your app in Xcode (next to the Play button).

1.5 In the project navigator, select the project directory and select the NotificationServiceExtension target in the targets list.

Check that the Deployment Target is set to the same value as your Main Application Target.

📘

Note

iOS versions under 10 will not be able to get Rich Media.

1.6 In the project navigator, select the NotificationServiceExtension folder and open the NotificationService.swift, then replace the entire file contents with the following code. Ignore any build errors at this point. We will import the Reteno module, which will resolve any errors.

import UserNotifications
import Reteno

class NotificationService: RetenoNotificationServiceExtension {}

This gives the ability to allow your iOS application to receive rich notifications with images.

More about the Modifying Content in Newly Delivered Notifications Extension

Step 2: Import Reteno into Your App Delegate File

📘

To setup SDK you need an SDK_ACCESS_KEY, visit Managing Mobile SDK Access Keys to get it.

Method 1: UIKit

In UIKit, navigate to your AppDelegate file and add the Reteno initialization code to the didFinishLaunchingWithOptions method.

Make sure to import the Reteno module import Reteno

import UIKit
import Reteno

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
  
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        // Reteno initialization
        Reteno.start(apiKey: "SDK_ACCESS_KEY")
      
        // Register for receiving push notifications
        // registerForRemoteNotifications will show the native iOS notification permission prompt
        // Provide UNAuthorizationOptions or use default
        Reteno.userNotificationService.registerForRemoteNotifications(with: [.sound, .alert, .badge], application: application)

        return true
    }
  
    // Remaining contents of your AppDelegate Class...
}

Method 2: SwiftUI

In SwiftUI, update your main 'APP_NAME'App.swift file and use the code below. Make sure to replace 'YOURAPP_NAME' with your app name.

import SwiftUI
import Reteno

class AppDelegate: NSObject, UIApplicationDelegate {

    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
    ) -> Bool {
        // Reteno initialization
        Reteno.start(apiKey: "SDK_ACCESS_KEY")

        // Register for receiving push notifications
        // registerForRemoteNotifications will show the native iOS notification permission prompt
        // Provide UNAuthorizationOptions or use default
        Reteno.userNotificationService.registerForRemoteNotifications(with: [.sound, .alert, .badge], application: application)

        return true
    }

}
import SwiftUI
import Reteno

@main
struct YOURAPP_NAME: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
📘

Note

Starting from Reteno SDK 2.0.4 onwards, the start method was updated. In the previous version, it had the following parameters: apiKey, isAutomaticScreenReportingEnabled, isDebugMode, isPausedInAppMessages, inAppMessagesPauseBehaviour. In the new version, it now has the following parameters: apiKey, configuration. RetenoConfiguration is a struct containing the previous setups.

📘

Note

Starting from Reteno SDK 2.5.10 owards, RetenoConfiguration was updated with useCustomDeviceId parameter which provides an option to set custom device id for SDK. By default it is set to fasle and no changes are required. When set to true, SDK will wait for the call of Reteno.customDeviceIdProvider.setDeviceId(deviceId) and then proceed with initialization. Please call the method above as soon as device id is obtained.

Step 3: Set Up a Notification Permission Request

Option 1. Saving existing logic

If you have already configured subscribing for push notifications by yourself and don't want to change an existing logic, but have Reteno analytics features, call the Reteno.userNotificationService.processRemoteNotificationResponse(_:) SDK method right after you have received user response on the push notification. Pass a UNNotificationResponse as a parameter.

For example, you are using UNUserNotificationCenterDelegate for processing incoming notifications and responding to notification actions. Call the SDK method like described below:

extension AppDelegate: UNUserNotificationCenterDelegate {
    
    func userNotificationCenter(
        _ center: UNUserNotificationCenter,
        willPresent notification: UNNotification,
        withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
    ) {
        completionHandler([.sound, .badge, .alert])
    }
    
    func userNotificationCenter(
        _ center: UNUserNotificationCenter,
        didReceive response: UNNotificationResponse,
        withCompletionHandler completionHandler: @escaping () -> Void
    ) {
        Reteno.userNotificationService.processRemoteNotificationResponse(response)
        completionHandler()
    }
    
}

Option 2. Creating new logic

From Reteno SDK 1.5.4 onwards, method registerForRemoteNotifications has a closure with user response on notifications permission prompt and you can register for Remote notifications wherever you want. For example, you want to ask a user permissions for Remote notifications only after they have completed onboarding.

func onboardingCompleted() {
    Reteno.userNotificationService.registerForRemoteNotifications(with: [.sound, .alert, .badge]) { granted in
        // granted == true if user allowed receiving Remote notifications
    }
}

Old SDK versions support >

Step 4: Add App Groups

App Groups allow additional interposes communication between the app and notification service and are necessary to provide access to Reteno storage data.

4.1 In your Main app target got to Signing & CapabilitiesAll

4.2 Click + Capability if you do not have App Groups in your app yet.

4.3 Select App Groups.

4.4 Under App Groups, click the + button.

4.5 Fill the App Groups container with group.{bundle_id}.reteno-local-storage, where bundle_id is the same as Bundle Identifier off your app (in the main target), then press OK.

4.6 In the NotificationServiceExtension target, repeat steps 5.2 - 5.5 for the extension target.

📘

Note

The group name structure must be group.{bundle_id}.reteno-local-storage, where bundle_id is the same as your Main App target "Bundle Identifier". Do Not Include NotificationServiceExtension.

For more information, visit Configuring App Groups

Step 5: Provide Device Tokens to the SDK via the Following Method:

Reteno.userNotificationService.processRemoteNotificationsToken(_ deviceToken: String)
📘

Note

Providing device token depends on how you send push notifications: via Firebase Cloud Messaging (FCM) or directly to APNs

Option 1. If you send push notifications via Firebase Cloud Messaging (FCM) include a call to Reteno processRemoteNotificationsToken: in your MessagingDelegate delegate as follows:

extension AppDelegate: MessagingDelegate {
    
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
        guard let fcmToken = fcmToken else { return }
        
        Reteno.userNotificationService.processRemoteNotificationsToken(fcmToken)
    }
    
}

Option 2. In case sending push notifications directly to APNs include a call to Reteno in the AppDelegate method like in the example below:

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        let tokenString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
        Reteno.userNotificationService.processRemoteNotificationsToken(tokenString)
    }
📘

Note

Device token data should be converted to String as in the example. Don't send this token if you are using Firebase Cloud Messaging (FCM).

Step 6: Add Custom Behavior for Notifications

If you want to add custom behavior in the UNUserNotificationCenterDelegate methods, provide your implementation in the appropriate closure:

Reteno.userNotificationService.willPresentNotificationHandler = { notification in
    // The closure will be called only if the application is in the foreground. 
    // You can choose to have the notification presented as a sound, badge, alert and/or in the notification list.
    // This decision should be based on whether the information in the notification is otherwise visible to the user.

    let authOptions: UNNotificationPresentationOptions
    if #available(iOS 14.0, *) {
        authOptions = [.badge, .sound, .banner]
    } else {
        authOptions = [.badge, .sound, .alert]
    }
    return authOptions
}
Reteno.userNotificationService.didReceiveNotificationResponseHandler = { notification in
    // Add your code here.
    // The closure will be called when the user responded to the notification by opening the application, 
    // dismissing the notification or choosing a UNNotificationAction.
}

Do it after configuring the Reteno SDK.

Step 7: Run Your App and Send Yourself a Notification

Run your app on a physical iOS device to make sure it builds correctly. You should be prompted to subscribe to push notifications. Send test notifications not from the Firebase but directly from the Reteno. More on sending test messages from the event debug view >

📘

Note

Starting with Xcode 14 and iOS 16, iOS Simulator supports receiving remote push notifications. For earlier versions, use a physical device for testing

Also, we recommend using Debug Mode to detect possible errors and inaccuracies within your Analytics implementation and events' logging.

Troubleshooting

Push notifications are not delivered

  1. Verify APNs configuration

    • Make sure the APNs Auth Key (.p8) or APNs certificate (.p12) is uploaded in the Reteno panel.
    • Confirm that the Bundle ID in Reteno exactly matches your app’s Bundle ID.
    • Ensure the correct Team ID is configured.
  2. Verify the device token

    • Enable SDK debug logging to check that the device successfully registers for push notifications.
    • Make sure the device token is received in didRegisterForRemoteNotificationsWithDeviceToken and sent to Reteno.
  3. Verify App Groups

    • The main app and the Notification Service Extension must use the same App Group.
    • Expected format: group.<bundle_id>.reteno-local-storage

Rich push notifications are not shown

  1. Make sure the NotificationServiceExtension target exists in your project.
  2. Check that the extension’s deployment target is compatible with (and not higher than) the main app’s deployment target.
  3. Ensure your extension class correctly inherits from RetenoNotificationServiceExtension.

Common errors

ErrorCauseSolution
Missing App GroupApp Groups are not configuredConfigure the App Group as described in Step 4 of the setup instructions
Invalid token formatWrong token type is passedUse either the FCM token or the APNs token, not both
SDK not initializedstart() has not been calledCall Reteno.start() in application:didFinishLaunchingWithOptions: