Swift: Conventions are good, strings are bad
I really like Apple APIs. In my opinion, they are logical and well-designed. But some of their choices just cripple me. One of the choices is to use strings as key-paths or identifiers. Strings are not checked during the compile time, you could easily mistype them. Or the name could change. Oh I remember those long nights, when I tried to find the memory leak in UITableView, that happened, because I’ve forgotten to set the reuseIdentifier for my cell in Interface Builder. Oh those moments of pain and regret.
Another problem was, that my code was highly error prone, as I reused the cells throughout my whole project, which lead me to creating constants header with identifiers, which was a mess, as I had to continuously synchronize it with nib names and identifiers. Overall, there were just too much strings for my liking. The code of other devs was even more painful to watch and use, as it looked like the one copy-pasted directly from Apple tutorials with Massive View Controllers all over the place with corresponding zero reusability and extensibility. Sadly, the code as such could be seen even today and it wasn’t much better on average back in the olden days either:
So, one day around 7 years ago I started reflecting on my deeds and the deeds of the ones around me. I felt, that I was a sinner and that was not for good. I had one particular question in my mind: “Why do we use Apple conventions but are afraid of creating our own?” And then, I’ve taken a look around and noticed, that ActiveRecords built on top of CoreData do have their own conventions, my team has them as well. They are just not formalized and aren’t put to good use. And I was enlightened.
I decided, that I want to create the conventions to simplify the code from that glorious day and up to eternity. But I felt, that conventions were dangerous at the same time, as everyone must abide them. So, I documented them and spread the word across the team with care and diligence.
I restore my actions and ideas at that point in time, but I rewrite them in Swift to show you, how you could analyze and create the conventions yourself.
The formal analysis lead me to one discovery. All the cells had the identifier, that was a direct derivative of their class name. Moreover, all the nibs for all the views had the same name, as the class of that particular view.
First of all, I’ve gotten rid of the problem with cells. I created a convention saying, that all our cell class names and reuse identifiers should equal:
Note, that I immediately created a function responsible for type stringification. We don’t need it at that point in time, but that’s the common entry point, that could allow for extensibility later on, when we need it.
Back in ObjC days we could overload any property in extension, but I didn’t do that and created IDPTableViewCell subclass instead. Why? Simple, we had interoperability with the code form other dev teams. And if I overloaded UITableViewCell reuseIdentifier, I’d mess the code of others, who could use different or even not use any conventions at all.
Then, I thought, that I want to get rid strings in table views and this meant, that I needed to remove strings from UINib (there was no UINib back then, just the good old NSBundle.loadNibNamed, but as I mentioned previously, I want to be up to date with my story):
I created a bunch of other methods fully removing strings from the APIs I used for table views (I’ll open source them pretty soon, I promise). My production code started looking like this:
While not being a huge improvement in LOC department, this code has one huge win: I don’t have to bother with strings and identifiers in nibs any more.
Another case I wanted to mention is quite different, but also related to conventions. After some analysis, I’ve found, that all the view controller nibs in my projects had the same name, as the view controller class. That’s in vein with the convention Apple itself imposes. But the real problem was, that my product owner (I wish you a pleasant vacation in hell, in case you are reading this) wanted different nibs with completely different layouts for different devices and there were no size classes back then. That’s when I’ve found, that conventions should be extensible in case of the need. So, I had my solution — overload the nibName property of my abstract view controller class. And I was really happy I had that class, because otherwise I would have to create it and refactor all the code accordingly.
So, another convention I came up with is: “Create subclasses for all you base components, from which you are planning to inherit”. In my case those were UIView, UIViewController abstract subclass and model abstract class. All my project related entities were subclassed from them. They were empty at first, but I was really happy I had them, when product owner started imposing some tasks, that affected the whole project.
That’s all, folks. Have a great day and stay DRY, no matter, where you are.
P.S. This line here is just a placeholder and the reminder for myself to put a link to the repo with destringification conventions. In case I don’t do that till February 2017, I address my plea to anyone, who reads these lines: please, remind me about that.