Book Image

Hands-On Design Patterns with Swift

By : Florent Vilmart, Giordano Scalzo, Sergio De Simone
Book Image

Hands-On Design Patterns with Swift

By: Florent Vilmart, Giordano Scalzo, Sergio De Simone

Overview of this book

Swift keeps gaining traction not only amongst Apple developers but also as a server-side language. This book demonstrates how to apply design patterns and best practices in real-life situations, whether that's for new or already existing projects. You’ll begin with a quick refresher on Swift, the compiler, the standard library, and the foundation, followed by the Cocoa design patterns – the ones at the core of many cocoa libraries – to follow up with the creational, structural, and behavioral patterns as defined by the GoF. You'll get acquainted with application architecture, as well as the most popular architectural design patterns, such as MVC and MVVM, and learn to use them in the context of Swift. In addition, you’ll walk through dependency injection and functional reactive programming. Special emphasis will be given to techniques to handle concurrency, including callbacks, futures and promises, and reactive programming. These techniques will help you adopt a test-driven approach to your workflow in order to use Swift Package Manager and integrate the framework into the original code base, along with Unit and UI testing. By the end of the book, you'll be able to build applications that are scalable, faster, and easier to maintain.
Table of Contents (22 chapters)
Title Page
Copyright and Credits
About Packt
Contributors
Preface
Index

Classes and structs


Let's start with a quick refresher on classes and structures. Both of them help to encapsulate functionality by defining methods and properties. While they share the same semantics, they differ in many ways. In this section, we'll quickly refresh you on the differences between classes and structs, and we will show you a simple refactoring from classes to structs.

Classes

Let's start with an example of a simple class that represents a Point in an x, y coordinate system (Cartesian). Consider the following:

class Point {
    var x: Double
    var y: Double

    init(x: Double, y: Double) {
        self.x = x
        self.y = y
    }
}

Now, let's define a simple translate function that will mutate the x and y properties of the point objects by adding dx and dy to x and y, respectively:

func translate(point : Point, dx : Double, dy : Double) {
 point.x += dx
 point.y += dy
}

Now, we can create a point instance with, for example, an initial value of 0.0, and translate it to the position 1.0:

let point = Point(x: 0.0, y: 0.0)
translate(point: point, dx: 1.0, dy: 1.0)
point.x == 1.0
point.y == 1.0

Because classes follow reference semantics, only a reference to the point object is passed to the translate function; x and y are defined as var, and all of this code is valid.

Struct

Now, let's try to port our Point class into a struct. Consider the following:

struct Point {
    var x: Double
    var y: Double
}

We have defined a simple struct; as you should notice, there's no need to add a constructor, as the compiler will synthesize it for us:

let point = Point(x: 0.0, y: 0.0)
translate(point: point, dx: 1.0, dy: 1.0)

If we keep the original implementation, our program won't compile. Point is a value type now, and it's forbidden to mutate a value inside of a function! We need to add the inout keyword to indicate that this function will mutate the contents of the value that is passed. When the function returns, the value will be assigned back to the original variable.

With those changes complete, we also need to change our call to indicate that our point variable can be modified by our translate function with the & (ampersand) character. We also need to mark our point as var; otherwise, the inout function cannot modify its contents:

func translate(point: inout Point, dx : Double, dy : Double) {
    point.x += dx
    point.y += dy
}

var point = Point(x: 0.0, y: 0.0)
translate(&point, dx: 1.0, dy: 1.0)
point.x == 1.0 // true
point.y == 1.0 // true

We've successfully ported this function, but we can do better.

With structs, you will often see that this pattern is cumbersome. We may want the translate function to return a mutated copy of the value we passed in, as follows:

func translate(point: Point, dx : Double, dy : Double) -> Point {
    var point = point
    translate(point: &point, dx : dx, dy : dy)
    return point
}

We'll be able to use the previously defined function with the following code:

let point = Point(x: 0.0, y: 0.0)
let translatedPoint = translate(point, dx: 1.0, dy: 1.0)
point.x == 0.0
point.y == 0.0
translatedPoint.x == 1.0
translatedPoint.y == 1.0

With this new implementation, we're not mutating the value anymore, but the translate function is always returning a new Point value. This has many benefits, including the ability to chain such calls together. Let's add a method to our Point struct:

extension Point {
    func translating(dx: Double, dy: Double) -> Point {
        return translate(point: self, dx: dx, dy: dy)
    }
}

Note

You don't need to declare this new method in your struct, but you can declare it anywhere in your program. 

Using our newly crafted extension, we can easily create new Point values and translate them:

let point = Point(x: 0.0, y: 0.0)
    .translating(dx : 5.0, dy : 2.0)
    .translating(dx : 2.0, dy : 3.0)
point.x == 7.0
point.y == 5.0