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.