In The Basics of Swift Object-Oriented Programming, we covered the basic concepts of object-oriented programming and worked through an example of creating and working with a new class using Swift. In that example, our new class was not derived from any base class and, as such, did not inherit any traits from a parent or superclass. In this chapter, we will introduce the concepts of subclassing, inheritance, and extensions in Swift.
Inheritance, Classes, and Subclasses
The concept of inheritance brings something of a real-world view to programming. It allows a class to be defined that has a certain set of characteristics (such as methods and properties) and then other classes to be created which are derived from that class. The derived class inherits all of the features of the parent class and typically then adds some features of its own.
By deriving classes, we create what is often referred to as a class hierarchy. The class at the top of the hierarchy is known as the base class or root class and the derived classes as subclasses or child classes. Any number of subclasses may be derived from a class. The class from which a subclass is derived is called the parent class or super class.
Classes need not only be derived from a root class. For example, a subclass can also inherit from another subclass with the potential to create large and complex class hierarchies.
In Swift, a subclass can only be derived from a single direct parent class. This is a concept referred to as single inheritance.
You are reading a sample chapter from an old edition of iOS App Development Essentials. Purchase the fully updated iOS 18 App Development Essentials book. The full book contains 71 chapters, over 612 pages of in-depth information, downloadable source code, and access to over 50 SwiftUI knowledge test quizzes. |
A Swift Inheritance Example
As with most programming concepts, the subject of inheritance in Swift is perhaps best illustrated with an example. In The Basics of Swift Object-Oriented Programming we created a class named BankAccount designed to hold a bank account number and corresponding current balance. The BankAccount class contained both properties and instance methods. A simplified declaration for this class is reproduced below:
class BankAccount {
var accountBalance: Float
var accountNumber: Int
init(number: Int, balance: Float)
{
accountNumber = number
accountBalance = balance
}
func displayBalance()
{
print("Number \(accountNumber)")
print("Current balance is \(accountBalance)")
}
}
Code language: Swift (swift)
Though this is a somewhat rudimentary class, it does everything necessary if all you need it to do is store an account number and account balance. Suppose, however, that in addition to the BankAccount class you also needed a class to be used for savings accounts. A savings account will still need to hold an account number and a current balance and methods will still be needed to access that data. One option would be to create an entirely new class, one that duplicates all of the functionality of the BankAccount class together with the new features required by a savings account. A more efficient approach, however, would be to create a new class that is a subclass of the BankAccount class. The new class will then inherit all the features of the BankAccount class but can then be extended to add the additional functionality required by a savings account.
To create a subclass of BankAccount that we will call SavingsAccount, we simply declare the new class, this time specifying BankAccount as the parent class:
class SavingsAccount: BankAccount {
}
Code language: Swift (swift)
Note that although we have yet to add any instance variables or methods, the class has actually inherited all the methods and properties of the parent BankAccount class. We could, therefore, create an instance of the SavingsAccount class and set variables and call methods in exactly the same way we did with the BankAccount class in previous examples. That said, we haven’t really achieved anything unless we take steps to extend the class.
Extending the Functionality of a Subclass
So far, we have created a subclass containing all the functionality of the parent class. For this exercise to make sense, however, we now need to extend the subclass so that it has the features we need to make it useful for storing savings account information. To do this, we simply add the properties and methods that provide the new functionality, just as we would for any other class we might wish to create:
You are reading a sample chapter from an old edition of iOS App Development Essentials. Purchase the fully updated iOS 18 App Development Essentials book. The full book contains 71 chapters, over 612 pages of in-depth information, downloadable source code, and access to over 50 SwiftUI knowledge test quizzes. |
class SavingsAccount: BankAccount {
var interestRate: Float = 0.0
func calculateInterest() -> Float
{
return interestRate * accountBalance
}
}
Code language: Swift (swift)
Overriding Inherited Methods
When using inheritance, it is not unusual to find a method in the parent class that almost does what you need, but requires modification to provide the precise functionality you require. That being said, it is also possible you’ll inherit a method with a name that describes exactly what you want to do, but it actually does not come close to doing what you need. One option in this scenario would be to ignore the inherited method and write a new method with an entirely new name. A better option is to override the inherited method and write a new version of it in the subclass.
Before proceeding with an example, there are two rules that must be obeyed when overriding a method. First, the overriding method in the subclass must take exactly the same number and type of parameters as the overridden method in the parent class. Second, the new method must have the same return type as the parent method.
In our BankAccount class we have a method named displayBalance that displays the bank account number and current balance held by an instance of the class. In our SavingsAccount subclass we might also want to output the current interest rate assigned to the account. To achieve this, we simply declare a new version of the displayBalance method in our SavingsAccount subclass, prefixed with the override keyword:
class SavingsAccount: BankAccount {
var interestRate: Float
func calculateInterest() -> Float
{
return interestRate * accountBalance
}
override func displayBalance()
{
print("Number \(accountNumber)")
print("Current balance is \(accountBalance)")
print("Prevailing interest rate is \(interestRate)")
}
}
Code language: Swift (swift)
It is also possible to make a call to the overridden method in the super class from within a subclass. The displayBalance method of the super class could, for example, be called to display the account number and balance, before the interest rate is displayed, thereby eliminating further code duplication:
override func displayBalance()
{
super.displayBalance()
print("Prevailing interest rate is \(interestRate)")
}
Code language: Swift (swift)
Initializing the Subclass
As the SavingsAccount class currently stands, it inherits the init initializer method from the parent BankAccount class which was implemented as follows:
You are reading a sample chapter from an old edition of iOS App Development Essentials. Purchase the fully updated iOS 18 App Development Essentials book. The full book contains 71 chapters, over 612 pages of in-depth information, downloadable source code, and access to over 50 SwiftUI knowledge test quizzes. |
init(number: Int, balance: Float)
{
accountNumber = number
accountBalance = balance
}
Code language: Swift (swift)
Clearly this method takes the necessary steps to initialize both the account number and balance properties of the class. The SavingsAccount class, however, contains an additional property in the form of the interest rate variable. The SavingsAccount class, therefore, needs its own initializer to ensure that the interestRate property is initialized when instances of the class are created. This method can perform this task and then make a call to the init method of the parent class to complete the initialization of the remaining variables:
class SavingsAccount: BankAccount {
var interestRate: Float
init(number: Int, balance: Float, rate: Float)
{
interestRate = rate
super.init(number: number, balance: balance)
}
.
.
.
}
Code language: Swift (swift)
Note that to avoid potential initialization problems, the init method of the superclass must always be called after the initialization tasks for the subclass have been completed.
Using the SavingsAccount Class
Now that we have completed work on our SavingsAccount class, the class can be used in some example code in much the same way as the parent BankAccount class:
let savings1 = SavingsAccount(number: 12311, balance: 600.00,
rate: 0.07)
print(savings1.calculateInterest())
savings1.displayBalance()
Code language: Swift (swift)
Swift Class Extensions
Another way to add new functionality to a Swift class is to use an extension. Extensions can be used to add features such as methods, initializers, computed properties and subscripts to an existing class without the need to create and reference a subclass. This is particularly powerful when using extensions to add functionality to the built-in classes of the Swift language and iOS SDK frameworks. A class is extended using the following syntax:
extension ClassName {
// new features here
}
Code language: Swift (swift)
For the purposes of an example, assume that we need to add some additional properties to the standard Double class that will return the value raised to the power 2 and 3. This functionality can be added using the following extension declaration:
You are reading a sample chapter from an old edition of iOS App Development Essentials. Purchase the fully updated iOS 18 App Development Essentials book. The full book contains 71 chapters, over 612 pages of in-depth information, downloadable source code, and access to over 50 SwiftUI knowledge test quizzes. |
extension Double {
var squared: Double {
return self * self
}
var cubed: Double {
return self * self * self
}
}
Code language: Swift (swift)
Having extended the Double class with two new computed properties we can now make use of the properties as we would any other properties of the Double class:
let myValue: Double = 3.0
print(myValue.squared)
Code language: Swift (swift)
When executed, the print statement will output the value of 9.0. Note that when declaring the myValue constant we were able to declare it as being of type Double and access the extension properties without the need to use a subclass. In fact, because these properties were added as an extension, rather than using a subclass, we can now access these properties directly on Double values:
print(3.0.squared)
print(6.0.cubed)
Code language: Swift (swift)
Extensions provide a quick and convenient way to extend the functionality of a class without the need to use subclasses. Subclasses, however, still have some advantages over extensions. It is not possible, for example, to override the existing functionality of a class using an extension and extensions cannot contain stored properties.
Summary
Inheritance extends the concept of object re-use in object-oriented programming by allowing new classes to be derived from existing classes, with those new classes subsequently extended to add new functionality. When an existing class provides some, but not all, of the functionality required by the programmer, inheritance allows that class to be used as the basis for a new subclass. The new subclass will inherit all the capabilities of the parent class, but may then be extended to add the missing functionality.
Swift extensions provide a useful alternative option to adding functionality to existing classes without the need to create a subclass.
You are reading a sample chapter from an old edition of iOS App Development Essentials. Purchase the fully updated iOS 18 App Development Essentials book. The full book contains 71 chapters, over 612 pages of in-depth information, downloadable source code, and access to over 50 SwiftUI knowledge test quizzes. |