Estoy tratando de cambiar el método de un marco privado para realizar una lógica personalizada y luego me gustaría llamar a la implementación original.
Código:
class SwizzlingHelper { private struct Constants { static let privateFrameworkClassName = "privateFrameworkClassName" static let swizzledMethodSignature = "swizzledMethodSignature:" } static func swizzle() { let originalSelector = NSSelectorFromString(Constants.swizzledMethodSignature) if let swizzlingClass: AnyClass = NSClassFromString(Constants.privateFrameworkClassName), let originalMethod = class_getInstanceMethod(swizzlingClass.self, originalSelector), let swizzledMethod = class_getClassMethod(SwizzlingHelper.self, #selector(swizzledMethod)) { method_exchangeImplementations(originalMethod, swizzledMethod) } } @objc private static func swizzledMethod(_ arg: String) { // Custom logic // Call original method here - how? } } He visto varios ejemplos de cómo se realiza el swizzling extendiendo una clase y luego invocando la implementación original llamando a self.originalImplementation() dentro del método swizzled. Dado que esta es una clase de marco privado, no puedo extenderla y, por lo tanto, la clase SwizzlingHelper ayuda a ayudar con el swizzling. Sin embargo, no hay acceso a self dentro del método swizzled para llamar a la implementación original.
Cualquier pista será apreciada. ¡Gracias!
Realmente no veo el propósito de swizzling en este escenario. Si desea ejecutar la lógica del método original al final, entonces el swizzling no tiene mucho sentido para mí. Parece que podría simplemente pasar un bloque que ejecuta el método del marco después de ejecutar su propia lógica.
Dicho esto, se me ocurrió esto:
typealias PrivateClassFunction = @convention(c) () -> Void class SwizzingHelper { private let originalMethod: PrivateClassFunction init(forClass: String, methodSignature: String) { let originalSelector: Selector = NSSelectorFromString(methodSignature) guard let swizzledClass = NSClassFromString(forClass), let swizzledMethod = class_getInstanceMethod(SwizzingHelper.self, #selector(run)), let originalMethod = class_getInstanceMethod(swizzledClass.self, originalSelector) else { fatalError() } self.originalMethod = unsafeBitCast(originalMethod, to: PrivateClassFunction.self) method_exchangeImplementations(originalMethod, swizzledMethod) } @objc func run() { print("Running new logic") originalMethod() } } Pasar el nombre de la clase y las cadenas de selección solo lo hace un poco más flexible en caso de que desee cambiar otros métodos/clases en el futuro. También podría idear alguna forma de hacer que PrivateClassFunction sea más variable (tal como está, querrá ajustar eso para que coincida con los parámetros y el tipo de retorno de los métodos de su marco privado).
Ejecutará el código así:
let swizzler = SwizzingHelper(forClass: "PrivateClass", methodSignature: "privateClassMethodWithArg:") swizzler.run()