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)
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()
}
}
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
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.