Class Hooking
-
The base class hook type. All class hooks must subclass this.
This type allows hooking methods of a chosen target class. For a detailed description of all available configuration options, see
ClassHookProtocol
.Specifying a Target Class
In order to hook a class which is known by the Swift compiler at compile-time, specialize
ClassHook
with that class as theTarget
type. For example, to hookMyClass
one could declareclass MyHook: ClassHook<MyClass>
.In order to hook a class which is not known by the Swift compiler at compile-time, specify a class in its inheritance chain as
Target
(you can useNSObject
if no class which is more specific is available at compile-time), and provide the actual target class’ name by implementing the statictargetName
property.Hooking Methods
Instance/Class Methods
To hook an instance method on the target class, simply declare an instance method with the same name and method signature in your hook class. The contents of your method will replace the original method implementation.
In order to hook a class method, declare the replacement method as a
class
method.In either case, the method must not be
final
orstatic
. Methods which have a visibility offileprivate
orprivate
will be ignored by Orion and can thus be used as helper methods.Accessing Target Information
Within a method hook function, the object upon which the hooked method was invoked can be accessed via
target
. To call the original implementation of the method, call the method itself on theorig
proxy. Similarly, the superclass implementation can be accessed via thesupr
proxy.Method Naming
To figure out the required Swift name for an Objective-C method, you may want to follow the way that Objective-C APIs are renamed when they are imported into Swift, in reverse. If you cannot figure out the Swift name for the method, you can also provide the Objective-C selector name directly by declaring the function as
@objc(selector:name:) func
.Initializers
In order to hook an initializer with Orion, declare an instance method with the initializer’s name and Objective-C method signature, with a return type of
Target
. For examplefunc initWithFrame(_ frame: CGRect) -> Target
. Inside the method, your first statement should be a call to an initializer onsupr
ororig
, after which you can configuretarget
as you desire before you return it. To hookNSObject.init
, use backticks to escape the init keyword:func `init`() -> Target
.Example of hooking
initWithFrame
:class ViewHook: ClassHook<UIView> { func initWithFrame(_ frame: CGRect) -> Target { let target = orig.initWithFrame(frame) // configure target return target } }
Deinitializers
If you find the need to perform custom behavior when a target object is deallocated, you can declare a
deinitializer
function (notdeinit
). For more information, seeClassHookProtocol.deinitializer()
.Adding to the Class
Properties
Use the
@Property
property wrapper. For more information, refer to its documentation.Protocols
You can specify a list of protocols for which conformance will be added to the class, by declaring the
ClassHookProtocol.protocols
property.New Methods
To add a new method to the target class, simply declare it on the hook and mark it as
final
. This may be useful, for example, to make the target class conform to a protocol requirement.Creating Subclasses
In some situations, you may need to declare a subclass for a base class which is not known at compile-time. In this case, create a
ClassHook
with theTarget
ortargetName
as the base class, and declare thesubclassMode
property with a value ofSubclassMode.createSubclass
(to automatically pick a subclass name) or useSubclassMode.createSubclassNamed(_:)
if you need a specific name.The static
target
property will refer to the subclass type. It is possible to add methods, properties, protocols, and so on to the subclass as described above.Example
To change the text of all
UILabel
s to “hello”, one could write something like this:class MyHook: ClassHook<UILabel> { func setText(_ text: String) { orig.setText("hello") } }
Declaration
Swift
public typealias ClassHook<Target> = ClassHookClass<Target> & ClassHookProtocol where Target : AnyObject
-
Declaration
Swift
public protocol ClassHookProtocol : AnyObject, AnyHook
-
A property wrapper which allows
ClassHook
types to add new properties to their hooked class.This type is an ergonomic wrapper around Objective-C associated objects. The type of the associated object is the generic argument
T
.If you are declaring a property on a
ClassHook
, you likely want to use this. Note that this property only works on types which conform toClassHook
.Important
All properties with this attribute must have a default value.Attributes
This property wrapper gives you the ability to specify attributes on the property, which are similar to those used by Objective-C’s
@property
. For a description of each attribute, seeProperty.Assign
,Property.Atomicity
, andProperty.RetainOrCopy
.The default attributes are
atomic
andretain
. Specifyingassign
will replace both defaults, and specifying an atomicity and/or retain policy will only override the respective default.Example
See more@objcMembers class Person: NSObject { dynamic func sayHello() -> String { "hello" } } class PersonHook: ClassHook<Person> { @Property(.nonatomic) var x = 0 func sayHello() -> String { x += 1 return "Hi! I've been called \(x) time(s)" } } // later... let alice = Person() alice.sayHello() // Hi! I've been called 1 time(s) alice.sayHello() // Hi! I've been called 2 time(s) let bob = Person() bob.sayHello() // Hi! I've been called 1 time(s) bob.sayHello() // Hi! I've been called 2 time(s)
Declaration
Swift
@propertyWrapper public struct Property<T>
-
Declaration
Swift
public enum SubclassMode
-
The action to perform after a
ClassHookProtocol.deinitializer()
is run.Unless you’re managing the target class’ own resources, you usually want to use
See morecallOrig
.Declaration
Swift
public enum DeinitPolicy