I use to swizzle main bundle with test bundle like follow in obj c
#import "NSBundle+Bundle.h"
#import <objc/runtime.h>
@implementation NSBundle (Bundle)
+(void)loadSwizzler {
static dispatch_once_t once_token;
dispatch_once(&once_token, ^{
Method originalMethod = class_getClassMethod(self, @selector(mainBundle));
Method extendedMethod = class_getClassMethod(self, @selector(bundleForTestTarget));
//swizzling mainBundle method with our own custom method
method_exchangeImplementations(originalMethod, extendedMethod);
});
}
//method for returning app Test target
+(NSBundle *)bundleForTestTarget {
NSBundle * bundle = [NSBundle bundleWithIdentifier:@"Philips.AppInfraTests"];
return bundle;
}
@end
But I tried the following for the same in swift
extension Bundle {
class func swizzle() {
let originalSelector = #selector(mainBundle)
let swizzledSelector = #selector(testBundle)
let originalMethod = class_getInstanceMethod(self, originalSelector)
let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
method_exchangeImplementations(originalMethod, swizzledMethod)
}
func mainBundle() -> Bundle
{
return Bundle.main
}
func testBundle() -> Bundle
{
return Bundle(for: self.classNamed("swizzler")!)
}
}
But this is throwing some errors "Argument of '#selector' cannot refer to variable 'testBundle'"
could some one help me how do I do it
This answer has been tested in Swift 3 & 4 Playground, any other version and YMMV.
Your Objective-C code swizzles two class methods, your Swift version attempts to swizzle two instance methods - so they are not doing the same thing.
You (probably) cannot swizzle a (pure) Swift function, you can swizzle Objective-C methods, this is due to the differences in how functions/methods are dispatched. So in Swift the replacement function must be marked @objc
in Swift 4 (it is optional and apparently harmless in Swift 3).
Swift renames mainBundle
to main
and surfaces it as a property, so to get the selector for mainBundle
you need to use getter: main
.
Combine the above and you get the following Playground code:
extension Bundle
{
class func swizzle()
{
let originalSelector = #selector(getter: main)
let swizzledSelector = #selector(testBundle)
let originalMethod = class_getClassMethod(self, originalSelector)!
let swizzledMethod = class_getClassMethod(self, swizzledSelector)!
method_exchangeImplementations(originalMethod, swizzledMethod)
}
@objc class func testBundle() -> Bundle
{
// just for testing in Playground
return Bundle(path: "/Applications/TextEdit.app")!
}
}
let b = Bundle.main
print(b)
Bundle.swizzle()
let c = Bundle.main
print(c)
which prints:
NSBundle </Applications/Xcode.app> (not yet loaded)
NSBundle </Applications/TextEdit.app> (not yet loaded)
Note that class_getClassMethod()
returns a Method?
and the above code forces this without any checks, those checks should exist in real code!
Finally note that your swizzle code assumes mainBundle
is implemented directly by NSBundle
and not one of its ancestors, that is probably a safe assumption in this case but is not always. See for example this question on doing swizzling safely.
HTH