Initializers, Structs, Inheritance, & Extensions

Free iOS Development Tutorial

Delve into this comprehensive iOS development tutorial that covers various topics, including designated initializers, structs, inheritance, method overriding, and extensions.

This exercise is excerpted from Noble Desktop’s past app development training materials and is compatible with iOS updates through 2021. To learn current skills in web development, check out our coding bootcamps in NYC and live online.

Topics covered in this iOS Development tutorial:

Designated initializers, Structs, Inheritance, Method overriding, Extensions

Exercise Overview

In this exercise, we’ll delve deeper into classes. You’ll learn about using initializers to set property values at the same time you create an instance of a class, how to create a class that inherits properties and methods from another class, and how to extend one of Apple’s classes. You’ll also learn about structs, which are similar to classes.

Getting Started

  1. Launch Xcode if it isn’t already open.

  2. If you completed the previous exercise, Classes.playground should still be open. If you closed it, re-open it now.

  3. We suggest you complete the previous exercise (3A) before doing this one. If you did not do the previous exercise, complete the following:

    • Go to File > Open.
    • Navigate to Desktop > Class Files > yourname-iOS App Dev 1 Class and double–click on Classes Ready for Initializers.playground.
Full-Stack Web Development Certificate: Live & Hands-on, In NYC or Online, 0% Financing, 1-on-1 Mentoring, Free Retake, Job Prep. Named a Top Bootcamp by Forbes, Fortune, & Time Out. Noble Desktop. Learn More.

Designated Initializers

When you create an instance of a class, you are initializing that class. Initialization preps an instance for use by setting an initial value for each property listed in the class. If you don’t include an initializer, Swift assigns default property values.

  1. To the far right of let mustang = Car(), mouse over the results sidebar and click the Quick Look eye quick look eye. In the pop-up, it says nil nil. That’s because we declared speed and mpg as optionals, which are equal to nil until their values are set.

    There’s another type of initializer called a designated initializer. Often, when initializing a class, you will want to immediately set the values for your class’s properties in order to give it the info it needs to proceed. For example, we want to require the initial speed and mpg of mustang to be set as soon as it is initialized.

  2. A designated initializer is set using the init keyword. Near the top, create the bold designated initializer that will take two parameters for speed and mpg:

    var mpg: Float?
    
    init(speed: Int, mpg: Float) {
       self.speed = speed
       self.mpg = mpg
    }
    
    func start() {
    
  3. On the let mustang = Car() line (near the bottom), notice there’s a red error red alert icon. We can get rid of it by calling the designated initializer. Add the following bold code to set the instance’s properties by passing in arguments for speed and mpg:

    let mustang = Car(speed: 0, mpg: 25.0)
    

    Designated initializers are commonly used to set up a class and automatically set the required data for that class in order to give it a starting point.

  4. In the results sidebar, next to let theGasUsed = mustang.gasUsed(milesDriven: 100.0) (at the very bottom), theGasUsed is still being calculated to 3.333333. That’s because even though we set the default mustang mpg to 25.0, it’s being overridden by the following line, which updates it to 30.0.

  5. Delete that line (which is shown in bold) and any extra whitespace:

    let mustang = Car(speed: 0, mpg: 25.0)
    mustang.mpg = 30.0
    mustang.start()
    
  6. Take another look in the results sidebar, to the far right of the last line, to see that theGasUsed is now 4 because it calculated with the new default value you set.

Structures

Structures, or structs, are similar to classes. They can have variables, properties, and methods related to them. Structs are generally used for smaller groupings of data.

  1. You declare a structure the same way as a class, but using the struct keyword. Below the Car class and above the mustang instance, add the following bold code:

       func gasUsed(milesDriven: Float) -> Float {
          let gas = milesDriven / mpg!
          return gas
       }
    
    }
    
    struct Engine {
       var type = "V8"
       var horsePower = 300
    }
    
    let mustang = Car(speed: 0, mpg: 25.0)
    

    NOTE: Structs do not have some of the features that classes have, such as inheritance, type casting, deinitializers, and reference counting. You’ll learn about inheritance next, and we’ll cover the rest of these topics later.

  2. Now that we’ve declared a struct, we need to instantiate it. Again, you do this the same way you instantiate a class. Type the bold code shown below:

    struct Engine {
       var type = "V8"
       var horsePower = 300
    }
    
    let engine = Engine()
    
    let mustang = Car(speed: 0, mpg: 25.0)
    
  3. As shown below, reference the Engine structure’s engine instance and access its type property using dot syntax:

    let engine = Engine()
    engine.type
    
    let mustang = Car(speed: 0, mpg: 25.0)
    
  4. Notice on the far right in the results sidebar, it says V8.

  5. Let’s use the struct in conjunction with our class. Inside the Car class, in the variable declarations near the top, create a property based on our Engine structure:

    var mpg: Float?
    var engine: Engine
    
    init(speed: Int, mpg: Float) {
    

    This creates a property called engine, set to the type of Engine. But Engine isn’t a data type like String or Int… or is it? Defining a new class or structure is actually defining a new Swift type. So you can set a variable type to that of a struct or class.

    NOTE: Apple advises that types should be written in UpperCamelCase, to reflect the standard Swift types such as Bool, Double, Array, Dictionary, etc. Hence why our classes and structs are written as Car and Engine.

  6. You may see a red error next to the end of the init method, saying that not all the stored properties have been initialized. Let’s do that. Inside the designated initializer, add the following bold code:

    init(speed: Int, mpg: Float) {
       self.speed = speed
       self.mpg = mpg
       self.engine = Engine()
    }
    
  7. The engine property we just added is set to the Engine struct when Car is initialized. Toward the bottom of the code, look at the let engine = Engine() line and notice that the Engine struct’s engine instance has no red error. This shows that we can use structs by themselves and within classes.

  8. We want to reset the default values of the Engine struct. Edit the code as shown in bold, deleting V8 to make an empty string and changing 300 to 0:

    struct Engine {
       var type = ""
       var horsePower = 0
    }
    
  9. A few lines further down, set the following properties for the Car class’s mustang instance:

    let mustang = Car(speed: 0, mpg: 25.0)
    mustang.engine.type = "V8"
    mustang.engine.horsePower = 300
    mustang.start()
    
  10. Look on the right of the two lines you added to see that the Car class is using properties that were declared in the Engine structure.

    Using .engine.type as an example, the engine property is listed first in the chain of multiple properties because the engine property references the Engine struct (which has the type property). Swift is going through the Car class’s engine property, then to the Engine struct, then to the struct’s type property.

Inheritance

Inheritance is when one class inherits the properties and methods of another class. Inheritance is useful and widely used because it saves us from re-writing the same code for another class. To illustrate this, we’ll create a Mustang class that inherits from the Car class. The Car class has methods for start, stop, accelerateTo, and gasUsed. These have pretty general properties. For the Mustang class, you’ll write more specific properties.

  1. Below the engine constant declaration and above the mustang constant declaration, add the Mustang class:

    engine.type
    
    class Mustang: Car {
    
    }
    
    let mustang = Car(speed: 0, mpg: 25.0)
    

    By adding : Car you made Mustang a subclass of Car, meaning Mustang will now inherit all of Car’s properties and methods. So if you create an instance of Mustang, it will automatically have everything from the Car class included in it. You can also add new methods and properties that will only be accessible to the Mustang class. On the other hand, the Car class cannot access what is in the Mustang class.

  2. Let’s take a look at how this works. Add a couple of new properties to the Mustang class and give them some initial values, as shown in bold below:

    class Mustang: Car {
       var initialSpeed: Int = 10
       var milesPerGallon: Float = 20.0
    }
    
  3. Create an init function inside the Mustang class:

       var milesPerGallon: Float = 20.0
    
       init() {
    
       }
    }
    
  4. To make things more convenient, let the Mustang subclass set the values for the Car class when we initialize it. Type:

    init() {
       super.init(speed: self.initialSpeed, mpg: self.milesPerGallon)
    }
    

    The keyword super works similar to self, except it refers to the parent class (Car). The super.init code passes the Mustang class properties back up to the Car class.

  5. A few lines below, change the instance from Car to Mustang as shown in bold:

    let mustang = Mustang()
    mustang.engine.type = "V8"
    

    We are able to conveniently call an instance of the Mustang class without needing to set any values.

  6. You can also take out the engine settings and set them within the Mustang class as well. Delete the following lines shown in bold:

    let mustang = Mustang()
    mustang.engine.type = "V8"
    mustang.engine.horsePower = 300
    mustang.start()
    
  7. Inside the designated initializer, a few lines above, add the engine settings back in, using the self reference:

    init() {
       super.init(speed: self.initialSpeed, mpg: self.milesPerGallon)
       self.engine.type = "V8"
       self.engine.horsePower = 300
    }
    
  8. Go back down a few lines to the mustang instance’s code. Just to make sure that those values were applied, add:

    let mustang = Mustang()
    mustang.start()
    mustang.engine.type
    
  9. Look to the far right in the results sidebar to see V8 is displayed, showing it has been updated.

  10. Now that you’ve checked the engine type, you don’t need that line anymore. Delete the line you just wrote: mustang.engine.type

  11. Let’s add more properties just for the Mustang class. Go up a few lines and add:

    class Mustang: Car {
       var initialSpeed: Int = 10
       var milesPerGallon: Float = 20.0
       var type = "Mach 1"
    
       init() {
    
  12. A few lines down, access the Mustang class’s new type property as shown in bold:

    let mustang = Mustang()
    mustang.start()
    mustang.type
    

    On the far right in the results sidebar, Mach 1 is displayed.

  13. Just as a demonstration, let’s create an instance of the parent Car class and try to access the Mustang class’s type property:

    let car = Car(speed: 30, mpg: 20.0)
    car.type
    
    let mustang = Mustang()
    

    When you try to access type it doesn’t work because as a subclass, Mustang is inheriting from the parent Car class but Car can’t access anything inside the child Mustang class.

  14. Delete those two lines you just added (as shown below), as well as any extra whitespace:

    let car = Car(speed: 30, mpg: 20.0)
    car.type
    

    The fact that a class can inherit from another class is very useful. At times you may want to design a base class that handles a lot of generic functions that you can refer back to and reuse. You can then use inheritance to customize individual instances.

Method Overriding

What if you call a parent class method in a subclass, but you want it to do something different from what’s declared in the parent class? For example, let’s say you want to call the parent Car class’s start method and have a Mustang-only revEngine method happen in conjunction with it. This is called method overriding, because our subclass is overriding an inherited method.

  1. As shown below in bold, let’s add the aforementioned revEngine method to our Mustang subclass under its init() function:

       self.engine.type = "V8"
       self.engine.horsePower = 300
    }
    
    func revEngine() {
       print("The engine is revving")
    }
    
  2. To override a method from a parent class, we use the override keyword followed by func methodName(). Under the code you just wrote, override the start method as follows:

    func revEngine() {
       print("The engine is revving")
    }
    
    override func start() {
       self.revEngine()
       super.start()
    }
    
  3. A few lines above, look to the far right of the print function to see The engine is revving has printed in the sidebar. When we added the start method override, it automatically called the revEngine method.

    Let’s break this down further. When you call mustang.start() it’s calling start from the override function within the Mustang subclass, rather than from Car. The override contains the revEngine method and the Car class’s start using super.start(), so it prints the string.

Extensions

Extensions act similarly to inheritance, extending the functionality of a class. They are used for classes and instances for which you don’t have access to the source code. For example, often you’ll end up using classes or instances from Apple or other third-party libraries. Though you can’t edit or look into Apple’s code, they do offer the ability to extend their code.

  1. Let’s try working with a common Apple class, UIAlertController. It is used for popping up alert boxes. Below the Mustang class, type:

          super.start()
       }
    }
    
    extension UIAlertController {
    
    }
    
    let mustang = Mustang()
    mustang.start()
    

    Note the extension keyword that is included before the class name.

  2. Though you can’t edit this class code, you can add custom methods to it. Within the extension, add a method, as shown in bold:

    extension UIAlertController {
       func gasAlert() {
          print("The car is low on gas!")
       }
    }
    
  3. Below the extension, create an instance of the UIAlertController class:

    extension UIAlertController {
       func gasAlert() {
          print("The car is low on gas!")
       }
    }
    
    let alert = UIAlertController()
    

    Now you have an instance called alert that is set to the type UIAlertController.

  4. Our custom method, gasAlert, has been added to the UIAlertController codebase. Call the method by typing:

    let alert = UIAlertController()
    alert.gasAlert()
    
  5. Notice in the results sidebar to the right of the new print function, it prints: The car is low on gas!

    You can use extensions on any class to add methods or properties.

  6. Save and close the file. Keep Xcode open, as we’ll use it in the next exercise.

Noble Desktop Publishing Team

The Noble Desktop Publishing Team includes writers, editors, instructors, and industry experts who collaborate to publish up-to-date content on today's top skills and software. From career guides to software tutorials to introductory video courses, Noble aims to produce relevant learning resources for people interested in coding, design, data, marketing, and other in-demand professions.

More articles by Noble Desktop Publishing Team

How to Learn iOS & Web Development

Master iOS development, web development, coding, and more with hands-on training. iOS development involves designing apps for Apple mobile devices with tools like Xcode and SwiftUI.

Yelp Facebook LinkedIn YouTube Twitter Instagram