The tale of JSONModel and the type-hinted arrays

Lately, I've been working a lot with JSON (see Intro-to-jq), and this led me to the excellent Objective-C library, JSONModel.

One thing I couldn't find documented was how I could type-hint array properties so that I didn't need to manually cast them to the type I knew JSONModel was producing.

The problem

The docs tell you how to make JSONModel aware of the type of your array properties, using a @protocol, but this does nothing to inform the compiler:

@protocol ProductModel
@end

@interface ProductModel : JSONModel
@property (strong, nonatomic) NSString *name;
@end

@interface OrderModel : JSONModel
@property (strong, nonatomic) NSArray<ProductModel> *products;
@end

This allows you to do the following (implicitly casting to ProductModel * from id):

OrderModel *order = …;
for (ProductModel *product in order.products) {
  NSLog(@\"Found product: %@\", product.name);
}

However, what won't work, is accessing the objects in the array directly and trying to access their properties.

This will not work:

OrderModel *order = …;
if (order.products.count > 0) {
  NSLog(@\"Found first product: %@\", order.products[0].name);
}

This is due to the compiler only expecting objects of type id in the array.

Research

I tried all sorts of combinations to get this working, putting the type hint on the array with the protocol on the inner objects, attempting to join them with commas, all sorts. I referred back to the documentation, and found nothing.

On a whim, I tried the following:

@property (strong, nonatomic) NSArray<ProductModel><ProductModel *> *products;

Xcode finally had some good news! Sort of. The error was: Protocol qualifiers must precede type arguments. Getting close!

So what's the answer?

Define your array with a typehint for the compiler first, then add the protocol afterwards:

@property (strong, nonatomic) NSArray<ProductModel *><ProductModel> *products;

You are now free to access your objects as you wish, and the compiler will know what's going on, as well as JSONModel!