9

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)
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Nitish
  • 13,845
  • 28
  • 135
  • 263

1 Answers1

7

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

Swift Native swizzling

As of Swift 5.1, there is a native version of method swizzling that does not rely on Objective-C’s message passing. The @_dynamicReplacement modifier can be used upon a replacement function, and takes as an argument the name of the function that it should replace. The function that is being replaced must be marked with the @dynamic modifier, unless -enable-implicit-dynamic compilation flag is used, which makes the compiler assume that every eligible entity has been marked with the modifier.

For example:

dynamic func original() {
    print("I am the original")
}

@_dynamicReplacement(for: original)
func replacement() {
    print("I am the replacement")
}

original() // prints "I am the replacement"

For more details on dynamic method replacement, visit this Swift forum page.

ielyamani
  • 17,807
  • 10
  • 55
  • 90
  • Can you walk me through your answer ? I am facing difficulty in converting and calling MPApplicationDidRegisterForRemoteNotificationsWithDeviceToken – Nitish Oct 03 '18 at 08:46
  • I've added a concrete example of swizzling methods – ielyamani Oct 03 '18 at 13:13