Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift - method swizzling

This is the method swizzling code written in Objective-C. I am having a hard time converting this in Swift.

void MPApplicationDidRegisterForRemoteNotificationsWithDeviceToken(id self, SEL _cmd, UIApplication *application, NSData *deviceToken) {
    [[MPPush shared] appRegisteredForRemoteNotificationsWithDeviceToken:deviceToken];

    IMP original = [MPAppDelegateProxy originalImplementation:_cmd class:[self class]];
    if (original)
        ((void(*)(id, SEL, UIApplication *, NSData*))original)(self, _cmd, application, deviceToken);
}  

Swiftify isn't converting the above code correctly.

I tried to do this but I am not sure how to pass parameters and use the exact above parameters in Swift swizzling method. This is my fail attempt to convert the above in Swift (code doesn't even compile) :

var MPApplicationDidRegisterForRemoteNotificationsWithDeviceToken: Void {
        //     TODO:   MPPush.shared.app

        let original = MPAppDelegateProxy.proxyAppDelegate.originalImplementation(selector: cmd, forClass: type(of: self))
    }(self: Any, _cmd: Selector, application: UIApplication, deviceToken: Data)
like image 455
Nitish Avatar asked Sep 17 '18 11:09

Nitish


People also ask

What is method swizzling in Swift?

Swizzling (other languages call this “monkey patching”) is the process of replacing a certain functionality or adding custom code before the original code is called. For example, you could swizzle UIViewController. viewDidAppear to be informed whenever a view controller is displayed.

How does method swizzling work?

Method swizzling is the process of changing the implementation of an existing selector. It's a technique made possible by the fact that method invocations in Objective-C can be changed at runtime, by changing how selectors are mapped to underlying functions in a class's dispatch table.

How do I disable method swizzling?

Method swizzling in Firebase Cloud Messaging Developers who prefer not to use swizzling can disable it by adding the flag FirebaseAppDelegateProxyEnabled in the app's Info. plist file and setting it to NO (boolean value).


1 Answers

Extend your class:

extension YourClassName {
    static let classInit: () -> () = {
        let originalSelector = #selector(originalFunction)
        let swizzledSelector = #selector(swizzledFunction)
        swizzle(YourClassName.self, originalSelector, swizzledSelector)
    }

    @objc func swizzledFunction() {
        //Your new implementation
    }
}

Your class (YourClassName) should inherit from NSObject and originalSelector should be a dynamic method.

swizzle is a closure that exchanges the implementations:

private let swizzle: (AnyClass, Selector, Selector) -> () = { fromClass, originalSelector, swizzledSelector in
    guard 
        let originalMethod = class_getInstanceMethod(fromClass, originalSelector),
        let swizzledMethod = class_getInstanceMethod(fromClass, swizzledSelector)
        else { return }
    method_exchangeImplementations(originalMethod, swizzledMethod)
}

You could define swizzle in the class where you are going to do the swizzling. For example the AppDelegate class definition. And then do the swizzling in AppDelegate.init():

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    override init() {
        super.init()
        YourClassName.classInit()
    }
}

Example

Here is a concrete example of swizzling, it exchanges the implementation of two methods that take one argument:

class Example: NSObject {
    @objc dynamic func sayHi(to name: String) {
        print("Hi", name)
    }
}

extension Example {
    public class func swizzleMethod() {
        guard
            let originalMethod = class_getInstanceMethod(Example.self, #selector(Example.sayHi(to:))),
            let swizzledMethod = class_getInstanceMethod(Example.self, #selector(Example.sayHello(to:)))
            else { return }
        method_exchangeImplementations(originalMethod, swizzledMethod)
    }
    @objc func sayHello(to name: String) {
        print("Hello", name)
    }
}

Example.swizzleMethod()

let a = Example()
a.sayHi(to: "Nitish")        //Hello Nitish
like image 103
ielyamani Avatar answered Sep 28 '22 07:09

ielyamani