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 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.

    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 UILabels 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
  • The protocol to which class hooks conform. Do not conform to this directly; use ClassHook.

    See more

    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 to ClassHook.

    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, see Property.Assign, Property.Atomicity, and Property.RetainOrCopy.

    The default attributes are atomic and retain. Specifying assign will replace both defaults, and specifying an atomicity and/or retain policy will only override the respective default.

    Example

    @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)
    
    See more

    Declaration

    Swift

    @propertyWrapper
    public struct Property<T>
  • An enumeration describing a ClassHook‘s subclassing behavior.

    See more

    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 callOrig.

    See more

    Declaration

    Swift

    public enum DeinitPolicy