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

Closures, functions, and currying


Closures are blocks of code that can be executed later, and functions are a special case of closures. Functions and closures can be passed around in your code, returned by other functions or closures. You can store a closure or a function in a variable, and execute them later:

let runMe = { () -> Int in
    print(“run”)
    return 0
}
runMe()

The preceding code is equivalent to the following:

func runMe() -> Int {
    print(“run”)
    return 0
}
runMe()

Closures and functions are almost always interchangeable, except when it comes to class or struct members:

class MyClass{
var running = false
lazyvar runWithClosure: () -> Void = {
self.running = true
}

func runWithFunction() {
self.running = true
}
}

While both implementations are somewhat equivalent, we rarely want this function to be overridable at runtime. The closure can't reference self inside of it, unless marked lazy. Marking it lazy forces the implementation to be var, which, in turn, doesn't reflect what we want to express. In practice, we never declare instance methods as closures.

Currying

Functions and closures don't have to be defined at the top level. This can be unintuitive, when coming from languages such as Objective-C and Java. Swift, like JavaScript, lets you define functions and closures anywhere in your code. Functions can also return functions. This mechanism is known as currying.

Imagine that you want to create a logger method that will print a single argument, but it will always pretend to be a string to find it easily in your logs.

Let's start with the following basic implementation:

private let PREFIX = ‘MyPrefix'

private func log(_ value: String) {
    print(PREFIX + “ “ + value)
}

class MyClass {
    func doSomething() {
        log(“before”)
        /* complex code */
        log(“after”)
    }
}

While this works properly in the scope of a simple class, if you need to reuse the log method or change the internal implementation, this will lead to a lot of duplication.

You can use currying to overcome that issue, as follows:

func logger(prefix: String) -> (String) ->  Void {
    func log(value: String) {
        print(prefix + “ “ + value)
    }
    return log
}

let log = logger(prefix: “MyClass”)
log(“before”)
// do something
log(“after”)

// console:
MyClass before
MyClass after

Using closures as callbacks

Functions and closures can capture the current scope, which means all of the declared variables outside of the function or closure definition, such as local variables or self. In the case of self, you can inadvertently extended the lifetime of your objects and leak memory:

class MyClass {
    var running = false
    func run() {
        running = true
        DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
            self.running = false
        }
    }
}

var instance: MyClass? = MyClass()
instance?.run()
instance = nil

Can you spot the potential issue in this code?

Depending on the use case, you may want instance to be destroyed when it is not referenced by any owner. In our case, we'll probably cause a memory leak, as the dispatch block is referencing self without any memory management qualifier.

Using weak and unowned

Swift provides us with two keywords that indicate how we want to extend the lifetime of an object in a closure. While both prevent creating retain cycles, they are fundamentally different.

Using weak will wrap the captured value inside of an optional, indicating that the instance may have been deallocated before the closure was executed:

class MyClass {
    var running = false
    func run() {
        running = true
        DispatchQueue.main.asyncAfter(deadline: .now() + 10) { [weak self] in
            self?.running = false
        }
    }
}

var instance: MyClass? = MyClass()
instance?.run()
instance = nil

In this execution, instance will immediately be deallocated when set to nil.

Using unowned indicates that the variable won't be owned by the block. Another mechanism should be responsible for ensuring that the lifetime of the captured object is properly extended until the block is executed:

class MyClass {
    var running = false
    func run() {
        running = true
        DispatchQueue.main.asyncAfter(deadline: .now() + 10) { [unowned self] in
            self.running = false
        }
    }
}

var instance: MyClass? = MyClass()
instance?.run()
instance = nil

In this case, your program will crash when the block is executing, because the self variable will be deallocated upon the execution of the block:

Fatal error: Attempted to read an unowned reference but object 0x7f80bc75a4e0 was already deallocated