4.x -> 4.3
UpdatedVersion 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.
Before (4.x)
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)
}
}
After (4.3)
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.
Before (4.x)
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
}
After (4.3)
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.
Before (4.x)
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
}
}
After (4.3)
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.
Before (4.x)
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
}
After (4.3)
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.
Important Notes
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
The
@main
attribute - Must be on the wrapper class, not your AppDelegate.
Troubleshooting
If push notifications stop working after you update your implementation:
- Make sure that you’ve added the
@main
attribute to the wrapper class - Verify that you’ve removed
@main
from your original AppDelegate - Check that you’re calling
MessagingPushAPN.initialize()
orMessagingPushFCM.initialize()
- 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.