Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Swift 4 Programming Cookbook

You're reading from   Swift 4 Programming Cookbook 50 task-oriented recipes to maximise Swift 4 productivity

Arrow left icon
Product type Paperback
Published in Sep 2017
Publisher Packt
ISBN-13 9781786460899
Length 384 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Keith Moon Keith Moon
Author Profile Icon Keith Moon
Keith Moon
Arrow right icon
View More author details
Toc

Table of Contents (9) Chapters Close

Preface 1. Swift Building Blocks FREE CHAPTER 2. Building on the Building Blocks 3. Data Wrangling with Swift Control Flow 4. Generics, Operators, and Nested Types 5. Beyond the Standard Library 6. Swift Playgrounds 7. Server-Side Swift 8. Performance and Responsiveness in Swift

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

  • In Chapter 8, Performance and Responsiveness in Swift, we will examine value semantics in more detail, and see how it affects performance.
You have been reading a chapter from
Swift 4 Programming Cookbook
Published in: Sep 2017
Publisher: Packt
ISBN-13: 9781786460899
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image