Skip to content

Instantly share code, notes, and snippets.

@leoniralves
Last active April 19, 2021 13:20
Show Gist options
  • Save leoniralves/63fa3d3c4e87e870d097db551cfc2e3b to your computer and use it in GitHub Desktop.
Save leoniralves/63fa3d3c4e87e870d097db551cfc2e3b to your computer and use it in GitHub Desktop.
import Foundation
/*
"Method swizzling is the process of changing the implementation of an existing selector.
It’s a technique made possible by the fact that method invocations in Objective-C can be
changed at runtime, by changing how selectors are mapped to underlying functions in a
class’s dispatch table."
Source: https://nshipster.com/method-swizzling/
"This is only possible because in Objective-C the method to call when a message is sent
to an object is resolved at runtime."
Source: https://nshint.io/blog/2019/04/08/testing-the-camera-on-the-simulator/
Objective-C Runtime: https://developer.apple.com/documentation/objectivec/objective-c_runtime
*/
struct Swizzler {
private let klass: AnyClass
/// Initializer
/// - Parameter klass: original class
init(klass: AnyClass) {
self.klass = klass
}
/// Exchanges Class Implementations (class methods) between two classes
///
/// Whenever the method in the original class is called, who responds first is the method in the swizzle class.
///
/// - Parameters:
/// - originalSelector: Class method in original class
/// - targetClass: New class with swizzle method
/// - swizzledSelector: Class method in new class
///
/// ```
/// Swizzler(klass: UIView.self).exchangeClassImplementations(
/// originalSelector: #selector(UIView.animate(withDuration:animations:completion:)),
/// targetClass: SwizzleUIView.self,
/// swizzledSelector: #selector(SwizzleUIView.se_animate(withDuration:animations:completion:))
/// )
/// ```
func exchangeClassImplementations(
originalSelector: Selector,
targetClass: AnyClass,
swizzledSelector: Selector
) {
let original = class_getClassMethod(
klass,
originalSelector
)
let swizzled = class_getClassMethod(
targetClass,
swizzledSelector
)
zip(original, swizzled).map {
method_exchangeImplementations($0.0, $0.1)
}
}
///
///
func exchangeInstaceImplementations(
originalSelector: Selector,
targetClass: AnyClass,
swizzledSelector: Selector
) {
let original = class_getInstanceMethod(
klass,
originalSelector
)
let swizzled =
class_getInstanceMethod(
targetClass,
swizzledSelector
)
zip(original, swizzled)
.map { method_exchangeImplementations($0.0, $0.1) }
}
///
///
func injectNSObjectInit(into selector: Selector) {
let original = [
class_getInstanceMethod(klass, selector)
].compactMap
let swizzled = [
class_getInstanceMethod(klass, #selector(NSObject.init))
].compactMap
zip(original, swizzled)
.forEach {
method_setImplementation($0.0, method_getImplementation($0.1))
}
}
}
private func zip<A, B>(_ a: A?, _ b: B?) -> (A, B)? {
guard let a = a, let b = b else { nil }
return (a, b)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment