What is Method Swizzling?

Method swizzling is a technique used in Objective-C and Swift that allows developers to interchange the implementations of two methods at runtime. This is particularly useful in modifying the behavior of existing system methods without subclassing or altering the original source code.

In the context of iOS development, swizzling can intercept system calls to the AppDelegate’s push notification delegate methods, providing an opportunity to insert additional logic such as analytics, third-party SDK functions, or custom handling.

Advantages of Using Swizzling for Push Notifications

Swizzling streamlines the process of integrating push notifications with various services and frameworks. When using swizzling:

  • Developers do not need to write boilerplate code to handle common notification tasks.

  • ensures that all the necessary methods are called without explicitly invoking them in the app’s code.

-SDKs can provide out-of-the-box functionality, enhancing or augmenting notification handling with minimal setup required.

Default Behavior in Castled SDK

Castled SDK is designed to simplify push notification handling. By default, swizzling is enabled, which means the SDK automatically handles the registration of device tokens, the presentation of notifications, and the response to user interactions with those notifications.

This default setup allows for a seamless integration process, requiring minimal code changes on the developer’s part.

Disabling Swizzling in Castled SDK

Although swizzling is convenient, some developers may prefer to have more granular control over notification handling or may need to comply with specific architectural requirements. In such cases, Castled SDK allows swizzling to be disabled.

To disable method swizzling with Castled SDK, you must update the Info.plist file of your application. Add a new key-value pair where the key is specific to the Castled SDK’s configuration and the value explicitly indicates that swizzling should be turned off.

Info.plist
<key>CastledSwizzlingDisabled</key>
<true/>

Manual Handling of Push Notifications

Once swizzling is disabled, the responsibility of invoking the necessary methods falls on the developer. Developer needs to implement various UNUserNotificationCenterDelegate and UIApplicationDelegate methods to handle different notification scenarios.

Castled SDK exposes corresponding APIs to be called within these delegate methods to maintain the functionality that swizzling would have provided:

1. Handling Device Token Registration

When the device registers for push notifications, you should capture the device token and pass it to the Castled SDK using the setPushToken method.

class AppDelegate: UIResponder, UIApplicationDelegate , UNUserNotificationCenterDelegate {

    // MARK: - Registration for Push Notifications

    /// This method is called when the app has successfully registered with the Apple Push Notification service (APNs).
    /// If you have disabled swizzling, you should manually set the device token via the Castled SDK in this method.
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        let deviceTokenString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
        Castled.sharedInstance.setPushToken(deviceTokenString, type: .apns)
    }
}

2. Handling Registration Failure

If the device fails to register, log the error. There is no Castled method for this scenario as it is purely for logging purposes.

// MARK: - Handling Failure to Register for Notifications

/// This method is called if the application fails to be registered for remote notifications.
/// Use this method to handle the failure as necessary and log the error.
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
    print("Failed to register for remote notifications: \(error.localizedDescription)")
}

3. Handling Notification Responses

When the user interacts with the notification (e.g., tap on the notification), invoke the userNotificationCenter(didReceive:response) method from the Castled SDK.

// MARK: - Handling Notification Delivery When App is in Foreground

/// This method is called when a push notification is delivered to the app while it is in the foreground.
/// The Castled SDK should be informed so that it can handle the notification accordingly.
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    Castled.sharedInstance.userNotificationCenter(center, willPresent: notification)
    if #available(iOS 14.0, *) {
        // For iOS 14 and later
        completionHandler([.banner, .list, .badge, .sound])
    } else {
        // For iOS 13 and earlier
        completionHandler([.alert, .badge, .sound])
    }
}

4. Handling Foreground Notification Presentation

Determine how to handle notifications when the app is in the foreground with the userNotificationCenter(willPresent:notification) Castled method.

// MARK: - Handling User Interaction with Notifications

/// This method is called when the user interacts with a notification (e.g., taps on it).
/// Pass the interaction to the Castled SDK so it can handle any deep linking or notification response.
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    Castled.sharedInstance.userNotificationCenter(center, didReceive: response)
    completionHandler()
}

5. Handling Background Notifications

For handling background notification fetch calls, use the didReceiveRemoteNotification Castled method.

// MARK: - Handling Remote Notifications in the Background

/// This method is called when a remote notification is received and the app is running in the background.
/// It is crucial to inform the Castled SDK about the notification for proper processing.
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        
        Castled.sharedInstance.didReceiveRemoteNotification(userInfo)

        // Implement your logic here...

        completionHandler(.noData)
    }