Utilities

  • A wrapper around an object for accessing ivars on it.

    This type supports dynamic member lookup, which means that ivars can be accessed as if they were members of the type. Note that if an ivar with the given name does not exist, the dynamic member access will result in a crash.

    Example

    let object = MyObject(foo: 5)
    Ivars<Int>(object)._foo // 5
    Ivars(object)._foo = 7
    print(object.foo) // 7
    
    See more

    Declaration

    Swift

    @dynamicMemberLookup
    public struct Ivars<IvarType>
  • A wrapper enabling ergonomic dynamic Objective-C lookup.

    This type can be used as syntactic sugar to access ObjC classes and protocols by name, and also to call undeclared methods on them in a relatively type-safe manner.

    This type is inspired by mhdhejazi/Dynamic.

    Obtaining an Instance

    Use Dynamic.TypeName to get an instance of Dynamic. Accessing classes/protocols with dots in their name can be done in the same way: e.g. Dynamic.Foo.Bar.

    You can also get an instance of Dynamic by passing a string, e.g. Dynamic("Foo.Bar"), or even by passing an existing class: Dynamic(NSString.self).

    Getting the Underlying Type

    If the type name corresponds to an Objective-C class, it is possible to retrieve an AnyClass from the type using the class property. For example, Dynamic.TypeName.class will return the class named “TypeName”. To cast to a more specific superclass type, use as(type:), for example Dynamic.PrivateViewType.as(type: UIView.self).

    If the type name corresponds to an Objective-C protocol, the protocol property returns the type as a Protocol.

    Calling Undeclared Instance Methods

    The first step to calling an undeclared method on the type (one that is not known to the Swift compiler) is to declare an “interface” with the method. An interface is simply an Objective-C protocol.

    Say you wanted to call -[MyClass someInstanceMethod], where someInstanceMethod is not exposed to Swift. First, declare the interface:

    @objc protocol MyInterfaceA {
        func someInstanceMethod()
    }
    

    Then, use Dynamic.convert(_:to:) to “cast” the object to that interface:

    let obj = MyClass()
    let converted = Dynamic.convert(obj, to: MyInterfaceA.self)
    

    Swift sees converted as a type conforming to MyInterface, and so it is now possible to directly call the method on it:

    converted.someInstanceMethod()
    

    If MyClass conforms to NSObject, a way to do this with even more syntactic sugar is to use NSObject.as(interface:):

    MyClass().as(interface: MyInterfaceA.self).someInstanceMethod()
    

    Calling Undeclared Class Methods

    Calling class methods is similar to calling instance methods. Say you wanted to call +[MyClass someClassMethod] where neither the class nor the method is publicly exposed to Swift. First, declare an interface:

    @objc protocol MyInterfaceB {
        func someClassMethod()
    }
    

    Note that the method is declared as an instance method in the protocol, even though it is actually a class method.

    Next, obtain an instance of Dynamic corresponding to the type, and use Dynamic.as(interface:) to cast it to the interface.

    let converted = Dynamic.MyClass.as(interface: MyInterfaceB.self)
    

    Following this, it is possible to call methods on the type which have been declared on the interface:

    converted.someClassMethod()
    

    As with instance methods, class methods too have syntactic sugar available via an extension on NSObject. If MyClass was accessible in Swift and conformed to NSObject, the private method could be called as follows:

    MyClass.as(interface: MyInterfaceB.self).someClassMethod()
    

    Calling Initializers

    In order to call a private initializer using Dynamic, declare an interface with the initializer method (escaping `init` with backticks if required). Then call alloc(interface:) on the class or an instance of Dynamic corresponding to it, passing in the interface. Finally, call the initializer method on the returned object.

    For example, initializing MyClass using -[MyClass initWithString:] would look like this:

    @objc protocol MyInterfaceC {
        func initWithString(_ string: String)
    }
    
    let obj = Dynamic.MyClass
        .alloc(interface: MyInterfaceC.self)
        .initWithString("hello")
    

    Since MyClass conforms to NSObject, it is again possible to utilize additional syntactic sugar, in this case replacing Dynamic.MyClass with simply MyClass.

    See more

    Declaration

    Swift

    @dynamicMemberLookup
    public struct Dynamic
  • Declaration

    Swift

    extension NSObject