1. Forced Unwrapping and Downcasting
Once you get used to optionals, you come to appreciate their value. Not only are optionals making your code safer, they also make your code more readable. An optional carries a message that says “I may not have a value. Be careful.
It doesn’t matter how certain you are, an optional should be treated with caution. Forced unwrapping or forced downcasting is asking for trouble. There are situations in which you can use the
! operator (outlets, for example), but do not force unwrap an optional to avoid an
if statement or a few extra lines of code.
if statement tells the developer what we expect, but, at the same time, it hints that we may not get what we expected.
If you find yourself using a
!instead of an
?, then stop for a moment and ask yourself whether there is a safer alternative.
2. Monster Classes
Some of us apply the MVC (Model-View-Controller) pattern a bit too strictly. It’s fine to create classes that are not models, views, or controllers. Really.
Some developers even argue that you should create fat models and skinny controllers. While that may be a bit too much responsibility for the model layer, I agree that controllers should be lean and lightweight. Of course, it’s easy to stuff business logic in controllers and it takes a bit of extra work to refactor code that can belong in a separate class. But it’s often the right choice.
By isolating functionality, you put the view controllers of your project on a diet and make code easier to reuse. Why not extract a piece of functionality in an Objective-C category or a Swift extension.
If the implementation of a class exceeds 1000 lines of code, It’s a warning bells. Can you load 1000 lines of code in your working memory? That’s what it takes to work with a class. If you make changes to a class, you need to know how other parts are affected and that’s only possible if you know what the various parts of the class do.
3. Ignoring Errors
It’s great to see that error handling is tightly integrated into Swift. In Objective-C, it’s easy to ignore errors, too easy. Have you ever seen something like this?
I don’t know anyone who enjoys error handling. But if you want to write code that works and that can recover when things start to go wrong, then you need to accept that error handling is part of it. Ignoring errors also makes debugging more difficult. Putting your head in the sand isn’t the solution when things go wrong.
Singletons are great. They are so useful that less experienced developers tend to overuse them. Been there, done that. It’s true that singletons are great. Even though I don’t consider the singleton pattern an anti-pattern, sometimes it seems as if developers have forgotten how to pass objects by reference.
One of the negative side effects of singletons is tight coupling. Over the years, I have come across projects that were littered with singletons, singletons referencing other singletons. This can make refactoring a nightmare.
Another benefit of keeping singletons to a minimum is testing. Singletons make testing more difficult.
There is nothing wrong with passing objects by reference. Whenever you are about to create a singleton, ask yourself whether passing the object by reference is an option. That should be your first choice.
5. String Literals
String literals are smelly by default. They are great in playgrounds, but how often do you (or should you) use string literals in a project? The only valid use cases I can think of are localization, constants, and logging.
I agree that creating constants or enumerations is tedious, but it pays off in the long run. You avoid typos, autocompletion kicks in, and it significantly improves maintainability.