WWDC 15 had me feeling a bit nostalgic. Swift 2 and iOS 9 certainly are exciting, but for me the event was an evocative reminder of the days when I first began iOS development.
I still remember that old Objective-C book I bought all those years ago. To this day, it sits quietly on the book shelf in my office. But as I fervently ran through its pages years ago, I couldn’t help but be aware of all the things Objective-C couldn’t do instead of what it could.
This year — a lot those could nots are gone. This week — we’ll be looking at an old friend who grew up. Let’s talk Objective-C enhancements.
Friends, let’s take a walk down a path we’ve traveled many, many times:
@property (strong, nonatomic) NSArray *someViews;
This is standard fare for the consummate Objective-C developer. It denotes a property that will have a collection of some views. However, there are several inherent weaknesses hiding out in this small bit of code:
Can this ever return nil?
Unless the documentation is there or the developer is present, one can’t really know that information just by looking at it.
Will this ever have anything besides a UIView?
Same as above, who knows? Use some reflection, perhaps? Or — can there ever be anything besides UIViews in there? Say, any subclass of UIView?
Looks like there will be casting…a lot
Since this is an array of…..something, when one finds out what type that something is — there will be a lot of casts involved to make any use of it.
This weakens Swift code and readability
Swift has generics, so it’s unfortunate that this will map as a collection typed to an optional AnyObject. That’s going to force the developer to cast in Swift and Objective-C when using this property.
It’s perhaps a bit discomforting to know that so many concerns were risen from just a single property. Maintaining code that implicitly produces several questions can be error prone, let alone performing interoperability between the said language and a newer one (i.e. Swift).
The first step to making this better is one of my favorite new Objective-C features, nullability annotations. These annotations provide something extremely valuable in programming…
They clearly tell the story of an API. This can return nil. This can’t return nil. In short, hours lost to debugging can be avoided. The three available are:
- nullable — Think UIView?
- nonnull — Think UIView
- null_unspecified — Think UIView!
Let’s revisit our example property. Assume that this property is iterated on to create some sort of user interface at run time. With it, there should be some UIButtons and UIViews to place them in.
But heavens — they shouldn’t be nil in any circumstance. And now, that information is present:
@property (strong, nonatomic, nonnull) NSArray *someViews;
Not only does this help Objective-C, but now this property won’t be pumped full of optionals over in Swift as well.
The developer can see this code and know if there could ever be a nil pointer. Bravo.
We’ve improved static checking, the usability in Swift, and have most importantly communicated the intent of the API.
…and the developers using Objective-C all over the lands rejoiced. Alas, generics doth roamed the lands, and all was made right by these brave developers.
If Cocoa Touch was a kid’s bedtime story and Objective-C the main protagonist, the book would certainly end with those lines. Long has the sharp absence of generics been the pain point of many developers.
Though it took about thirty-two years, generics is now a thing in Objective-C. Lost in all the Swift 2 announcements, WWDC 15 attendees and consumers alike shouldn’t take this news lightly. It means a lot of things, and all of them good.
Back to our property. We can now type it to tell both the compiler and the programmer that it expects UIViews:
@property (strong, nonatomic, nonnull) NSArray<UIView *> *someViews;
This is great, because the compiler will whine and complain if one tries to assign anything other than UIViews to the property. It also saves an embarrassing number of casts.
Swift is also happier too. In the last improvement, we let Swift know that the objects shouldn’t be optionals. Now, Swift also knows they are UIViews, so we can get rid of the ambiguous AnyObject declaration.
One can denote type much like C#, C++, Swift, and many others via the <> brackets. Even though this usually denotes conformance to a protocol, the compiler knows when, where, and how they should be used. The application is inferred.
Going further, it’s also possible to parameterize extensions, categories, and classes. The benefits extends beyond just collections. The feature of generics reaches all over Objective-C, and collections are just a result of it. For example, look at NSDictionary and smile:
@interface NSDictionary<KeyType, ObjectType> (Lookup)
- (nullable ObjectType)objectForKey:(KeyType)aKey;
Though I was a bit disappointed to learn that type erasure was used to accomplish this, it’s more than understandable due to the extensive backlog of Objective-C legacy code.
Type erasure achieves binary compatibility and it also allowed for zero changes to the Objective-C runtime. So dear developer — grimace for a moment, whine how C# has generics implemented better than anyone else, and then move on.
Alas, our journey still has one more important stop. Recall that the property we’ve been working on can have UIViews in it. So — it’s perfectly sane to assume there will be some views and buttons contained within.
Given this, what would happen if one authors this code:
[self.someViews addTarget:self action:selector(aMethod:) forControlEvents:UIControlEventTouchUpInside];
Ah, a compiler warning.
And rightfully so, because even though it is valid to insert a button in this property, and even though it is valid to say it can be a UIView, it’s not valid to assume that it’s a button without a cast.
This scenario happens more than one would think, and using the new KindOf feature it’s easy to solve. Going back to the example property:
@property (strong, nonatomic, nonnull) NSArray<__kindof UIView *> *someViews;
Essentially, we’ve informed the compiler that the property and its collection will have some kind of a UIView. This allows for more information in the type contract that we’ve not had before.
The real treat? Implicit downcasting.
That means the code above compiles just fine, because the compiler knows that a button could certainly be in the collection.
Now, if you revisit the initial concerns we had above, they’ve all been accounted for.
While Swift naysayers will wax poetic about the merits of the language, they should also be thankful. These changes are really all about one thing — Swift interoperability.
Even so, at perhaps the most ironic time, Objective-C is more powerful as it has ever been.
Objective-C was my first love in my programming career. Different though it was (and still is), I marveled at the power of its features as much as I did its shortcomings. Now with Swift quickly winning over my ever affectionate engineer heart, I still find myself enjoying my time in Objective-C.
These changes are welcome. They should be used to create better code, and they already have been all over Foundation. Until next week, this is the NSEnd<Post *>.