Let's craft a class especially for our demonstration purposes. It has the following features:
- It is a generic class, similar to Swift's generic classes
- It exposes generic types (in the form of arrays)
- It exposes a generic type that is bound to the classes' own generics
Consider the following MyGeneric class:
NS_ASSUME_NONNULL_BEGIN
@interface MyGeneric<__covariant T> : NSObject
@property (nullable, nonatomic, retain) NSArray<T> *genericArray;
- (NSArray *)untypedArray;
- (NSArray<NSString *> *)stringArray;
- (NSArray<__kindof NSString *> *)kindofStringArray;
@end
NS_ASSUME_NONNULL_END
The previous code is a fully generic Objective-C class and introduces two new keywords:
- __covariant is used to make the MyGeneric class generic with a type that will be determined when creating an instance
- __kindof is used to allow not only the provided type (NSString *) but also all subclasses.
In Objective-C, we can now use it this way:
MyGeneric * untypedGeneric = [MyGeneric new];
NSArray* objects = [untypedGeneric genericArray];
MyGeneric<NSNumber *> * numberGeneric = [MyGeneric new];
NSArray<NSNumber *> * numbers = [numberGeneric genericArray];
As you can see, in Objective-C, you can always use the non-generic type; the compiler doesn't force you to specialize. You can also do unsafe casts from one type to another:
MyGeneric<NSNumber *> * casted = (MyGeneric<NSNumber *> *) untypedGeneric;
This is valid Objective-C code, but will likely crash at runtime when accessing the objects.
And from Swift's perspective, it is as follows:
let generic = MyGeneric<NSNumber>()
assert(generic.untypedArray() is [Any])
assert(generic.stringArray() is [String])
assert(generic.kindofStringArray() is [String])
assert(generic.genericArray is [NSNumber]?)
There are multiple things to note from the Swift code:
- You cannot create an instance of MyGeneric without the specialization to a particular type
- You cannot create an instance of MyGeneric with a value type as a specialization; for example, MyGeneric<String>() won't work
- You cannot create extensions of MyGeneric in Swift, you'll be hit by: Extension of a generic Objective-C class cannot access the class's generic parameters at runtime
With that reference in mind, you can come back to it whenever you need to bridge Objective-C code in Swift. The last step in our introduction is to cover the basic Cocoa design patterns, how they translate and apply to Swift, and how to put them in practice safely and efficiently.