A secure, simple, and efficient Swift/Objective-C hook library that dynamically modifies the methods of a specific object or all objects of a class. It supports both Swift and Objective-C and has excellent compatibility with Key-Value Observing (KVO).
It’s based on Objective-C runtime and libffi.
- Call the hook closure before executing specified instance’s method.
class TestObject { // No need to inherit from NSObject.
// Use `@objc` to make this method accessible from Objective-C
// Using `dynamic` tells Swift to always refer to Objective-C dynamic dispatch
@objc dynamic func testMethod() {
print("Executing `testMethod`")
}
}
let obj = TestObject()
let token = try hookBefore(object: obj, selector: #selector(TestObject.testMethod)) {
print("Before executing `testMethod`")
}
obj.testMethod()
token.cancelHook() // cancel the hook
- Call the hook closure after executing specified instance's method and get the parameters.
class TestObject {
@objc dynamic func testMethod(_ parameter: String) {
print("Executing `testMethod` with parameter: \(parameter)")
}
}
let obj = TestObject()
let token = try hookAfter(object: obj, selector: #selector(TestObject.testMethod(_:)), closure: { obj, sel, parameter in
print("After executing `testMethod` with parameter: \(parameter)")
} as @convention(block) ( // Using `@convention(block)` to declares a Swift closure as an Objective-C block
AnyObject, // `obj` Instance
Selector, // `testMethod` Selector
String // first parameter
) -> Void // return value
)
obj.testMethod("ABC")
token.cancelHook() // cancel the hook
- Totally override the mehtod for specified instance.
class Math {
@objc dynamic func double(_ number: Int) -> Int {
let result = number * 2
print("Executing `double` with \(number), result is \(result)")
return result
}
}
let math = Math()
try hookInstead(object: math, selector: #selector(Math.double(_:)), closure: { original, obj, sel, number in
print("Before executing `double`")
let originalResult = original(obj, sel, number)
print("After executing `double`, got result \(originalResult)")
print("Triple the number!")
return number * 3
} as @convention(block) (
(AnyObject, Selector, Int) -> Int, // original method block
AnyObject, // `math` Instance
Selector, // `sum` Selector
Int // number
) -> Int // return value
)
let number = 3
let result = math.double(number)
print("Double \(number), got \(result)")
- Call the hook closure before executing the method for all instances of the class.
class TestObject {
@objc dynamic func testMethod() {
print("Executing `testMethod`")
}
}
let token = try hookBefore(targetClass: TestObject.self, selector: #selector(TestObject.testMethod)) {
print("Before executing `testMethod`")
}
let obj = TestObject()
obj.testMethod()
token.cancelHook() // cancel the hook
- Call the hook closure before executing the class method.
class TestObject {
@objc dynamic class func testClassMethod() {
print("Executing `testClassMethod`")
}
@objc dynamic static func testStaticMethod() {
print("Executing `testStaticMethod`")
}
}
try hookClassMethodBefore(targetClass: TestObject.self, selector: #selector(TestObject.testClassMethod)) {
print("Before executing `testClassMethod`")
}
TestObject.testClassMethod()
try hookClassMethodBefore(targetClass: TestObject.self, selector: #selector(TestObject.testStaticMethod)) {
print("Before executing `testStaticMethod`")
}
TestObject.testStaticMethod()
SwiftHook can be integrated by cocoapods.
pod 'EasySwiftHook'
Or use Swift Package Manager. SPM is supported from 3.2.0. Make sure your Xcode is greater than 12.5, otherwise it compiles error.
Comparing with Aspects (respect to Aspects).
- Hook with Before and After mode for all instances, SwiftHook is 13 - 17 times faster than Aspects.
- Hook with Instead mode for all instances, SwiftHook is 3 - 5 times faster than Aspects.
- Hook with Before and After mode for specified instances, SwiftHook is 4 - 5 times faster than Aspects.
- Hook with Instead mode for specified instances, SwiftHook is 2 - 4 times faster than Aspects.
SwiftHook is full compatible with KVO from 3.0.0 version. For more test cases: Here
We already have great Aspects. Why do I create SwiftHook?
- Aspects has some bugs. Click here for test code.
- Aspects doesn’t support Swift with instead mode in some cases. Click here for test code.
- Aspects’s API is not friendly for Swift.
- Aspects doesn’t support Swift object which is not based on NSObject.
- Aspects is based on message forward. This performance is not good.
- Aspects are no longer maintained. Author said: “STRICTLY DO NOT RECOMMEND TO USE Aspects IN PRODUCTION CODE”
- Aspects is not compatible with KVO.
BTW, Respect to Aspects!
- iOS 12.0+, MacOS 10.13+ (Unverified for tvOS, watchOS)
- Xcode 15.1+
- Swift 5.0+