Search icon CANCEL
Subscription
0
Cart icon
Cart
Close icon
You have no products in your basket yet
Save more on your purchases!
Savings automatically calculated. No voucher code required
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletters
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Swift 4 Programming Cookbook

You're reading from  Swift 4 Programming Cookbook

Product type Book
Published in Sep 2017
Publisher Packt
ISBN-13 9781786460899
Pages 384 pages
Edition 1st Edition
Languages
Toc

Table of Contents (9) Chapters close

Preface 1. Swift Building Blocks 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

Protocols

Protocols are a way to describe the interface that a type provides; they can be thought of as a contract, defining how you can interact with instances of that type. They are a great way to abstract the "what" something does from "how" it does it. As we will see in the subsequent chapters, Swift adds functionalities to protocols, which make them even more useful and powerful.

Getting ready

We will continue to build on examples from the previous recipes, but don't worry if you haven't followed these yet as all the code you need is listed in the upcoming sections.

How to do it...

In the last recipe, we added a method to our Person class to save it to a remote database. This is a very useful functionality, and as we add more and more features to our app, we will likely have more types where we will want to save instances of that type to a remote database. Let's create a protocol to define how we will interface with anything that can be saved in this way:

protocol Saveable {
var saveNeeded: Bool { get set }
func saveToRemoteDatabase(handler: @escaping (Bool) -> Void)
}

How it works...

Protocols are defined with the protocol keyword, and the implementation is contained within curly brackets. It is conventional to begin a protocol name with a capital letter, and to name a protocol as either something that the type is or something that it does; in this protocol, we are declaring that any type implementing it is saveable.

Types implementing this protocol have two parts of the interface to implement.

The Saveable protocol declares that anything implementing it needs to have a variable called saveNeeded, which is a Bool. This property will indicate that the information held in the remote database is out of date and a save is needed. In addition to the usual property declaration, a protocol requires us to define whether the property can be accessed (get) and changed (set), which is added in curly brackets after the type declaration. Removing the set keywords makes it a read-only variable. Consider the given example:

var saveNeeded: Bool { get set } 
Defining a protocol property as read-only doesn't prevent an implementing type from allowing the property to be set, just that the setting of that property isn't defined in the interface.

The second part of our protocol definition is to describe the method we can call to save the information to the remote database. This func declaration is exactly the same as other function declarations we have seen; however, the implementation of the function, usually contained in the curly brackets, is omitted as this is provided by the implementing type:

func saveToRemoteDatabase(handler: @escaping (Bool) -> Void) 

Now that we have defined our protocol, we need to implement the Saveable protocol on our Person class that we have been using throughout this chapter:

class Person: Saveable { 
//....
var saveHandler: ((Bool) -> Void)?

func saveToRemoteDatabase(handler: @escaping (Bool) -> Void) {
saveHandler = handler
// Send person information to remove database
// Once remote save is complete, it calls saveComplete(Bool)
}

func saveComplete(success: Bool) {
saveHandler?(success)
}
}

We conform to a protocol, and in a similar way, we declare that an object inherits from another object, by adding the protocol name after the type name, separated by :. By adding this conformance, the compiler will complain that our Person object doesn't implement part of the protocol as we haven't declared a saveNeeded property, so let's add that:

class Person: Saveable { 
//....
var saveHandler: ((Bool) -> Void)?
var saveNeeded: Bool = true

func saveToRemoteDatabase(handler: @escaping (Bool) -> Void) {
saveHandler = handler
// Send person information to remove database
// Once remote save is complete, it calls saveComplete(Bool)
}

func saveComplete(success: Bool) {
saveHandler?(success)
}
}

We'll add a default value of true since when an instance of this object is created, it won't be in the remote database, and so it will need saving.

There's more...

Protocol conformance can be applied to classes, structs, enums, and even other protocols, allowing an instance to be stored and passed without needing to know how it's implemented under the hood. This provides many benefits, including testing using mock objects and changing implementations without changing how and where the implementations are used.

Let's add a feature to our app that lets us set a reminder for a contact's birthday, which we will also want to save to our remote database.

We can use class inheritance to give our reminder the save functionality, but a reminder should not have the same features and functionality as a person, and our process for saving a reminder may be different to that used for a person.

Instead, we can create our Reminder object and have it conform to the Saveable protocol:

class Reminder: Saveable { 

var dateOfReminder: String // There is a better to store dates, but this suffice currently.
var reminderDetail: String // eg. Alissa's birthday

init(date: String, detail: String) {
dateOfReminder = date
reminderDetail = detail
}
var saveHandler: ((Bool) -> Void)?
var saveNeeded: Bool = true

func saveToRemoteDatabase(handler: @escaping (Bool) -> Void) {
saveHandler = handler
// Send reminder information to remove database
// Once remote save is complete, it calls saveComplete(success: Bool)
}

func saveComplete(success: Bool) {
saveHandler?(success)
}
}

Our Reminder object conforms to Saveable and implements all the requirements.

We now have two objects that represent very different things and have different functionalities, but they both implement Saveable, and therefore we can treat them in a common way.

To see this in action, let's create an object that will manage the saving of information in our app:

class SaveManager { 
func save(_ thingToSave: Saveable) {
thingToSave.saveToRemoteDatabase { success in
print("Saved! Success: \(success))")
}
}
}
let colin = createPerson("Colin", "Alfred", "Moon") // This closure was covered in the previous recipe
let birthdayReminder = Reminder(date: "27/11/1982", detail: "Colin's Birthday")
let saveManager = SaveManager()
saveManager.save(colin)
saveManager.save(birthdayReminder)

In the preceding example, our SaveManager doesn't know the underlying types that it is being passed, but it doesn't need to. It receives instances that conform to the Saveable protocol, and can, therefore, use the interface provided by Saveable to save each object.

See also

Further information about protocols can be found in Apple's documentation of the Swift language at http://swiftbook.link/docs/protocols.

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 AU $19.99/month. Cancel anytime}