iosswiftswizzlingmethod-swizzlingswizzle

how to swizzle didReceiveRemoteNotification of AppDelegate from framework


I'm developing a framework for iOS apps (a pod). I want to swizzle

application(_:didReceiveRemoteNotification:fetchCompletionHandler:)

with a method defined in my framework. this is my code:

class MyClass {
    @objc
    func myCustomizedMethod(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        // my code
    }

    private func swizzleDidReceiveRemoteNotification() {
        guard let appDelegateClass = object_getClass(UIApplication.shared.delegate) else { return }

        let originalSelector = #selector((appDelegateClass as! UIApplicationDelegate).application(_:didReceiveRemoteNotification:fetchCompletionHandler:))
        let swizzledSelector = #selector(MyClass.self.myCustomizedMethod(_:didReceiveRemoteNotification:fetchCompletionHandler:))

        guard let originalMethod = class_getInstanceMethod(appDelegateClass, originalSelector) else { return }
        guard let swizzledMethod = class_getInstanceMethod(MyClass.self, swizzledSelector) else { return }

        method_exchangeImplementations(originalMethod, swizzledMethod)
    }
}

but when I run my code, it appears that originalMethod has nil value and so

class_getInstanceMethod(appDelegateClass, originalSelector)

returns nil. what I'm doing wrong? (please consider that I don't have access to AppDelegate, because as I said, I'm developing a framework)


Solution

  • this is the code that worked for me:

    class MyClass {
        @objc
        func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
            // my code
        }
    
        private func swizzleDidReceiveRemoteNotification() {
            let appDelegate = UIApplication.shared.delegate
            let appDelegateClass = object_getClass(appDelegate)
    
            let originalSelector = #selector(UIApplicationDelegate.application(_:didReceiveRemoteNotification:fetchCompletionHandler:))
            let swizzledSelector = #selector(MyClass.self.application(_:didReceiveRemoteNotification:fetchCompletionHandler:))
    
            guard let swizzledMethod = class_getInstanceMethod(MyClass.self, swizzledSelector) else {
                return
            }
    
            if let originalMethod = class_getInstanceMethod(appDelegateClass, originalSelector)  {
                // exchange implementation
                method_exchangeImplementations(originalMethod, swizzledMethod)
            } else {
                // add implementation
                class_addMethod(appDelegateClass, swizzledSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
            }
        }
    }