1 // Copyright 2015-present 650 Industries. All rights reserved.
2 
3 import Foundation
4 
5 class EXDevLauncherUtils {
6   /**
7    Swizzles implementations of given selectors.
8    */
swizzlenull9   static func swizzle(selector selectorA: Selector, withSelector selectorB: Selector, forClass: AnyClass) {
10     if let methodA = class_getInstanceMethod(forClass, selectorA),
11       let methodB = class_getInstanceMethod(forClass, selectorB) {
12       let impA = method_getImplementation(methodA)
13       let argsTypeA = method_getTypeEncoding(methodA)
14 
15       let impB = method_getImplementation(methodB)
16       let argsTypeB = method_getTypeEncoding(methodB)
17 
18       if class_addMethod(forClass, selectorA, impB, argsTypeB) {
19         class_replaceMethod(forClass, selectorB, impA, argsTypeA)
20       } else {
21         method_exchangeImplementations(methodA, methodB)
22       }
23     }
24   }
25 
26   /**
27    Swizzles implementations of given class method selectors.
28    This function will backup original selector implementation for `invokeOriginalClassMethod`.
29    */
swizzleClassMethodnull30   static func swizzleClassMethod(selector selectorA: Selector, withSelector selectorB: Selector, forClass: AnyClass) {
31     if let methodA = class_getClassMethod(forClass, selectorA),
32       let methodB = class_getClassMethod(forClass, selectorB) {
33       let impA = method_getImplementation(methodA)
34       let backupSelectorA = NSSelectorFromString("_" + NSStringFromSelector(selectorA))
35       let metaClass = objc_getMetaClass(String(describing: forClass)) as? AnyClass
36       class_addMethod(metaClass, backupSelectorA, impA, method_getTypeEncoding(methodA))
37       method_setImplementation(methodA, method_getImplementation(methodB))
38     }
39   }
40 
41   /**
42    Invokes the original implementation before swizzling for the given selector
43    */
invokeOriginalClassMethodnull44   static func invokeOriginalClassMethod(selector: Selector, forClass: AnyClass) throws -> Any? {
45     typealias ClassMethod = @convention(c) (AnyObject, Selector) -> Any
46     let imp = try getOriginalClassMethodImp(selector: selector, forClass: forClass)
47     return unsafeBitCast(imp, to: ClassMethod.self)(self, selector)
48   }
49 
50   /**
51    Invokes the original implementation before swizzling for the given selector
52    */
invokeOriginalClassMethodnull53   static func invokeOriginalClassMethod(selector: Selector, forClass: AnyClass, A0: Any) throws -> Any? {
54     typealias ClassMethod = @convention(c) (AnyObject, Selector, Any) -> Any
55     let imp = try getOriginalClassMethodImp(selector: selector, forClass: forClass)
56     return unsafeBitCast(imp, to: ClassMethod.self)(self, selector, A0)
57   }
58 
getOriginalClassMethodImpnull59   private static func getOriginalClassMethodImp(selector: Selector, forClass: AnyClass) throws -> IMP {
60     let backupSelector = NSSelectorFromString("_" + NSStringFromSelector(selector))
61     guard let method = class_getClassMethod(forClass, backupSelector) else {
62       fatalError("Backup selector does not exist - forClass[\(forClass)] backupSelector[\(NSStringFromSelector(backupSelector))]")
63     }
64     return method_getImplementation(method)
65   }
66 
resourcesBundlenull67   static func resourcesBundle() -> Bundle? {
68     let frameworkBundle = Bundle(for: EXDevLauncherUtils.self)
69 
70     guard let resourcesBundleUrl = frameworkBundle.url(forResource: "EXDevLauncher", withExtension: "bundle") else {
71       return nil
72     }
73     return Bundle(url: resourcesBundleUrl)
74   }
75 }
76