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);}
Objective-C
swizzling using category
@interface cA : NSObject {}@end@implementation cA+(NSString*) cClassFooA { return @"class fooA";}-(NSString*) cFooA { return @"fooA";}@end@interface cB : NSObject {}@end@implementation cB+(NSString*) cClassFooB { return @"class fooB";}-(NSString*) cFooB { return @"fooB";}@end
NSObject+Swizzling.h
#import <Foundation/Foundation.h>@interface NSObject (Swizzling)+ (void)cExchangeClassWithCls1:(Class)cls1 Sel1:(SEL)sel1 Cls2:(Class)cls2 Sel2:(SEL)sel2;+ (void)cExchangeInstanceWithCls1:(Class)cls1 Sel1:(SEL)sel1 Cls2:(Class)cls2 Sel2:(SEL)sel2;@end
NSObject+Swizzling.m
#import "NSObject+Swizzling.h"#import <objc/runtime.h>@implementation NSObject (Swizzling)+ (void)cExchangeClassWithCls1:(Class)cls1 Sel1:(SEL)sel1 Cls2:(Class)cls2 Sel2:(SEL)sel2 { Method originalMethod = class_getClassMethod(cls1, sel1); Method swizzledMethod = class_getClassMethod(cls2, sel2); method_exchangeImplementations(originalMethod, swizzledMethod);}+ (void)cExchangeInstanceWithCls1:(Class)cls1 Sel1:(SEL)sel1 Cls2:(Class)cls2 Sel2:(SEL)sel2 { Method originalMethod = class_getInstanceMethod(cls1, sel1); Method swizzledMethod = class_getInstanceMethod(cls2, sel2); method_exchangeImplementations(originalMethod, swizzledMethod);}@end
using via Objective-C
- (void)testCExchangeClass { [NSObject cExchangeClassWithCls1:[cA class] Sel1:@selector(cClassFooA) Cls2:[cB class] Sel2:@selector(cClassFooB)]; XCTAssertEqual(@"class fooB", [cA cClassFooA]);}- (void)testCExchangeInstance { [NSObject cExchangeInstanceWithCls1:[cA class] Sel1:@selector(cFooA) Cls2:[cB class] Sel2:@selector(cFooB)]; XCTAssertEqual(@"fooB", [[[cA alloc] init] cFooA]);}
using via Swift
func testCExchangeClass() { NSObject.cExchangeClass(withCls1: sA.self, sel1: #selector(sA.sClassFooA), cls2: sB.self, sel2: #selector(sB.sClassFooB)) XCTAssertEqual("class fooB", sA.sClassFooA())}func testCExchangeInstance() { NSObject.cExchangeInstance(withCls1: sA.self, sel1: #selector(sA.sFooA), cls2: sB.self, sel2: #selector(sB.sFooB)) XCTAssertEqual("fooB", sA().sFooA())}