Tuesday, 20 May 2014

More poor design decisions by Apple/Objective-C/iOS

I wrote on Facebook earlier that developing for iOS is like trying to run with your shoelaces tied. Every time you think you are gaining momentum (i.e. knowledge), you trip over again on something. When you are learning a new thing, it can be hard to work out what is wrong because perhaps you just don't understand it or perhaps the design of the framework is rubbish.

I really don't like any technology where it works because you learn all of its poor design decisions rather than because it is logical, consistent and instinctive. I dislike Java but for the most part, it is consistent and there are a few things that are a pain (like having to catch exceptions) but to be fair, these are few and far between compared to Objective-C.

Let me give you today's example.

I have to write a custom alert dialog. Why? Not because I need to do something outlandish but because prior to iOS 7, you could rotate the alert dialog using setTransform which was useful when you had to hand-crank the rotation of the device since Apple decided you can't force a rotation. Anyway, as from iOS 7, this is no longer possible for unclear reasons and what really annoys me is that it breaks existing code. The correct way to make changes is to either deprecate something or create a new class and recommend that developers change to the new one, they have only succeeded in forcing even more people to write custom UI classes, something which will only lead to less consistency across apps rather than more.

Anyway, that is another story. As part of this dialog, I wanted to add the buttonTitleAtIndex method as per the UIAlertDialog because I was using it. In order to do this, I thought I would create a dictionary that would map indices to button titles and this function could simply use that to retrieve the button title. So, how to do this? Well, first off, I created an NSDictionary and then tried to use it. I couldn't. Why? Because the NSDictionary is NOT MUTABLE. What does that mean? You can't change it once it is initialised. So only useful if you know the contents when you first create it. You have to use the NSMutableDictionary if you want mutable contents. Now, to me, this is a poor decision. Most languages would use const to create an immutable object - that was even available in C++ - which makes it nice and obvious. The idea that you use two separate classes just to distinguish their mutability does not meet the OO concept that class = identity. In other words, two classes are two different things. An NSDictionary and an NSMutableDictionary are not two different things in identity, they just behave slightly differently.

Well, once that was worked out, I tried to use it to store a key/value pair. In my case, very simple, an integer (or NSInteger) and a string (NSString*). I added the object and immediately got a compiler error: Cannot convert NSInteger to id. What on earth is NSCopying? It turns out it is a protocol (read interface) that says an object can be copied, which is fair enough but why would keys in a dictionary need to be copyable? Because the dictionary copies the key when the item is inserted. Hmm, OK but why isn't my NSInteger copyable? Surely a simple object primitive would define all these types of methods but...oh wait....NSInteger is NOT a class, it is a typedef for long (i.e. a hack). So Apple created a massive framework of classes but couldn't be bothered to do it for the humble basic types. .Net does it, Java does it and C++ doesn't pretend that primitives are objects by type-deffing them into class-style names.

So, the solution was to use another class NSNumber, which is a class, and which can therefore be used as a key and utilising its static method numberWithInt to convert the integer into an NSNumber. So all of these poor design decisions take what I would expect to be an obvious action in any language and turn what would look like this in Java/.Net:
myDictionary[itemIndex] = myObject;

myDictionary.AddObject(itemIndex, myObject);

into this in Obj-C/iOS
[[self myDictionary] setObject:myObject forKey:[NSNumber numberWithInt:itemIndex]];

Is this really the correct way to write production code that should be maintainable and readable?
Post a Comment