How to swizzle a class method on iOS?
Turns out, I wasn't far away. This implementation works for me:
void SwizzleClassMethod(Class c, SEL orig, SEL new) {
Method origMethod = class_getClassMethod(c, orig);
Method newMethod = class_getClassMethod(c, new);
c = object_getClass((id)c);
if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
else
method_exchangeImplementations(origMethod, newMethod);
}
How to swizzle a method of a private class
Managed to get this to work, it's pretty simple actually.
So the way I did it:
- made a NSObject category:
@interface NSObject(PrivateSwizzleCategory)
swizzled:
+(void)load
{
Method original, swizzled;
original = class_getInstanceMethod(objc_getClass("SomePrivateClass"), @selector(somePrivateMethod:));
swizzled = class_getInstanceMethod(self, @selector(swizzled_somePrivateMethod:));
method_exchangeImplementations(original, swizzled);
}To call the original implementation, I had to cast self to NSObject:
id ret = [(NSObject *)self swizzled_somePrivateMethod:someParam];
To access private properties of the private class, I used valueForKey on self:
id privateProperty = [self valueForKey:@"__privateProperty"];
Everything works!
Swizzling is not working for class methods
Methods instance exist in dispatch table class, but
class methods exist in dispatch table meta_class so
you need use 'meta class' instead self(class).
#import "TNUserDetail.h"
#import <objc/runtime.h>
@implementation TNUserDetail
+ (void)swizzleInstanceSelector:(SEL)originalSelector withNewSelector:(SEL)newSelector {
const char *className = [NSStringFromClass(self) UTF8String];
Class clazz = objc_getMetaClass(className);
Method originalMethod = class_getClassMethod(clazz, originalSelector);
Method newMethod = class_getClassMethod(clazz, newSelector);
BOOL methodAdded = class_addMethod(clazz,
originalSelector,
method_getImplementation(newMethod),
method_getTypeEncoding(newMethod));
if (methodAdded) {
class_replaceMethod(clazz,
newSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, newMethod);
}
}
+ (void)load {
[super load];
[self swizzleInstanceSelector:@selector(printHello) withNewSelector:@selector(printHelloWorld)];
}
+ (void)printHello {
NSLog(@"Hello");
}
+ (void)printHelloWorld {
NSLog(@"Hello World");
}
@end
and call [TNUserDetail printHello];
print 'Hello World'
But your swizzling affects the entire project. For this case I recommendation use partial mocks (OCMock)
how to swizzle method of class with some customized method through extension
Refer to swift method_exchangeImplementations not work
By adding the dynamic declaration modifier, the swizzling happens properly. Without this, the call to method_exchangeImplementations() does not have the intended effect. See https://swiftunboxed.com/interop/objc-dynamic/ for more information about dynamic dispatch.
So like this:
@objc dynamic func name() {
print("this is class A")
}
ios swizzle better understanding
Method swizzling is used for overriding original methods with the custom one at runtime. So you can exchange almost any method, (including the private apple implemented ones) with the custom one you wrote.
So imagine there is class named Parent
with a method named A
and you exchange it with B
somewhere before it been called like inside load
method. From now on every sub class off 'Parent' will use B
except the original 'A' method. But what if you override A
in a child class? As Inheritance definition, objects will call their own methods and if they haven't implement it, they use their suprclass's method. So what if you want the parent implementation
? Thats where super
comes in.
Conclusion
- If you override a method, the super class (or the custom exchanged method in superclass) method will not getting called
- If you want the parent implementation, you have to use super keyword to access it
And in this questions case:
- Overriding a method in sub class without calling super means you just override swizzled method and it will not getting called.
Hope it helps
How to swizzle init method of NSURLConnection class
extension NSURLConnection{
public override class func initialize() {
struct Static {
static var token: dispatch_once_t = 0
}
if self !== NSURLConnection.self {
return
}
dispatch_once(&Static.token) {
let originalSelector = Selector("initWithRequest:delegate:startImmediately:")
let swizzledSelector = Selector("initWithTest:delegate:startImmediately:")
let originalMethod = class_getInstanceMethod(self, originalSelector)
let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
if didAddMethod {
class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
} else {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
}
// MARK: - Method Swizzling
convenience init(test: NSURLRequest, delegate: AnyObject?, startImmediately: Bool){
print("Inside Swizzled Method")
self.init()
}
}
Swift - method swizzling
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
Related Topics
Convert an iOS Objective C Object to a JSON String
Error: Error Domain=Nsurlerrordomain Code=-1001 "The Request Timed Out."
Firebase with Swift 3 Counting the Number of Children
Does This App Use the Advertising Identifier (IDFA)? - Admob 6.8.0
What Is the "Right" Way to Handle Orientation Changes in iOS 8
How to Prevent the iPhone Screen from Dimming or Turning Off While My Application Is Running
Uitapgesturerecognizer Tap on Self.View But Ignore Subviews
Auto-Layout: What Creates Constraints Named Uiview-Encapsulated-Layout-Width & Height
Why am I Getting Ibtool Failed with Exit Code 255
iOS Touch Event Notifications (Private API)
How to Set Uibutton Background Color Forstate: Uicontrolstate.Highlighted in Swift
Xctest and Asynchronous Testing in Xcode 6
Error: Ld: Library Not Found for -Lpods with Cocoapods
Spritekit - Not Loading @3X Images from Sktextureatlas
How to Mutate Structs in Swift Using Map
Cannot Subscript a Value of Type '[Nsobject:Anyobject]' with an Index of Type 'String'
Unexpected Non-Void Return Value in Void Function Swift3
Swiftui - How to Initialize an Observedobject Using an Environmentobject as a Parameter