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.
To access a pointer to the ivar instead, use
Ivars.withIvar(_:_:)
.To fail gracefully if the ivar does not exist, use
Ivars.subscript(safelyAccessing:)
.To access an ivar with a name that clashes with an actual member on this type, it is possible to use
Ivars.subscript(dynamicMember:)
directly.
Example
See morelet object = MyObject(foo: 5) Ivars<Int>(object)._foo // 5 Ivars(object)._foo = 7 print(object.foo) // 7
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 ofDynamic
. 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 theclass
property. For example,Dynamic.TypeName.class
will return the class named “TypeName”. To cast to a more specific superclass type, useas(type:)
, for exampleDynamic.PrivateViewType.as(type: UIView.self)
.If the type name corresponds to an Objective-C protocol, the
protocol
property returns the type as aProtocol
.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]
, wheresomeInstanceMethod
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 toMyInterface
, and so it is now possible to directly call the method on it:converted.someInstanceMethod()
If
MyClass
conforms toNSObject
, a way to do this with even more syntactic sugar is to useNSObject.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 useDynamic.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
. IfMyClass
was accessible in Swift and conformed toNSObject
, 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 callalloc(interface:)
on the class or an instance ofDynamic
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
See moreMyClass
conforms toNSObject
, it is again possible to utilize additional syntactic sugar, in this case replacingDynamic.MyClass
with simplyMyClass
.Declaration
Swift
@dynamicMemberLookup public struct Dynamic
-
Declaration
Swift
extension NSObject