Skip to content

A library to hook methods in Swift and Objective-C. Making Aspect Oriented Programming (AOP) easy.

License

Notifications You must be signed in to change notification settings

623637646/SwiftHook

Repository files navigation

中文

What is SwiftHook?

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.

How to use SwiftHook

  1. 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
  1. 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
  1. 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)")
  1. 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
  1. 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()
  1. Hooking the dealloc method
  2. Hooking only once (Cancel the hook once triggered)
  3. Using in Objective-C

How to integrate SwiftHook?

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.

Compatibility with KVO

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?

  1. Aspects has some bugs. Click here for test code.
  2. Aspects doesn’t support Swift with instead mode in some cases. Click here for test code.
  3. Aspects’s API is not friendly for Swift.
  4. Aspects doesn’t support Swift object which is not based on NSObject.
  5. Aspects is based on message forward. This performance is not good.
  6. Aspects are no longer maintained. Author said: “STRICTLY DO NOT RECOMMEND TO USE Aspects IN PRODUCTION CODE
  7. Aspects is not compatible with KVO.

BTW, Respect to Aspects!

Requirements

  • iOS 12.0+, MacOS 10.13+ (Unverified for tvOS, watchOS)
  • Xcode 15.1+
  • Swift 5.0+

About

A library to hook methods in Swift and Objective-C. Making Aspect Oriented Programming (AOP) easy.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published