Loading…

4.x -> 4.3

Updated

Version 4.3 of the Customer.io React Native SDK introduces a new CioAppDelegateWrapper pattern for iOS that simplifies push notification setup and eliminates the need for method swizzling.

Key Changes

The primary change in version 4.3 is the introduction of the wrapper pattern for handling push notifications on iOS. This change:

  • Eliminates method swizzling: No more automatic method replacement
  • Simplifies setup: Less boilerplate code required
  • Improves reliability: More predictable behavior

See the instructions below to update your app depending on whether you send push notifications with APN or FCM and whether you use UIKit or SwiftUI.

Update with APN (Apple Push Notification service)

UIKit

Update your AppDelegate.swift file to use the new CioAppDelegateWrapper pattern. See the Before sample to see what needs to change and the After sample to see the new pattern.

import UIKit
import CioMessagingPushAPN
import UserNotifications

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    func application(_ application: UIApplication, 
                    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Initialize push 
        MessagingPushAPN.initialize(withConfig: MessagingPushConfigBuilder().build())

        // Register for push notifications
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, _ in
            if granted {
                DispatchQueue.main.async {
                    UIApplication.shared.registerForRemoteNotifications()
                }
            }
        }
        
        return true
    }
    
    // Manual push handling methods
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        MessagingPush.shared.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
    }
    
    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        MessagingPush.shared.application(application, didFailToRegisterForRemoteNotificationsWithError: error)
    }
    
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        MessagingPush.shared.application(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler)
    }
}
import UIKit
import CioMessagingPushAPN
import UserNotifications

@main
class AppDelegateWithCioIntegration: CioAppDelegateWrapper<AppDelegate> {}

class AppDelegate: UIResponder, UIApplicationDelegate {
    
    func application(_ application: UIApplication, 
                    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Initialize push with wrapper - handles all push methods automatically
        MessagingPushAPN.initialize(withConfig: MessagingPushConfigBuilder().build())
        
        // Register for push notifications
        // You can move this line to any part of your app. It's not critical to call it in this method.
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, _ in
            if granted {
                // Remove this, as Customer.io SDK handles this automatically
                DispatchQueue.main.async {
                    UIApplication.shared.registerForRemoteNotifications()
                }
            }
        }
        
        return true
    }
    
    // No manual push methods needed - CioAppDelegateWrapper handles everything
}

SwiftUI

If you’re using SwiftUI, you’ll need to use the @UIApplicationDelegateAdaptor instead of the @main attribute. See the Before sample to see what needs to change and the After sample to see the new pattern.

import SwiftUI
import CioMessagingPushAPN
import UserNotifications

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

class AppDelegate: NSObject, UIApplicationDelegate {
    // Similar manual push handling as UIKit example above
}
import SwiftUI
import CioMessagingPushAPN
import UserNotifications

@main
struct MyApp: App {
    @UIApplicationDelegateAdaptor(CioAppDelegateWrapper<AppDelegate>.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication,
                    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Initialize push with wrapper
        MessagingPushAPN.initialize(withConfig: MessagingPushConfigBuilder().build())
        
        return true
    }
    
    // No manual push methods needed
}

Update with FCM (Firebase Cloud Messaging)

UIKit

Update your AppDelegate.swift file to use the new CioAppDelegateWrapper pattern. See the Before sample to see what needs to change and the After sample to see the new pattern.

import UIKit
import CioMessagingPushFCM
import UserNotifications
import Firebase
import FirebaseMessaging

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    func application(_ application: UIApplication, 
                    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        // Configure Firebase
        FirebaseApp.configure()
        
        // Set FCM messaging delegate
        Messaging.messaging().delegate = self
        
        // Register for push notifications
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, _ in
            if granted {
                DispatchQueue.main.async {
                    UIApplication.shared.registerForRemoteNotifications()
                }
            }
        }
        
        return true
    }
    
    // Manual push handling methods
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        Messaging.messaging().apnsToken = deviceToken
        MessagingPush.shared.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
    }
    
    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        MessagingPush.shared.application(application, didFailToRegisterForRemoteNotificationsWithError: error)
    }
    
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        MessagingPush.shared.application(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler)
    }
}

extension AppDelegate: MessagingDelegate {
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
        // Handle FCM token
    }
}
import UIKit
import CioMessagingPushFCM
import UserNotifications
import Firebase
import FirebaseMessaging

@main
class AppDelegateWithCioIntegration: CioAppDelegateWrapper<AppDelegate> {}

class AppDelegate: UIResponder, UIApplicationDelegate {
    
    func application(_ application: UIApplication, 
                    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        // Configure Firebase
        FirebaseApp.configure()
        
        // Set FCM messaging delegate
        Messaging.messaging().delegate = self
        
        // Initialize push FCM with wrapper - handles all push methods automatically
        MessagingPushFCM.initialize(withConfig: MessagingPushConfigBuilder().build())
        
        // Register for push notifications
        // You can move this line to any part of your app. It's not critical to call it in this method.
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, _ in
            if granted {
                // Remove this, as Customer.io SDK handles this automatically
                DispatchQueue.main.async {
                    UIApplication.shared.registerForRemoteNotifications()
                }
            }
        }
        
        return true
    }
    
    // No manual push methods needed - wrapper handles everything
}

extension AppDelegate: MessagingDelegate {
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
        // Handle FCM token - Customer.io SDK will also receive this automatically
    }
}

SwiftUI

If you’re using SwiftUI, you’ll need to use the @UIApplicationDelegateAdaptor instead of the @main attribute. See the Before sample to see what needs to change and the After sample to see the new pattern.

import SwiftUI
import CioMessagingPushFCM
import UserNotifications

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

class AppDelegate: NSObject, UIApplicationDelegate {
    // Similar manual push handling as UIKit example above
}
import SwiftUI
import CioMessagingPushFCM
import UserNotifications

@main
struct MyApp: App {
    @UIApplicationDelegateAdaptor(CioAppDelegateWrapper<AppDelegate>.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication,
                    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Initialize push with wrapper
        MessagingPushAPN.initialize(withConfig: MessagingPushConfigBuilder().build())
        
        return true
    }
    
    // No manual push methods needed
}

Configuration Options

You can customize the push behavior when you initialize your push package.

MessagingPushAPN.initialize(
    withConfig: MessagingPushConfigBuilder()
        .showPushAppInForeground(true)  // Show push when app is in foreground
        .autoTrackPushEvents(true)       // Automatically track push metrics
        .build()
)
MessagingPushFCM.initialize(
    withConfig: MessagingPushConfigBuilder()
        .showPushAppInForeground(true)  // Show push when app is in foreground
        .autoTrackPushEvents(true)       // Automatically track push metrics
        .build()
)

Important Notes

  1. Manual push handling methods are not required: the CioAppDelegateWrapper automatically records information from following methods. But you can still use these methods if you want to add custom push handling:

    • didRegisterForRemoteNotificationsWithDeviceToken
    • didFailToRegisterForRemoteNotificationsWithError
    • didReceiveRemoteNotification
    • All other push-related delegate methods
  2. The @main attribute - Must be on the wrapper class, not your AppDelegate.

Troubleshooting

If push notifications stop working after you update your implementation:

  1. Make sure that you’ve added the @main attribute to the wrapper class
  2. Verify that you’ve removed @main from your original AppDelegate
  3. Check that you’re calling MessagingPushAPN.initialize() or MessagingPushFCM.initialize()
  4. If you encounter some unexpected behavior and want to test is it related to new Push Notification tracking system, just comment the following line and compare class AppDelegateWithCioIntegration: CioAppDelegateWrapper<AppDelegate> {} with the original AppDelegate.
Copied to clipboard!
  Contents
Version
Is this page helpful?