Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Hands-On Design Patterns with Swift

You're reading from   Hands-On Design Patterns with Swift Master Swift best practices to build modular applications for mobile, desktop, and server platforms

Arrow left icon
Product type Paperback
Published in Dec 2018
Publisher Packt
ISBN-13 9781789135565
Length 414 pages
Edition 1st Edition
Languages
Arrow right icon
Authors (3):
Arrow left icon
Giordano Scalzo Giordano Scalzo
Author Profile Icon Giordano Scalzo
Giordano Scalzo
Florent Vilmart Florent Vilmart
Author Profile Icon Florent Vilmart
Florent Vilmart
Sergio De Simone Sergio De Simone
Author Profile Icon Sergio De Simone
Sergio De Simone
Arrow right icon
View More author details
Toc

Table of Contents (17) Chapters Close

Preface 1. Refreshing the Basics 2. Understanding ARC and Memory Management FREE CHAPTER 3. Diving into Foundation and the Standard Library 4. Working with Objective-C in a Mixed Code Base 5. Creational Patterns 6. Structural Patterns 7. Behavioral Patterns 8. Swift-Oriented Patterns 9. Using the Model-View-Controller Pattern 10. Model-View-ViewModel in Swift 11. Implementing Dependency Injection 12. Futures, Promises, and Reactive Programming 13. Modularize Your Apps with Swift Package Manager 14. Testing Your Code with Unit and UI Tests 15. Going Out in the Open (Source) 16. Other Books You May Enjoy

Protocols

The following is from Apple's Swift Programming Language book:

"A protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements. Any type that satisfies the requirements of a protocol is said to conform to that protocol."  
– Apple Inc., The Swift Programming Language (Swift 3.0.1), iBooks

Protocol-oriented programming is a vast topic that also deserves coverage. It is the subject of many discussions, and I won't dive into it in depth. However, let's go over the basic concepts, as they will be useful for understanding some concepts that will be explained later in this book.

Declaring a protocol

You declare protocols using the protocol keyword, as follows:

protocol Toggling {
mutating func toggle()
}

Now that this protocol has been declared, any time that you declare a type conforming to Toggling, you'll be required to implement a mutating toggle() function.

You can use protocols in your type declarations, method declarations, or variable declarations. While it is technically possible to use protocols as interfaces for your objects or structs, that is usually not how they are used in Swift. Often, you will find yourself conforming to protocols when declaring your custom types or later in your code base, part of extending your existing type to bring additional functionality to it. 

Conforming to a protocol

We have just declared this new toggling protocol. If we go back to the previous section about enums, you may remember that the State enum had a toggle() method. We can now declare that our enum, State, conforms to Toggling. As mentioned previously, we have many ways to declare our conformance. 

Conformance at declaration

The first method to declare a conformance is to do it at the top level, when you declare your custom type. You'll notice that the raw representation comes first, then the protocol conformance:

enum State: Int, Toggling {
case off = 0
case on

mutating func toggle() {
self = self == .on ? .off : .on
}
}

var state: State = .on
state.toggle()
assert(state == .off)

Conformance in an extension

The second way to declare a conformance is to add the conformance to an extension. The main benefit is that you can add functionalities, in the form of extensions, to existing types. The other main benefit of declaring a conformance inside of an extension is that you can scope this conformance to a particular file or module, with the private modifier.

For example, suppose that we want to add the toggle method to the Bool type, but only for the current file or your framework. You may not want it to leak outside, as the implementation may conflict with another one:

internal extension Bool: Toggling {
mutating func toggle() {
self = !self
}
}

var isReady = false
isReady.toggle()
assert(isReady)

Protocol extensions

With protocol extensions, it is possible to provide an implementation for the required methods, without letting the conforming types provide that implementation.

We have updated the Toggling protocol with an additional required member: isActive. With the protocol extension, we can declare a default implementation for our types, Bool and State. We can also provide a default implementation for any other type that would choose to conform to the Toggling protocol:

protocol Toggling {
mutating func toggle()
var isActive: Bool { get }
}

extension Toggling where Self == Bool {
var isActive: Bool {
return self
}
}

extension Toggling where Self == State {
var isActive: Bool {
return self == .on
}
}

Default implementations

It is possible to provide default implementations for protocols through extensions. Previously, we provided a partial default implementation for the Toggling protocol on a well-known type. But any other type, that would conform to Toggling needs to provide an implementation on isActive. Using another example, let's look at how we can leverage default implementations in protocol extensions without requiring additional conformance work. 

Let's work with a simple protocol, Adder, for the sake of the example:

protocol Adder {
func add(value: Int) -> Int
func remove(value: Int) -> Int
}

The Adder protocol declares two methods: add and remove. And, if we remember our math classes well, we can very well declare remove as a function of add. Removing is just adding a negative value. Protocol extension allows us to do just that:

extension Adder {
func remove(value: Int) -> Int {
return add(value: -value)
}
}

This may look a bit silly, but in reality, this pattern is really powerful. Remember, we were able to implement remove because we were able to express it as a function of another provided method. Often, in our code, we can implement a method as a function of another. Protocols give us a contract that is fulfilled by either the concrete type or the extension, and we can effectively and expressively compose our programs around those capabilities.

lock icon The rest of the chapter is locked
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 £16.99/month. Cancel anytime