In part two, I discussed the concept of optionals in relation to Hamlet (yep, it’s possible). This time around, I want to talk about a complicated topic, initializers. I want to scratch the surface, perhaps go a little bit deeper, but not overwhelm you with too much information. To this end, I’m going to provide a framing device for initializers first then some appropriate context that will help you better digest (no pun intended, as you’ll soon figure out) how initializers work.
Some time ago, while watching Chopped, I was talking with my friend about people who say they enjoy cooking, and how few of them actually went above and beyond just cooking basic meals like pasta with canned sauce. I tend to be mostly basic, occasionally rising above that to make fancier foods. My friend, on the other hand, likes to chef like a pro. The reason why I and others don’t cook like food aficionados is because of how much time it takes to prepare ingredients. It would be awfully convenient if I could just prepare all the ingredients of a recipe just once to make the dish, and then every subsequent time I make the dish, the ingredients would be all prepared for me. Since that’s not a thing, I’m relegated to trying to spend as little time as possible making food that doesn’t kill me.
With the idea of food and preparing recipes in mind, let’s tackle initializers. Initializers prepare various elements of a class, struct, or enum for usage. They set up initial values for stored properties, making sure they’re all created and ready to go before the instance is able to be used. Simply put, if the program you’re creating is a fancy, complex dish, initializers are your assistants at work, preparing the necessary prerequisites for the recipe like boiling water and grating cheese while you oversee the rest of the meal.
Let’s take a look at a simple example:
Here, we have a class denoting a burger at a restaurant. The burger has one stored property, like a characteristic. This stored property is a variable price of the type double. The init method in the class, which is known as the designated initializer of the base class (which, in this case, is burgerMeal), basically attaches a price to every burger we create using the burgerMeal class as a template. In a real-world application, price is definitely an intrinsic property of the food. Every meal sold at a restaurant, be it a burger or a chocolate mousse with gold flakes, requires a price. This is something that is needed for all meals (unless it’s from Jon Bon Jovi Soul Kitchen), so we want to make sure that every new type of meal we create has a price attached to it. When we create an instance of the burgerMeal class, we find this to be true:
Initializers don’t necessarily have to always have values passed into them, however. Let’s say we want to have a little variety in our meal. At In-N-Out, for example, I always order my burgers with grilled onions, while others I know order their burgers with no onions at all. The standard, if onion preparations are left unspecified, is just regular onions. Let’s take this into account:
Note that unlike burgerMeal’s init method, onion’s init method has nothing passed into it. The notation under init indicates that, unless otherwise specified, every new instance of a burger will have regular onions.
Let’s take this new development and apply it to a next step. Let’s say we want to make a new kind of burger based on the original burgerMeal. We want our new burger to have a price, but we also want it to incorporate the onion option that we created as an enum, and we also want to include a description of it so people will know what the new burger is all about. To put it in Swift terms, we want a subclass of the burgerMeal base class:
The class burgerMeal is the superclass of cheeseBurgerMeal, which inherits burgerMeal’s stored property of price. You can think of burgerMeal as the first burger ever invented; every subsequent derivation of that first burger, such as cheeseBurgerMeal, also has the traits of the original, and would be considered a subsequent iteration of it, or a subclass. In addition, cheeseBurgerMeal also has its own unique trait; it initializes its own stored property, which is a description of type string. Notice the colon and designation of burgerMeal after cheeseBurgerMeal to denote that cheeseBurgerMeal is a subclass of burgerMeal. Also note the “super” prefix to the init method of cheeseBurgerMeal, which makes it a super init – that is, it refers back to the superclass to initialize its stored properties.
Swift also gifts us with convenience initializers, which provides default values and accepts less parameters. Basically, if some of the subsequent instances of our base class have the same values, we can use the convenience initializer to make sure these values are passed over, and we don’t have to write out the parameters every single time:
The convenience keyword in front of the init allows us to have a convenience initializer. The parameters passed in are the ones that we want to specify, while the self.init within the convenience initializer shows the parameters we want to essentially automate, which, in this case, would be the price of four dollars. Look at the difference between having the convenience initializer:
And not having the convenience initializer:
Okay, maybe it doesn’t look like that much space saved, but imagine if our program (or burger) were more complex; you’d save yourself quite the headache.
Overall, initializers are a fantastic tool for developers to use in any language. Swift’s inclusion of things like convenience initializers really make developers’ lives easier, so it would certainly be an asset to learn.
For now, that’s all of my insights and opinions. I still have much to learn, so I have to get back to that so I can make sure I write all of this for you guys. Thanks for reading. Stick around for the next installment if you’d like, or don’t. But if you don’t, you best be learning Swift instead.