Upgrade from 3.x to 3.9.0
UpdatedThis page 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 3.9.0 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 APNs
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 (3.x)
import UIKit
import CioMessagingPushAPN
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// Initialize the Customer.io SDK
let cdpApiKey = "YOUR_CDP_API_KEY"
let siteId = "YOUR_SITE_ID"
let config = SDKConfigBuilder(cdpApiKey: cdpApiKey)
// If your account is in the EU region, uncomment the next line
// .region(.EU)
.migrationSiteId(siteId) // only required if you used version 2.x or earlier
.autoTrackUIKitScreenViews() // Set auto tracking of UIKit screen views
.logLevel(CioLogLevel.debug) // Add this to troubleshoot issues - disable debug in production
CustomerIO.initialize(withConfig: config.build())
return true
}
}
After (3.9.0)
import UIKit
import CioMessagingPushAPN
@main
class AppDelegateWithCioIntegration: CioAppDelegateWrapper<AppDelegate> {}
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// Initialize the Customer.io SDK
let cdpApiKey = "YOUR_CDP_API_KEY"
let siteId = "YOUR_SITE_ID"
let config = SDKConfigBuilder(cdpApiKey: cdpApiKey)
// If your account is in the EU region, uncomment the next line
// .region(.EU)
.migrationSiteId(siteId) // only required if you used version 2.x or earlier
.autoTrackUIKitScreenViews() // Set auto tracking of UIKit screen views
.logLevel(CioLogLevel.debug) // Add this to troubleshoot issues - disable debug in production
CustomerIO.initialize(withConfig: config.build())
return true
}
}
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 (3.x)
import SwiftUI
import CioMessagingPushAPN
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Initialize the Customer.io SDK
let cdpApiKey = "YOUR_CDP_API_KEY"
let siteId = "YOUR_SITE_ID"
let config = SDKConfigBuilder(cdpApiKey: cdpApiKey)
// If your account is in the EU region, uncomment the next line
// .region(.EU)
.migrationSiteId(siteId) // only required for migration
.autoTrackUIKitScreenViews() // Set auto tracking of UIKit screen views
.logLevel(CioLogLevel.debug) // Add this to troubleshoot issues - disable debug in production
CustomerIO.initialize(withConfig: config.build())
return true
}
}
After (3.9.0)
import SwiftUI
import CioMessagingPushAPN
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(CioAppDelegateWrapper<AppDelegate>.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Initialize the Customer.io SDK
let cdpApiKey = "YOUR_CDP_API_KEY"
let siteId = "YOUR_SITE_ID"
let config = SDKConfigBuilder(cdpApiKey: cdpApiKey)
// If your account is in the EU region, uncomment the next line
// .region(.EU)
.migrationSiteId(siteId) // only required for migration
.autoTrackUIKitScreenViews() // Set auto tracking of UIKit screen views
.logLevel(CioLogLevel.debug) // Add this to troubleshoot issues - disable debug in production
CustomerIO.initialize(withConfig: config.build())
return true
}
}
Update with FCM
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 (3.x)
import UIKit
import CioMessagingPushFCM
import Firebase
import FirebaseMessaging
@main
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
// Initialize the Firebase SDK.
FirebaseApp.configure()
let siteId = YOUR_SITE_ID
let cdpApiKey = YOUR_CDP_API_KEY
// Configure and initialize the Customer.io SDK
let config = SDKConfigBuilder(cdpApiKey: cdpApiKey)
.migrationSiteId(siteId)
.autoTrackUIKitScreenViews()
.autoTrackDeviceAttributes(true)
CustomerIO.initialize(withConfig: config.build())
// Initialize messaging features after initializing Customer.io SDK
MessagingPushFCM.initialize(
withConfig: MessagingPushConfigBuilder()
// optionally, configure the push module by calling functions on the builder. Such as:
.autoFetchDeviceToken(true)
// See section below to find all the configuration options you can set.
.build()
)
return true
}
}
After (3.9.0)
import UIKit
import CioMessagingPushFCM
import Firebase
import FirebaseMessaging
@main
class AppDelegateWithCioIntegration: CioAppDelegateWrapper<AppDelegate> {}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
// Initialize the Firebase SDK.
FirebaseApp.configure()
let siteId = YOUR_SITE_ID
let cdpApiKey = YOUR_CDP_API_KEY
// Configure and initialize the Customer.io SDK
let config = SDKConfigBuilder(cdpApiKey: cdpApiKey)
.migrationSiteId(siteId)
.autoTrackUIKitScreenViews()
.autoTrackDeviceAttributes(true)
CustomerIO.initialize(withConfig: config.build())
// Initialize messaging features after initializing Customer.io SDK
MessagingPushFCM.initialize(
withConfig: MessagingPushConfigBuilder()
// optionally, configure the push module by calling functions on the builder. Such as:
.autoFetchDeviceToken(true)
// See section below to find all the configuration options you can set.
.build()
)
return true
}
}
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 (3.x)
import SwiftUI
import CioMessagingPushFCM
import UserNotifications
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Initialize the Customer.io SDK
let cdpApiKey = "YOUR_CDP_API_KEY"
let siteId = "YOUR_SITE_ID"
let config = SDKConfigBuilder(cdpApiKey: cdpApiKey)
// If your account is in the EU region, uncomment the next line
// .region(.EU)
.migrationSiteId(siteId) // only required for migration
.autoTrackUIKitScreenViews() // Set auto tracking of UIKit screen views
.logLevel(CioLogLevel.debug) // Add this to troubleshoot issues - disable debug in production
CustomerIO.initialize(withConfig: config.build())
return true
}
}
After (3.9.0)
import SwiftUI
import CioMessagingPushFCM
import UserNotifications
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(CioAppDelegateWrapper<AppDelegate>.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Initialize the Customer.io SDK
let cdpApiKey = "YOUR_CDP_API_KEY"
let siteId = "YOUR_SITE_ID"
let config = SDKConfigBuilder(cdpApiKey: cdpApiKey)
// If your account is in the EU region, uncomment the next line
// .region(.EU)
.migrationSiteId(siteId) // only required for migration
.autoTrackUIKitScreenViews() // Set auto tracking of UIKit screen views
.logLevel(CioLogLevel.debug) // Add this to troubleshoot issues - disable debug in production
CustomerIO.initialize(withConfig: config.build())
return true
}
}
Important Notes
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
userNotificationCenter(_:willPresent:withCompletionHandler:)
userNotificationCenter(_:didReceive:withCompletionHandler:)
- 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()