Let’s think that compile-time errors and run-time errors are like twins! Compile-time errors are the 😇 (innocent ones) and run-time errors are the 😈 (evil ones). Compile-time errors will help us avoid any mistakes that could cause their evil twins i.e. run-time errors to bug our product. I’ll be sharing couple of instances on how we can leverage compile-time errors to design our classes or methods in a way that causes compile-time errors more often when someone interacts with them!
Introduction
Lets see an example to understand what I mean..
Objective — We have to create an User class which has two properties, firstName
& lastName
. The naive way of designing it would be..
class User {
var firstName: String!
var lastName: String!
init() {
}
}
..and to create a new user
object we could simply write..
let user = User()
Seems harmless for now as we don’t get any warnings/errors from our compiler friend. We think all went good but when we try to access one of its property say firstName
it crashes bad at run-time.
Something went wrong.. and as we can see the value of firstName
is nil. It’s because we made our user
object without initialising its firstName
& lastName
and for some reason the compiler couldn’t warn us either. Let’s see what’s going on and try to refactor it..
We could start by throwing away the unnecessary !
(bang operator). Wait.. why is it unnecessary here? Before we remove let’s talk about it…
var firstName: String!
…simply tells the compiler that it doesn’t need to worry about whether it should throw us any warning/error messages while accessing firstName
. Compiler can dangerously assume that this variable will have some value all the time and will crash silently if we don’t assign any value before its accessed! 😈 twin gets excited whenever we use ! operator as its not safe and should only be used on advise.
Now that we have removed it, lets try to build.
And the 😇 twin is happy now by showing us some error — not having initialised all the stored properties. We could fix it in two ways…
1. default values
2. dependency injection
Let’s see them one-by-one. When we design a class we can define its stored variables with some default/initial values which will make sure that it will have some value before its accessed at any time, something like this.
class User {
var firstName: String = "Ritesh"
var lastName: String = "Gupta"
init() {
}
}
This approach doesn’t change the way we create a new user.
let user = User()
It will compile just fine and won’t cause any run-time error either!
But this technique is only recommended if it makes sense to have default values for them. Otherwise it’s just an easy escape from your problem and will lead to side effects with unknown behaviour.
Let’s checkout our second solution, dependency injection. It’s also a way of providing initial values for stored properties but it defers from our first approach in a way that in dependency injection we pass their initial values within the initialiser itself when we create an instance. This way we can have more control over its variables which looks something like this.
class User {
var firstName: String
var lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
}
Now instead of relying on its class to have default values we make sure that we supply its value when we create its instance.
let user = User(firstName: "Ritesh", lastName: "Gupta")
This way feels way safer & in control now as you will know the exact values of its properties when creating its instance and there won’t be any side effects what so ever.
Of course sometimes its beneficial to have default values as well so its depend on your use case.
Now that we have talked about different ways of providing initial values, you may have silently noticed that I have used var for declaring properties so let’s talk about it and see if we can use compile-time errors here as well.
Swift has given us two ways to declare a variable — let
& var
.
let makes property immutable, once initialised you can’t update its value later.
var makes property mutable, you can update its value as you like, whenever you like.
So depending on your use case, if you know that its value shouldn’t change in future then you should make it let
, something like.
class User {
let firstName: String
let lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
}
This will tell the compiler to warn us again if we try to update firstName or lastName
later on in the code.
Again we have leveraged the compile-time error (😇 twin is happy) and made sure that we kept the design of our class clear & precise as per our use case! Also it will help & guide other developers to use it in a better and safe manner!
Conclusion
I hope you enjoyed this post and it made you think positive things about compile-time errors. Follow me to stay tuned with my following posts in this multi part blog series where I’ll unravel more such scenarios where compile-time errors are really helpful! 😊
About the content
This content has been published here with the express permission of the author.