Pure Swift tweak development library for iOS and macOS
Jinx is a library for tweak developers to write their tweaks in Swift.
- macOS device with Xcode 14 or newer
- (for using templates) Theos
- Struct-based hooks for ObjC messages and C functions
- Fearless speculative and dynamic hooking
- Supports batched hooking for ObjC messages
- Written in 100% Swift and Assembly
- No preprocessors— better for Xcode and fast compilation
- First tweak framework with SPM support =)
- Small, fast, and lightweight
- Easy templates for use with Theos (prefs or no prefs)
- Works on rootless even from non-rootless branches
- Includes preferences reader
- Download the latest archive from Releases
- Extract and run
python3 install_jinx.py
- Simply add this repo as a dependency to your Package.swift file
In the provided Tweak struct’s ctor function, initialise an instance of your hook, then run its .hook() function:
ExampleHook().hook()
ExampleHookGroup().hook()
ExampleHookFunc().hook()struct ExampleHook: Hook {
typealias T = @convention(c) (AnyObject, Selector) -> Bool
let cls: AnyClass? = objc_lookUpClass("SomeClass")
let sel: Selector = sel_registerName("someMethod")
let replace: T = { _, _ in true }
}One HookGroup-conforming struct can hold T0-T9 and corresponding sel and replace properties.
struct ExampleHookGroup: HookGroup {
typealias T0 = @convention(c) (AnyObject, Selector) -> Int
typealias T1 = @convention(c) (AnyObject, Selector) -> String
let cls: AnyClass? = objc_lookUpClass("SomeClass")
let sel0: Selector = sel_registerName("firstMethod")
let sel1: Selector = sel_registerName("secondMethod")
let replace0: T0 = { _, _ in 413 }
let replace1: T1 = { _, _ in "EMT!" }
struct ExampleHookFunc: HookFunc {
typealias T = @convention(c) (Int) -> Void
let name: String = "someFunc"
let image: String? = "/usr/lib/system/libsomething.dylib"
let replace: T = { _ in NSLog("Hej fra someFunc!") }
}For Hook and HookFunc, simply call orig() from within the replacement definition and pass the requisite parameters. For example, orig(obj, sel).
HookGroup uses orig0 through orig9. These are identical to the other protocols’ orig except for the name, which allows unambiguity as to which sub-hook it relates.
Inside a Hook or HookGroup replacement definition,
Ivar.get("ivarName", for: obj) // returns an optional
Ivar.set("ivarName", for: obj, to: 413)To use dynamic hooking (i.e., hooking a class and method determined at runtime), you can abstain from assigning a value to cls and/or sel in the struct definition and instead assign it during initialisation.
The same principle goes for HookGroup and HookFunc with their respective properties.
ExampleHook(cls: objc_lookUpClass(someString), sel: sel_registerName(anotherString)).hook()Preferences are stored in the JinxPreferences struct. Simply create an instance of this struct and use let isEnabled: Bool = prefs.get(for: "isEnabled", default: true)