Hello there, my sweet fellow developers,
When reviewing the code, I have 3 states with corresponding amount of WTFs per minute form worst to best:
- 30…infinity -> I want to kill the one responsible for introducing THAT to the world, he is the worst, even the most bloody dictators, like Stalin, Paul Pot or Hitler are kittens compared to him;
- 10..<30 -> I like that guy/gal, (s)he is clever, we could work together and still survive, but yeah, everyone has his/her own flaws;
- 0..<10 -> That dev is awesome, I want to learn from him/her and bear his/her children.
The perfect example of the code from the first category was a “little” class I’ve seen back in the olden days of iOS 3, that downloaded something from the API, parsed it and returned the result. Pretty simple, isn’t it? No one can’t spoil such a simple code, right? Wrong. The implementation was horrific. Do you think the guy used ASIHTTPRequest (there was no AFNetworking, as far, as I remember or AlamoFire back then)? Nope? Do you think the guy used a wrapper around NSURLConnection? Nope. But what did he use then? Well, he used UIWebView for downloading the JSON from the API. I can confess, there is blood on my hands, so I’m waiting for police to be incarcerated.
But there was another case some time ago, when the guy just hit 31 WTF/m. Everything was more or less fine, except for one thing, he was a fan of writing the code similar to the one below:
1 2 3 4 5 6 |
let tableView = UITableView() tableView.beginUpdates() // do something tableView.endUpdates() |
And that’s horrible. Just imagine forgetting to put either beginUpdates or endUpdates or just deleting them during refactoring. That would result in some headaches, especially, if we have several updates nested one into another. Moreover, project-wide, there were several such sections of code, which we can consider as duplication for our own good. But don’t be afraid, good old Oleksa has an advice for ya, it was passed through the generations of my family from before Christ and now I generously share it with you (tssssh, don’t tell anyone else): “Use categories/extensions and closures”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
extension UITableView { typealias Updates = () -> () func update(updates: Updates) { self.beginUpdates() updates() self.endUpdates() } } tableView.update { // do something } |
This extension is perfectly reusable and could be dragged from project to project or just podded (sorry, no Carthage here, Artsy and the Cocoapods are the best and I’m their biggest fan).
This sacred knowledge could also be applied to protocols:
1 2 3 4 5 |
let lock: NSLocking = NSLock() lock.lock() // do something lock.unlock() |
We have protocol extensions at our hands in Swift and plain awesome macros in ObjC (although, that’s debatable and some of the devs could easily burn my car with me sitting inside for just mentioning them, still, take a look, if you are interested: IDPLocking), that could solve the problem of NSLock:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
extension NSLocking { @discardableResult func dispatch<T>(_ work: () -> T) -> T { self.lock() let result = work() self.unlock() return result } } let a = lock.dispatch { return "some fancy result" } lock.dispatch { print(a) } |
Once again, same conveniences, as with UITableView. And no risks of forgetting to unlock. Moreover, thanks to protocol extensions we receive that behavior in all NSLocking compliant objects for free. And we have even taken it one step further and now we can actually get the result of function without using external variables. Why does it work? Because Void in Swift is a value, that equals empty tuple ().
This same idea could be applied in multiple ways not only in extensions though. As this approach is just a plain injection of behavior using functions, you could try using it for the greater good outside of extensions as well.
That’s all, folks. Have a great day and stay DRY, no matter, where you are.