Book Image

Swift 4 Programming Cookbook

Book Image

Swift 4 Programming Cookbook

Overview of this book

Swift 4 is an exciting, multi-platform, general-purpose programming language. Being open source, modern and easy to use has made Swift one of the fastest growing programming languages. If you interested in exploring it, then this book is what you need. The book begins with an introduction to the basic building blocks of Swift 4, its syntax and the functionalities of Swift constructs. Then, introduces you to Apple's Xcode 9 IDE and Swift Playgrounds, which provide an ideal platform to write, execute, and debug the codes thus initiating your development process. Next, you'll learn to bundle variables into tuples, set order to your data with an array, store key-value pairs with dictionaries and you'll learn how to use the property observers. Later, explore the decision-making and control structures in Swift and learn how to handle errors in Swift 4. Then you'll, examine the advanced features of Swift, generics and operators, and then explore the functionalities outside of the standard library, provided by frameworks such as Foundation and UIKit. Also, you'll explore advanced features of Swift Playgrounds. At the end of the book, you'll learn server-side programming aspect of Swift 4 and see how to run Swift on Linux and then investigate Vapor, one of the most popular server-side frameworks for Swift.
Table of Contents (9 chapters)

Object classes

Object-oriented programming is currently the dominant programming paradigm. At the core of this paradigm is the object class. Objects allow us to encapsulate data and functionality, which can then be stored and passed around.

Getting ready

Let's build some class objects. Then, we'll break down the components of the class to understand how it is defined and used.

How to do it...

Let's start by entering the following code into the playground:

class Person { 

let givenName: String
let middleName: String
let familyName: String
var countryOfResidence: String = "UK"

init(givenName: String, middleName: String, familyName: String) {
self.givenName = givenName
self.middleName = middleName
self.familyName = familyName
}

var displayString: String {
return "\(fullName()) - Location: \(countryOfResidence)"
}

func fullName() -> String {
return "\(givenName) \(middleName) \(familyName)"
}
}

final class Friend: Person {
var whereWeMet: String?

override var displayString: String {
return "\(super.displayString) - \(whereWeMet ?? "Don't know where we met")"
}
}

final class Family: Person {
let relationship: String

init(givenName: String, middleName: String, familyName: String = "Moon",
relationship: String) {
self.relationship = relationship
super.init(givenName: givenName, middleName: middleName, familyName:
familyName)
}

override var displayString: String {
return "\(super.displayString) - \(relationship)"
}
}

let steve = Person(givenName: "Steven", middleName: "Paul", familyName: "Jobs")
let dan = Friend(givenName: "Daniel", middleName: "James", familyName: "Woodel")
dan.whereWeMet = "Worked together at BBC News"
let finnley = Family(givenName: "Finnley", middleName: "David", relationship: "Son")
let dave = Family(givenName: "Dave", middleName: "deRidder", familyName: "Jones", relationship: "Father-In-Law")
dave.countryOfResidence = "US"

print(steve.displayString) // Steven Paul Jobs
print(dan.displayString) // Daniel James Woodel - Worked together at BBC News
print(finnley.displayString) // Finnley David Moon - Son

How it works...

Classes are defined with the class keyword, class names start with a capital letter by convention, and the implementation of the class is contained, or "scoped", within curly brackets:

class Person {
//...
}

An object can have property values, which are contained within the object. These properties can have initial values, as countryOfResidence does in the following code, although bear in mind that constants (defined with let) cannot be changed once the initial value has been set:

class Person {
let givenName: String
let middleName: String
let familyName: String
var countryOfResidence: String = "UK"
//...
}

If your class were to just have the preceding property definitions, the compiler would raise a warning, as givenName, middleName, and familyName are defined as non-optional strings, but we have not provided any way to populate those values.

The compiler needs to know how the object will be initialized, so that we can be sure that all the non-optional properties will indeed have values:

class Person { 
let givenName: String
let middleName: String
let familyName: String
var countryOfResidence: String = "UK"

init(givenName: String, middleName: String, familyName: String) {
self.givenName = givenName
self.middleName = middleName
self.familyName = familyName
}
//...
}

The init is a special method (functions defined within objects are called methods) that's called when the object is initialized. In the Person object of the preceding code, we expect givenName, middleName, and familyName to be passed in when the object is initialized, and we assign those provided values to the object's properties. The self. prefix is used to differentiate between the property and the value passed in as they have the same name.

We do not need to pass in a value for countryOfResidence as we defined an initial value when the property was defined. This isn't ideal, though, as when we initialize a Person object, it will always have the countryOfResidence variable set to "UK", and we will have to change that value after initializing. Another way to do this would be to use a default parameter value, as seen in the previous recipe. Amend the Person object initialization to the following:

class Person { 
let givenName: String
let middleName: String
let familyName: String
var countryOfResidence: String

init(givenName: String, middleName: String, familyName: String, countryOfResidence:
String = "UK") {
self.givenName = givenName
self.middleName = middleName
self.familyName = familyName
self.countryOfResidence = countryOfResidence
}
//...
}

Now, you can provide a country of residence in the initialization or omit it to use the default value:

class Person { 
//...
var displayString: String {
return "\(fullName()) - Location: \(countryOfResidence)"
}
//...
}

The property declaration for displayString is different from the others. Rather than having a value assigned to it, it is followed by an expression contained within curly braces. This is a computed property; its value is not static, but is determined by the given expression every time the property is accessed. Any valid expressions can be used to compute the property, but must return a value that matches the property type that is declared. The compiler will enforce this, and you can't omit the variable type for computed properties.

Since the value of the property is determined at the time of access, it follows that computed properties are read-only:

  
class Person {
//...
func fullName() -> String {
return "\(givenName) \(middleName) \(familyName))"
}
//...
}

Objects can do work based on the information they contain, and this work can be defined in methods. Methods are just functions that are contained within classes and have access to all the object's properties. All the abilities of a function are available, which we explored in the last recipe, including optional inputs and outputs, default parameter values, and parameter overloading:

  
final class Friend: Person {
var whereWeMet: String?
//...
}

Having defined a Person object, we want to extend the concept of Person to define a friend. A friend is also a person, so it stands to reason that anything a Person object can do, a Friend object can also do. We model this inherited behavior by defining Friend as a subclass of Person. We define the class that our Friend class inherits from after the class name, separated by :.

By inheriting from Person, our Friend object inherits all the properties and methods from its superclass. We can add any extra functionality we require--in this case, a property holding details of where we met this friend.

The final prefix tells the compiler that we don't intend for this class to be subclassed; it is the final class in the inheritance hierarchy. This allows the compiler to make some optimizations as we know it won't be extended:

final class Friend: Person { 
//...
override var displayString: String {
return "\(super.displayString) - \(whereWeMet ?? "Don't know where we met")"
}
}

In addition to implementing new functionalities, we can override functionalities from the superclass using the override keyword. In the preceding code, we override the displayString computed property from Person as we want to add the "where we met" information. Within the computed property, we want to get the superclass's implementation; we do this by referencing super and ., and then referencing the property. We can do the same to access the superclass's methods:

final class Family: Person {
let relationship: String
init(givenName: String, middleName: String, familyName: String = "Moon", relationship: String) {
self.relationship = relationship
super.init(givenName: givenName, middleName: middleName, familyName: familyName)
}
//...
}

Our Family class also inherits from Person, and we want to add a relationship property, which we want to form part of the initialization, so we can declare a new init that also takes a relationship value.

There's more...

Class objects are reference types that refer to the way they are stored and referenced internally. To see how these reference type semantics work, consider the following code:

class MovieReview { 
let movieTitle: String
var starRating: Int // Rating out of 5
init(movieTitle: String, starRating: Int) {
self.movieTitle = movieTitle
self.starRating = starRating
}
}

// Write a review
let shawshankReviewOnYourWebsite = MovieReview(movieTitle: "Shawshank Redemption", starRating: 3)

// Post it to social media
let reviewLinkOnTwitter = shawshankReviewOnYourWebsite
let reviewLinkOnFacebook = shawshankReviewOnYourWebsite

print(reviewLinkOnTwitter.starRating) // 3
print(reviewLinkOnFacebook.starRating) // 3

// Reconsider my review
shawshankReviewOnYourWebsite.starRating = 5

// The change visible from anywhere with a reference to the object
print(reviewLinkOnTwitter.starRating) // 5
print(reviewLinkOnFacebook.starRating) // 5

We created a review object and assigned that review to two separate constants. As an object is a reference type, it is a reference to the object that is stored in the constant, rather than a new copy of the object. Therefore, when we reconsider our review and rightly give The Shawshank Redemption five stars, we are changing the underlying object, and all references that access that underlying object will see that the starRating property has changed.

See also

  • In Chapter 8, Performance and Responsiveness in Swift, we will examine reference semantics in more detail, and see how it affects performance.