ClassHook
public typealias ClassHook<Target> = ClassHookProtocol & _ClassHookClass<Target> where Target : AnyObject
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 the Target
type. For example, to hook MyClass
one could declare
class 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 use NSObject
if no class which is more specific is available
at compile-time), and provide the actual target class’ name by implementing
the static targetName
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
or static
. Methods which have
a visibility of fileprivate
or private
will be ignored by Orion and can
thus be used as helper methods.
Methods which have an Objective-C selector that begins with alloc
, new
,
copy
, or mutableCopy
, follow the appropriate Objective-C conventions
described here.
In the rare case that you need to manually override this behavior,
add // orion:returns_retained true
(or false
) above your method
declaration. These directives behave like NS_RETURNS_RETAINED
and
NS_RETURNS_NOT_RETAINED
respectively.
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 the orig
proxy. Similarly, the
superclass implementation can be accessed via the supr
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 example func initWithFrame(_ frame: CGRect) -> Target
. Inside
the method, your first statement should be a call to an initializer on supr
or orig
, after which you can configure target
as you desire before you
return it. To hook NSObject.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 (not deinit
).
For more information, see ClassHookProtocol.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 the Target
or targetName
as the base class, and declare the subclassMode
property with
a value of SubclassMode.createSubclass
(to automatically pick a subclass name)
or use SubclassMode.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")
}
}