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)

Structs

Objects are great for encapsulating data and functionality behind a unifying and referenceable concept, such as a person. However, not everything is an object; we may have a set of data that is logically grouped together, but that isn't much more than that. It's not more than the sum of its parts--it is the sum of its parts.

For this, there are structs. Short for structure, structs can be found in the C programming language and were, therefore, available in Objective-C, which was built on top of C. If you are familiar with iOS/macOS development, CGRect is an example of a C struct.

Structs are value types, as opposed to classes, which are reference types, and as such behave differently when passed around. In this recipe, we will examine how structs work in Swift, and learn when and how to use them.

Getting ready

This recipe will build on top of the previous recipes, so open the playground you have used for the previous recipes. Don't worry if you haven't tried out the previous recipes; this one will contain all the code you need.

How to do it...

We have already defined a Person object as having three separate string properties relating to the person's name; however, these three separate strings don't exist in isolation from each other--they together define a person's name. Currently, to get a person's name, you have to access three separate properties and combine them. Let's tidy this up by defining a person's name as its own struct. Enter the following code into the playground and run the playground:

struct PersonName { 
let givenName: String
let middleName: String
var familyName: String

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

mutating func change(familyName: String) {
self.familyName = familyName
}
}
var alissasName = PersonName(givenName: "Alissa", middleName: "May", familyName: "Jones")

How it works...

Defining a struct is very similar to defining an object class, and that is intentional. Much of the functionality available to a class is also available to a struct.

Within the PersonName struct, we have properties for the three components of the name and the fullName method we saw earlier to combine the three name components into a full name string.

Next, we have a method to change the family name property, which is why we defined the familyName property as a var variable instead of a let constant. This method assigns a new value to a property of the struct; it is mutating, or changing, the struct, and therefore needs to be marked with the mutating keyword. This keyword is enforced by the compiler to remind us that when we mutate a struct, a new copy of the original struct is created with the new value. This is known as value-type semantics.

To see this in action, consider the following code:

let alissasBirthName = PersonName(givenName: "Alissa", middleName: "May", familyName: "Jones") 
print(alissasName.fullName()) // Alissa May Jones
var alissasCurrentName = alissasBirthName
print(alissasName.fullName()) // Alissa May Jones

So far, so good. We have created a PersonName struct and assigned it to a constant called alissasBirthName and a variable called alissasCurrentName.

When we change or "mutate" the alissasCurrentName variable, only this variable is changed; alissasBirthName is a copy, and so it doesn't have the amended family name, even though they were assigned from the same source:

alissasCurrentName.change(familyName: "Moon") 
print(alissasBirthName.fullName()) // Alissa May Jones
print(alissasCurrentName.fullName()) // Alissa May Moon

There's more...

Now that we have a PersonName struct, let's amend our Person class so that it can use it:

class Person { 

let birthName: PersonName
var currentName: PersonName
var countryOfResidence: String

init(name: PersonName, countryOfResidence: String = "UK") {
birthName = name
currentName = name
self.countryOfResidence = countryOfResidence
}

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

We've added the birthName and currentName properties of our new PersonName struct type, and we initiate them with the same value when the Person object is initiated. Since a person's birth name won't change, we define it as a constant, but their current name can change, so it's defined as a variable.

Now, let's create a new Person object:

var name = PersonName(givenName: "Alissa", middleName: "May", familyName: "Jones") 
let alissa = Person(name: name)
print(alissa.currentName.fullName()) // Alissa May Jones

Since our PersonName struct has value semantics, we can use this to enforce the behavior that we expect our model to have. We would expect to not be able to change a person's birth name, and if you try, you will find that the compiler won't let you.

As we discussed earlier, changing the family name mutates the struct, and so a new copy is made. However, we defined birthName as a constant, which can't be changed, so the only way we would be able to change the family name would be to change our definition of birthName from let to var:

alissa.birthName.change(familyName: "Moon") // Does not compile. Compiler tells you to change let to var 

When we change the currentName to have a new family name, which we can do, since we defined it as a var, it changes the currentName property, but not the birthName property, even though these were assigned from the same source:

print(alissa.birthName.fullName()) // Alissa May Jones 
print(alissa.currentName.fullName()) // Alissa May Jones
alissa.currentName.change(familyName: "Moon")
print(alissa.birthName.fullName()) // Alissa May Jones
print(alissa.currentName.fullName()) // Alissa May Moon

We have used a combination of objects and structs to create a model that enforces our expected behavior.

See also