{"id":17995175,"url":"https://github.com/623637646/swifthook","last_synced_at":"2025-04-05T13:08:20.205Z","repository":{"id":45828663,"uuid":"212008633","full_name":"623637646/SwiftHook","owner":"623637646","description":"A library to hook methods in Swift and Objective-C. Making Aspect Oriented Programming (AOP) easy.","archived":false,"fork":false,"pushed_at":"2024-03-16T05:16:57.000Z","size":789,"stargazers_count":303,"open_issues_count":1,"forks_count":36,"subscribers_count":9,"default_branch":"master","last_synced_at":"2024-10-29T21:17:59.089Z","etag":null,"topics":["aop","aspects","hook","hooks","ios","kvo","libffi","objective-c","runtime","swift","swizzle","swizzling"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/623637646.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-10-01T03:54:57.000Z","updated_at":"2024-10-27T04:54:11.000Z","dependencies_parsed_at":"2023-12-25T12:26:05.463Z","dependency_job_id":"2098ce66-3084-49bc-bbe5-fa5bb402f95d","html_url":"https://github.com/623637646/SwiftHook","commit_stats":{"total_commits":427,"total_committers":5,"mean_commits":85.4,"dds":0.2997658079625293,"last_synced_commit":"4b0b3e38eac8d318e582cf03699812815e9e029c"},"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/623637646%2FSwiftHook","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/623637646%2FSwiftHook/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/623637646%2FSwiftHook/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/623637646%2FSwiftHook/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/623637646","download_url":"https://codeload.github.com/623637646/SwiftHook/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247339158,"owners_count":20923014,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aop","aspects","hook","hooks","ios","kvo","libffi","objective-c","runtime","swift","swizzle","swizzling"],"created_at":"2024-10-29T20:18:10.813Z","updated_at":"2025-04-05T13:08:20.181Z","avatar_url":"https://github.com/623637646.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"[中文](Documents/README.zh-Hans.md)\n\n# What is SwiftHook?\n\nA 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).\n\nIt’s based on Objective-C runtime and [libffi](https://github.com/libffi/libffi).\n\n# How to use SwiftHook\n\n1. Call the hook closure **before** executing **specified instance**’s method.\n\n```swift\nclass TestObject { // No need to inherit from NSObject.\n    // Use `@objc` to make this method accessible from Objective-C\n    // Using `dynamic` tells Swift to always refer to Objective-C dynamic dispatch\n    @objc dynamic func testMethod() {\n        print(\"Executing `testMethod`\")\n    }\n}\n\nlet obj = TestObject()\n\nlet token = try hookBefore(object: obj, selector: #selector(TestObject.testMethod)) {\n    print(\"Before executing `testMethod`\")\n}\n\nobj.testMethod()\ntoken.cancelHook() // cancel the hook\n```\n\n2. Call the hook closure **after** executing **specified instance**'s method and get the parameters.\n\n```swift\nclass TestObject {\n    @objc dynamic func testMethod(_ parameter: String) {\n        print(\"Executing `testMethod` with parameter: \\(parameter)\")\n    }\n}\n\nlet obj = TestObject()\n\nlet token = try hookAfter(object: obj, selector: #selector(TestObject.testMethod(_:)), closure: { obj, sel, parameter in\n    print(\"After executing `testMethod` with parameter: \\(parameter)\")\n} as @convention(block) ( // Using `@convention(block)` to declares a Swift closure as an Objective-C block\n    AnyObject, // `obj` Instance\n    Selector, // `testMethod` Selector\n    String // first parameter\n) -\u003e Void // return value\n)\n\nobj.testMethod(\"ABC\")\ntoken.cancelHook() // cancel the hook\n```\n\n3. Totally override the mehtod for **specified instance**.\n\n```swift\nclass Math {\n    @objc dynamic func double(_ number: Int) -\u003e Int {\n        let result = number * 2\n        print(\"Executing `double` with \\(number), result is \\(result)\")\n        return result\n    }\n}\n\nlet math = Math()\n\ntry hookInstead(object: math, selector: #selector(Math.double(_:)), closure: { original, obj, sel, number in\n    print(\"Before executing `double`\")\n    let originalResult = original(obj, sel, number)\n    print(\"After executing `double`, got result \\(originalResult)\")\n    print(\"Triple the number!\")\n    return number * 3\n} as @convention(block) (\n    (AnyObject, Selector, Int) -\u003e Int,  // original method block\n    AnyObject, // `math` Instance\n    Selector, // `sum` Selector\n    Int // number\n) -\u003e Int // return value\n)\n\nlet number = 3\nlet result = math.double(number)\nprint(\"Double \\(number), got \\(result)\")\n```\n\n4. Call the hook closure **before** executing the method for **all instances of the class**.\n\n```swift\nclass TestObject {\n    @objc dynamic func testMethod() {\n        print(\"Executing `testMethod`\")\n    }\n}\n\nlet token = try hookBefore(targetClass: TestObject.self, selector: #selector(TestObject.testMethod)) {\n    print(\"Before executing `testMethod`\")\n}\n\nlet obj = TestObject()\nobj.testMethod()\ntoken.cancelHook() // cancel the hook\n```\n\n5. Call the hook closure **before** executing the **class method**.\n\n```swift\nclass TestObject {\n    @objc dynamic class func testClassMethod() {\n        print(\"Executing `testClassMethod`\")\n    }\n    @objc dynamic static func testStaticMethod() {\n        print(\"Executing `testStaticMethod`\")\n    }\n}\n\ntry hookClassMethodBefore(targetClass: TestObject.self, selector: #selector(TestObject.testClassMethod)) {\n    print(\"Before executing `testClassMethod`\")\n}\nTestObject.testClassMethod()\n\ntry hookClassMethodBefore(targetClass: TestObject.self, selector: #selector(TestObject.testStaticMethod)) {\n    print(\"Before executing `testStaticMethod`\")\n}\nTestObject.testStaticMethod()\n```\n\n6. [Hooking the dealloc method](SwiftHookTests/SwiftAPITests/HookAllInstancesTests.swift#L252)\n7. [Hooking only once (Cancel the hook once triggered)](SwiftHookTests/SwiftAPITests/HookOnceTests.swift)\n8. [Using in Objective-C](SwiftHookTests/OCAPITests)\n\n# How to integrate SwiftHook?\n\n**SwiftHook** can be integrated by [cocoapods](https://cocoapods.org/). \n\n```\npod 'EasySwiftHook'\n```\n\nOr use Swift Package Manager. SPM is supported from **3.2.0**. Make sure your Xcode is greater than 12.5, otherwise it compiles error.\n\n# [Performance](Documents/PERFORMANCE.md)\n\nComparing with [Aspects](https://github.com/steipete/Aspects) (respect to Aspects).\n\n* Hook with Before and After mode for all instances, SwiftHook is **13 - 17 times** faster than Aspects.\n* Hook with Instead mode for all instances, SwiftHook is **3 - 5 times** faster than Aspects.\n* Hook with Before and After mode for specified instances, SwiftHook is **4 - 5 times** faster than Aspects.\n* Hook with Instead mode for specified instances, SwiftHook is **2 - 4 times** faster than Aspects.\n\n# Compatibility with KVO\n\nSwiftHook is full compatible with KVO from 3.0.0 version.\nFor more test cases: [Here](SwiftHookTests/Advanced/CompatibilityTests.swift)\n\n# We already have great [Aspects](https://github.com/steipete/Aspects). Why do I create SwiftHook?\n\n1. Aspects has some bugs. [Click here for test code](SwiftHookTests/AspectsTests/AspectsErrorTests.m).\n2. Aspects doesn’t support Swift with instead mode in some cases. [Click here for test code](SwiftHookTests/AspectsTests/AspectsSwiftTests.swift).\n3. Aspects’s API is not friendly for Swift.\n4. Aspects doesn’t support Swift object which is not based on NSObject.\n5. Aspects is based on *message forward*. This performance is not good.\n6. Aspects are no longer maintained. Author said: “**STRICTLY DO NOT RECOMMEND TO USE Aspects IN PRODUCTION CODE**”\n7. Aspects is not compatible with KVO.\n\nBTW, **Respect to Aspects!**\n\n# Requirements\n\n- iOS 12.0+, MacOS 10.13+ (Unverified for tvOS, watchOS)\n- Xcode 15.1+\n- Swift 5.0+\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F623637646%2Fswifthook","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F623637646%2Fswifthook","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F623637646%2Fswifthook/lists"}