Generic classes in Objective-C

Let's craft a class especially for our demonstration purposes. It has the following features:

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:

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:

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.