Movatterモバイル変換


[0]ホーム

URL:


Packt
Search iconClose icon
Search icon CANCEL
Subscription
0
Cart icon
Your Cart(0 item)
Close icon
You have no products in your basket yet
Save more on your purchases!discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Profile icon
Account
Close icon

Change country

Modal Close icon
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timerSALE ENDS IN
0Days
:
00Hours
:
00Minutes
:
00Seconds
Home> Programming> Design Patterns> Hands-On Design Patterns with Swift
Hands-On Design Patterns with Swift
Hands-On Design Patterns with Swift

Hands-On Design Patterns with Swift: Master Swift best practices to build modular applications for mobile, desktop, and server platforms

Arrow left icon
Profile Icon VilmartProfile Icon Giordano ScalzoProfile Icon De Simone
Arrow right icon
$48.99
Full star iconFull star iconFull star iconFull star iconHalf star icon4.7(3 Ratings)
PaperbackDec 2018414 pages1st Edition
eBook
$35.98 $39.99
Paperback
$48.99
Subscription
Free Trial
Renews at $19.99p/m
Arrow left icon
Profile Icon VilmartProfile Icon Giordano ScalzoProfile Icon De Simone
Arrow right icon
$48.99
Full star iconFull star iconFull star iconFull star iconHalf star icon4.7(3 Ratings)
PaperbackDec 2018414 pages1st Edition
eBook
$35.98 $39.99
Paperback
$48.99
Subscription
Free Trial
Renews at $19.99p/m
eBook
$35.98 $39.99
Paperback
$48.99
Subscription
Free Trial
Renews at $19.99p/m

What do you get with Print?

Product feature iconInstant access to your digital copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Redeem a companion digital copy on all Print orders
Product feature icon Access this title in our online reader with advanced features
Product feature iconDRM FREE - Read whenever, wherever and however you want
OR

Contact Details

Modal Close icon
Payment Processing...
tickCompleted

Shipping Address

Billing Address

Shipping Methods
Table of content iconView table of contentsPreview book icon Preview Book

Hands-On Design Patterns with Swift

Refreshing the Basics

In order to properly kick-start our journey through Swift's best practices and design patterns, I believe it's important that we take some time to go back to the basics. It's important to always keep your foundation strong; the more we advance through this book, the more we'll rely on those concepts.

I'll assume that you have a proper understanding ofobject-oriented programming (OPP) fundamentals, classes, inheritance, composition, and other techniques, as well as a fundamental understanding of the differences between value and reference types. If you're rusty on these concepts, you shouldn't worry too much, as we'll cover them shortly.

This chapter will dive deeply into the Swift language. What is astruct,and what is aclass? What are their differences? Should you use anenum or anOptionSet? All of these questions will be answered in this chapter. We'll go back to the basics of classes and inheritance, and we'll discover the power of value types and immutability. We'll look it functions, closures, and currying. If you're unfamiliar with these constructs, or if you just want to get a refresher, you should tag along as we go back to the basics. These basics are essential to the Swift language, and are required to successfully apply efficient design patterns and best practices.

In this first chapter, we'll take the time to go back to the basics by covering the following topics:

  • Classes and structs: what they are, and how they behave
  • Exploring enums and their capabilities and extensibility
  • Getting functional with closures and functions
  • Introducing protocols and scratching the surface of extending protocols
  • Concluding with other useful language constructs, such as type aliases, tuples, and generics

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 anx,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 simpletranslate function that will mutate thex andy properties of thepoint objects by addingdx anddy tox andy, 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 of0.0, and translate it to the position1.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 andy are defined asvar, and all of this code is valid.

Struct

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

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

We have defined a simplestruct; 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 theinout 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 ourpoint variable can be modified by ourtranslate function with the& (ampersand) character. We also need to mark our point as var; otherwise, theinout 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 thetranslate 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 thetranslate function is always returning a newPoint value. This has many benefits, including the ability to chain such calls together. Let's add a method to ourPoint struct:

extension Point {
func translating(dx: Double, dy: Double) -> Point {
return translate(point: self, dx: dx, dy: dy)
}
}
You don't need to declare this new method in yourstruct, but you can declare it anywhere in your program. 

Using our newly crafted extension, we can easily create newPoint 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

Enums

Enums are one of the basic constructs that the Swift language offers. At the same level as classes, structs, and functions, they are used to represent values that can only have a finite amount of states.

Take theOptional enum, for example; it is represented by an enum perfectly. It represents a value that can have two, and only two, states, represented by the two members of theOptional enum. It can either be initialized to .none or filled with a value, .wrapped(value).

Enums are incredibly powerful in Swift. From very simple cases to generics, they are among the most powerful tools that we have for writing our programs.

Simple enums

Let's say you're building a smart light remote control; you can easily represent the state of this light with the followingenum:

enum State {
case on
case off
}

let anOnLight = State.on

This is a very simple example, and we could have used a Boolean value, but with theenum, we set ourselves up for expansion.

Adding methods

Now, we may want to add a method to thisState enumeration. After all, it's very common to just toggle the switch on and off without thinking:

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

var state: State = .on
state.toggle()
state == .off // true

As in the previous section, we can just extend theState enum to add thetoggle functionality. Enums follow value semantics; therefore, we have to mark thetoggle method as mutating.

Associating values

Enums can also contain associated values. In our scenario, we can leverage this to represent a dimmer. A dimmer changes the intensity of the light, so we can represent it with a third member-the dimmed member:

enum State: Equatable {
case on
case off
case dimmed(value: Double)
}

You may have noticed that we needed to add theEquatable conformance. This is required, as otherwise, the compiler can't synthesize equality anymore without our hint. This implementation works, but we lack a few things. First, not allDouble values are valid; we'd probably like to keep these in a reasonable span (between0 and1, for example). But perhaps not all of our lights support such values between0 and1. Others may want to support between0 and a100 or integers between0 and255.

Generic enums

In the following example, we will build a fully generic light:

enum State<T>: Equatable where T: Equatable {
case on
case off
case dimmed(T)
}

struct Bits8Dimming: Equatable {
let value: Int
init(_ value: Int) {
assert(value > 0 && value < 256)
self.value = value
}
}

struct ZeroOneDimming: Equatable {
let value: Double
init(_ value: Double) {
assert(value > 0 && value < 1)
self.value = value
}
}

let nostalgiaState: State<Bits8Dimming> = .dimmed(.init(10))
let otherState: State<ZeroOneDimming> = .dimmed(.init(0.4))

The dim type is now specified as a part of theState type. This gives us a lot of flexibility, as well as validation. Wrapping the value into a smallstruct adds very little overhead in terms of performance, and allows us to ensure that the values are sane before being set into ourenum

Raw type enums

A raw type is a base type for all enumeration members; in our example, we can hardcode presets for our dimming, as follows:

enum LightLevel: String {
case quarter
case half
case threequarters
}

let state: State<LightLevel> = .dimmed(.half)

Thanks to the generic implementation and the fact thatString is equatable, we can use this raw value in ourdimmed state.

With theLightLevel enum, which has a raw type ofString, the compiler will use the member name as the underlying raw value:

LightLevel.half.rawValue == “half” // == true

You can override these by specifying them, as follows:

enum LightLevel: String {
case quarter = “1/4”
case half = “1/2”
case threequarters = “3/4”
}

When usingInt as a raw type, the underlying raw values will follow the order of the cases:

enum Level: Int {
case base // == 0
case more // == 1
case high = 100
case higher // == 101
}

Switching the state of light

With our final case, let's look at how to interpret the current state of the light:

switch state {
case .on:
doSomething()
case .off:
doSomething()
case .dimmed(let value):
switch value {
case .quarter:
doSomething()
case .half:
doSomething()
case .threeQuarters:
doSomething()
}
}

Theswitch statement in Swift is very different from the one in Objective-C. First, the cases do not fall through each other, so there's no need to add thebreak statement after each case.

If you want multiple cases to be handled with the same code, you can use the following strategy:

switch state {
case .on, .off:
doSomething()
default:
break
}

Falling through is somehow not encouraged in Swift, so always try to adapt your code in order not to leverage this. If you can't avoid it, the following code shows how it should be implemented:

switch state {
case .off:
doSomethingOff()
fallthrough
case .on:
doSomething()
default:
break
}

Ifstate isoff, bothdoSomethingOff anddoSomething will be called. Ifstate ison, onlydoSomething will be called.

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 toclass orstruct 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 referenceself inside of it, unless markedlazyMarking 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 alogger 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 thelog 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 orself. In the case ofself, 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 wantinstance 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 referencingself 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.

Usingweak 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 tonil.

Usingunowned 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 theself variable will be deallocated upon the execution of the block:

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

Protocols

The following is from Apple'sSwift 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 theprotocol keyword, as follows:

protocol Toggling {
mutating func toggle()
}

Now that this protocol has been declared, any time that you declare a type conforming toToggling, you'll be required to implement a mutatingtoggle() 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 theState enum had atoggle() method. We can now declare that ourenumState, conforms toToggling. 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 theprivate modifier.

For example, suppose that we want to add thetoggle method to theBool 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 extensionBool:Toggling {
mutatingfunc 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 theToggling protocol with an additional required member: isActive. With the protocol extension, we can declare a default implementation for our types, Bool andState. We can also provide a default implementation for any other type that would choose to conform to theToggling protocol:

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

extensionTogglingwhereSelf ==Bool {
var isActive:Bool {
returnself
}
}

extensionTogglingwhereSelf ==State {
var isActive:Bool {
returnself == .on
}
}

Default implementations

It is possible to provide default implementations for protocols through extensions. Previously, we provided a partial default implementation for theToggling protocol on a well-known type. But any other type, that would conform toToggling needs to provide an implementation onisActive. 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
}

TheAdder protocol declares two methods: add andremove. And, if we remember our math classes well, we can very well declareremove as a function ofadd. Removing is just adding a negative value. Protocol extension allows us to do just that:

extensionAdder {
func remove(value:Int) ->Int {
returnadd(value: -value)
}
}

This may look a bit silly, but in reality, this pattern is really powerful. Remember, we were able to implementremove 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.

Tuples, type aliases, and generics

This chapter would not be complete if we didn't address some very useful features from Swift. Tuples are very useful types that let you return multiple objects as one, without a strongly typed wrapper. Aliases let you quickly define simple type shortcuts. Finally, we'll cover the basics of generics. While generics could be covered in a whole book, we'll just scratch the surface of their syntax, features, and limits, as we'll make use of them extensively throughout this book.

Tuples

Tuples are used to represent a group of values as a single value. Tuples cannot conform to protocols, nor can they inherit. They cannot declare functions in the same way that we can declare a function on astruct or aclass. They may look limited, but they have their place as first-class types in the language.

Declaring tuples

Tuples can hold any number of values, from any number of types. You can declare a tuple with the same types—let's say a 2D point inDouble:

let origin = (0.0, 0.0)

You can also name the parameters, as follows:

let point = (x: 10.0, y: 10.0)

The two forms are equivalent, but you may want to use the named version, for readability reasons. If you're referencing a size, for example, the tuple would more accordingly be named(width: Double, height: Double). For obvious reasons, this helps to provide a better understanding of your code.

Destructuring tuples

There is a simple method to access tuple values. Take, for example, thesize pair, as follows:

let size = (width: 200, height: 400)
let (w, h) = size
let (width, _) = size

In the preceding example, we initialize a tuple on the first line. On the second line, we destructure both parameters asw andh. On the last line is what we call a partial destructuring: when you're only interested in one part of the tuple, you can extract only a part of it. This is useful when dealing with large tuples.

Using tuples in functions

Tuples are first-class citizens in Swift; you can use them, like any other type, as function parameters. The following code demonstrates how to declare a simple function that computes to the Euclidean distance between two points, a andb, represented by tuples:

func distance(_ a: (Double,Double), _ b: (Double,Double)) ->Double {
returnsqrt(pow(b.0 - a.0,2) +pow(b.1 - a.1,2))
}
distance(point, origin) == 5.0

You may have noticed that the named parameters of thepoint tuple are ignored in this case; any pair ofDouble will be accepted in the method, no matter what they are named.

The opposite is true, as well:

func slope(_ a: (x:Double, y:Double),_ b: (x:Double, y:Double)) ->Double {
return (b.y - a.y) / (b.x - a.x)
}

slope((10, 10), (x: 1, y: 1)) == 1

We've seen examples of using tuples with the same types, but remember that, tuples can contain any type, and as many values as you wish.

Type aliases

Type aliases are a simple addition to the language; they let you reference simple or complex types by an alias. They support all declarations that you can imagine, from the simplest to the most complex.

The following block contains declarations for aliasing the following:

  • A string class into a MyString
  • A function declaration into a Block
  • A block that takes any argument and returns any value
  • A block that takes no argument and returns any value

Let's see the code block; they let you:

typealias MyString = String
typealias Block = () -> Void
typealias TypedBlock<T, U> = (T) ->U
typealias ReturningBlock<U> = () -> U

We could have also definedBlock in the function ofReturningBlock:

typealias Block =ReturningBlock<()>

You can also use type aliases for protocol compositions and complex types, as follows:

  • You can declare a type that conforms to a protocol and is of a particular class
  • You can delete a type that conforms to multiple protocols

Let's see an example, as follows:

protocol SomeProtocol {}
protocol OtherProtocol {}

typealias ViewControllerProtocol =NSViewController & SomeProtocol
typealias BothProtocols = SomeProtocol & OtherProtocol

You will often find yourself using type aliases, in order to make your code more readable and more expressive. They are a powerful tool for hiding away some of the implementation complexity or verbosity when declaring long conformances. With type aliases, you can be encouraged to craft many protocols, each with a very small requirement list; then, you can compose all of those protocols when you need them, expressed as those types.

Generics

Generics is a complex subject, and would likely require a full book of its own, for extensive coverage extensively. For the purpose of this book, we'll provide a quick refresher on generics, covering the basics that are required to understand the constructions that we'll use in the different design patterns presented in the next chapters.

Generic functions

In Swift, the simplest form of generics would be the generics in functions. You can use generics very simply, with angled brackets, as follows:

func concat<T>(a: T, b: T) -> [T] {
return [a,b]
}

Theconcat method knows nothing about the types that you are passing in, but generics gives us many guarantees over usingAny:

  • a andb should be of the same type
  • Thereturn type is an array of elements that have the same type asa andb
  • The type is inferred from the context so you don't have to type it in when you code

You can also leverage protocol conformance in your generic functions, as follows:

protocol Runnable {
func run()
}

func run<T>(runnable: T) where T: Runnable {
runnable.run()
}

In this case, the method that is run can only be called with an object that isRunnable.

Generic types

You can also make complex types generic. In our example, we created this wrapper around a list ofRunnable, calledManyRunner. The job of a many runner is to run all of the runnables. TheManyRunner is itself Runnable, so we have created a kind of type recursion, as follows:

struct ManyRunner<T>: Runnable where T: Runnable {
let runnables: [T]
func run() {
runnables.forEach { $0.run() }
}
}

Let's also provide a base object that runs a simpleIncrementer. Each time theIncrementer is run, the static count will increment, to keep track of the number of invocations:

struct Incrementer: Runnable {
private(set) static var count = 0
func run() {
Incrementer.count += 1
}
}

When using generics on types, remember that the types have to be the same:

// This works
let runner = ManyRunner(runnables: [Incrementer(),Incrementer()])
runner.run()
assert(Incrementer.count == 2)
// runner is of type ManyRunner<Incrementer>



ManyRunner(runnables: [Incrementer(), Runners(runnables: [Incrementer()])] as [Runnable]).run()
// This produces the following compile error
// In argument type '[Runnable]', 'Runnable' does not conform to expected type 'Runnable'

We'll look at how to overcome these limitationsinChapter 8Swift-Oriented Patterns.

Generics, protocols, and associated types

You can also use associated types in your protocols. These associated types let you define protocols that are generics, like this: RunnableWithResult. We can implement a bunch of logic and code around therun() method, without actually knowing anything about the return types. We'll encounter this construction many times in this book, so it's important that you're comfortable with associate types:

protocol RunnableWithResult {
associatedtype ResultType
func run() -> ResultType
}

struct RunnersWithResult<T>: RunnableWithResult where T: RunnableWithResult {
let runnables: [T]
func run() -> [T.ResultType] {
return runnables.map { $0.run() }
}
}

Like with generic types, you can't mix and match heterogeneous types. The following example will not compile; later in this book, you'll see strategies for overcoming this common problem when dealing with generics:

struct IntRunnable {
func run() -> Int {
return 0
}
}

struct StringRunnable {
func run() -> String {
return "OK"
}
}

let runnables: [RunnableWithResult] = [StringRunnable(), IntRunnable()]

This will yield the following dreaded error:

Protocol 'RunnableWithResult' can only be used as a generic constraint because it has Self or associated type requirements

Summary

In this chapter, we covered everything that I consider a prerequisite for the rest of this book. We started with classes, the basic building blocks of OOP. You should now be really familiar with them. Structs are unusual constructions for someone coming from OOP, but they are very useful in Swift, as they behave as values, can be immutable, and have other nice properties. With enums, you'll be able to write even more expressive code.

Functions and closures are first-class citizens in Swift, and should be treated as such. Currying is a powerful pattern that lets you reuse functions; in later chapters, you'll see how to use it to write clean code.

The concept of protocols opens the world of protocol extensions and protocol-oriented programming, which is a complex subject. In the following chapters, we'll look at various use cases for implementing particular patterns through protocol extensions.

In the next chapter, we'll focus on memory management and ARC. While value types are not subject to reference counting, classes, functions, and closures interact with each other, and can lead to memory-related crashes and other issues.

Download code iconDownload Code

Key benefits

  • Write clean, reusable and maintainable code, and make the most of the latest Swift version.
  • Analyze case studies of some of the popular open source projects and give your workflow a huge boost
  • Choose patterns such as MVP, MVC, and MVVM depending on the application being built

Description

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.

Who is this book for?

This book is for intermediate developers who want to apply design patterns with Swift to structure and scale their applications. You are expected to have basic knowledge of iOS and Swift.

What you will learn

  • Work efficiently with Foundation and Swift Standard library
  • Understand the most critical GoF patterns and use them efficiently
  • Use Swift 4.2 and its unique capabilities (and limitations) to implement and improve GoF patterns
  • Improve your application architecture and optimize for maintainability and performance
  • Write efficient and clean concurrent programs using futures and promises, or reactive programming techniques
  • Use Swift Package Manager to refactor your program into reusable components
  • Leverage testing and other techniques for writing robust code
Estimated delivery feeDeliver to South Africa

Standard delivery10 - 13 business days

$12.95

Premium delivery3 - 6 business days

$34.95
(Includes tracking information)

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date :Dec 24, 2018
Length:414 pages
Edition :1st
Language :English
ISBN-13 :9781789135565
Vendor :
Apple
Category :
Languages :

What do you get with Print?

Product feature iconInstant access to your digital copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Redeem a companion digital copy on all Print orders
Product feature icon Access this title in our online reader with advanced features
Product feature iconDRM FREE - Read whenever, wherever and however you want
OR

Contact Details

Modal Close icon
Payment Processing...
tickCompleted

Shipping Address

Billing Address

Shipping Methods
Estimated delivery feeDeliver to South Africa

Standard delivery10 - 13 business days

$12.95

Premium delivery3 - 6 business days

$34.95
(Includes tracking information)

Product Details

Publication date :Dec 24, 2018
Length:414 pages
Edition :1st
Language :English
ISBN-13 :9781789135565
Vendor :
Apple
Category :
Languages :
Concepts :
Tools :

Packt Subscriptions

See our plans and pricing
Modal Close icon
$19.99billed monthly
Feature tick iconUnlimited access to Packt's library of 7,000+ practical books and videos
Feature tick iconConstantly refreshed with 50+ new titles a month
Feature tick iconExclusive Early access to books as they're written
Feature tick iconSolve problems while you work with advanced search and reference features
Feature tick iconOffline reading on the mobile app
Feature tick iconSimple pricing, no contract
$199.99billed annually
Feature tick iconUnlimited access to Packt's library of 7,000+ practical books and videos
Feature tick iconConstantly refreshed with 50+ new titles a month
Feature tick iconExclusive Early access to books as they're written
Feature tick iconSolve problems while you work with advanced search and reference features
Feature tick iconOffline reading on the mobile app
Feature tick iconChoose a DRM-free eBook or Video every month to keep
Feature tick iconPLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick iconExclusive print discounts
$279.99billed in 18 months
Feature tick iconUnlimited access to Packt's library of 7,000+ practical books and videos
Feature tick iconConstantly refreshed with 50+ new titles a month
Feature tick iconExclusive Early access to books as they're written
Feature tick iconSolve problems while you work with advanced search and reference features
Feature tick iconOffline reading on the mobile app
Feature tick iconChoose a DRM-free eBook or Video every month to keep
Feature tick iconPLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick iconExclusive print discounts

Frequently bought together


Swift Protocol-Oriented Programming
Swift Protocol-Oriented Programming
Read more
Jun 2019224 pages
eBook
eBook
$19.99$22.99
$32.99
Mastering Swift 5
Mastering Swift 5
Read more
Apr 2019370 pages
Full star icon3.8 (29)
eBook
eBook
$23.99$26.99
$38.99
Hands-On Design Patterns with Swift
Hands-On Design Patterns with Swift
Read more
Dec 2018414 pages
Full star icon4.7 (3)
eBook
eBook
$35.98$39.99
$48.99
Stars icon
Total$120.97
Swift Protocol-Oriented Programming
$32.99
Mastering Swift 5
$38.99
Hands-On Design Patterns with Swift
$48.99
Total$120.97Stars icon

Table of Contents

16 Chapters
Refreshing the BasicsChevron down iconChevron up icon
Refreshing the Basics
Classes and structs
Enums
Closures, functions, and currying
Protocols
Tuples, type aliases, and generics
Summary
Understanding ARC and Memory ManagementChevron down iconChevron up icon
Understanding ARC and Memory Management
A brief history of reference counting
ARC – what is that?
Memory debugging
Leaks, cycles, and dangling references
Summary
Diving into Foundation and the Standard LibraryChevron down iconChevron up icon
Diving into Foundation and the Standard Library
Swift basic types
Container types
Mastering concurrency with Dispatch
HTTP with URLSession
Summary
Working with Objective-C in a Mixed Code BaseChevron down iconChevron up icon
Working with Objective-C in a Mixed Code Base
Setting up your project
Nullability and optionals in Objective-C
Naming, renaming, and refining Objective-C for Swift
Lightweight generics in Objective-C
Cocoa design patterns in Swift
Summary
Creational PatternsChevron down iconChevron up icon
Creational Patterns
The singleton pattern 
The factory method pattern
The abstract factory pattern
The builder pattern
The prototype pattern
Summary
Structural PatternsChevron down iconChevron up icon
Structural Patterns
The adapter pattern
The decorator pattern
The facade pattern and proxy pattern
The composite pattern
The bridge pattern
The flyweight pattern
Summary
Behavioral PatternsChevron down iconChevron up icon
Behavioral Patterns
The state pattern
The observer pattern
The memento pattern
The visitor pattern
The strategy pattern
Summary
Swift-Oriented PatternsChevron down iconChevron up icon
Swift-Oriented Patterns
Getting started with protocol-oriented programming
The type erasure pattern
Template pattern with protocol-oriented programming
Summary
Using the Model-View-Controller PatternChevron down iconChevron up icon
Using the Model-View-Controller Pattern
A refresher on MVC
UIViewController
Composition and child view controllers
The model layer
Summary
Model-View-ViewModel in SwiftChevron down iconChevron up icon
Model-View-ViewModel in Swift
Basics of the MVVM pattern
MVVM and data binding
Using Observables with ViewModels
Summary
Implementing Dependency InjectionChevron down iconChevron up icon
Implementing Dependency Injection
Dependency Injection, a primer
Dependency Injection by example
Using a Dependency Injection Container
Summary
Futures, Promises, and Reactive ProgrammingChevron down iconChevron up icon
Futures, Promises, and Reactive Programming
Callbacks and closures
Futures and promises
Reactive programming
Summary
Modularize Your Apps with Swift Package ManagerChevron down iconChevron up icon
Modularize Your Apps with Swift Package Manager
Creating a library package
Adding third-party dependencies
Extracting and sharing a framework
Summary
Testing Your Code with Unit and UI TestsChevron down iconChevron up icon
Testing Your Code with Unit and UI Tests
Unit testing using XCTest
Advanced testing with mocks, spy, and others
UI testing with Xcode
Tips and tricks
Summary
Going Out in the Open (Source)Chevron down iconChevron up icon
Going Out in the Open (Source)
Documenting Swift
Continuous integration
Using fastlane for automated delivery
Becoming a maintainer, tips and tricks
Summary
Other Books You May EnjoyChevron down iconChevron up icon
Other Books You May Enjoy
Leave a review - let other readers know what you think

Recommendations for you

Left arrow icon
Debunking C++ Myths
Debunking C++ Myths
Read more
Dec 2024226 pages
Full star icon5 (1)
eBook
eBook
$27.99$31.99
$39.99
Go Recipes for Developers
Go Recipes for Developers
Read more
Dec 2024350 pages
eBook
eBook
$27.99$31.99
$39.99
50 Algorithms Every Programmer Should Know
50 Algorithms Every Programmer Should Know
Read more
Sep 2023538 pages
Full star icon4.5 (68)
eBook
eBook
$35.98$39.99
$49.99
$49.99
Asynchronous Programming with C++
Asynchronous Programming with C++
Read more
Nov 2024424 pages
Full star icon5 (1)
eBook
eBook
$29.99$33.99
$41.99
Modern CMake for C++
Modern CMake for C++
Read more
May 2024504 pages
Full star icon4.7 (12)
eBook
eBook
$35.98$39.99
$49.99
Learn Python Programming
Learn Python Programming
Read more
Nov 2024616 pages
Full star icon5 (1)
eBook
eBook
$31.99$35.99
$39.99
Learn to Code with Rust
Learn to Code with Rust
Read more
Nov 202457hrs 40mins
Video
Video
$74.99
Modern Python Cookbook
Modern Python Cookbook
Read more
Jul 2024818 pages
Full star icon4.9 (21)
eBook
eBook
$38.99$43.99
$54.99
Right arrow icon

Customer reviews

Rating distribution
Full star iconFull star iconFull star iconFull star iconHalf star icon4.7
(3 Ratings)
5 star66.7%
4 star33.3%
3 star0%
2 star0%
1 star0%
Pijush DebbarmaFeb 20, 2019
Full star iconFull star iconFull star iconFull star iconFull star icon5
Quality of page is very good. Contents also good for beginner to professional privilege. The writter did a great job for it's reader. 5 starts 👍👍👍👍
Amazon Verified reviewAmazon
:DJul 14, 2019
Full star iconFull star iconFull star iconFull star iconFull star icon5
So far so good...5 stars for Chapter 1 & 2
Amazon Verified reviewAmazon
Hamza ButtDec 28, 2020
Full star iconFull star iconFull star iconFull star iconEmpty star icon4
My book arrived slightly scuffed at the time, having been brought brand new this was not expected and obviously very disappointing from Amazon.That being said, the book is fantastic and expects a reasonable baseline of knowledge; then moves at a quick pace, introducing concepts all integral to native iOS development.
Amazon Verified reviewAmazon

People who bought this also bought

Left arrow icon
50 Algorithms Every Programmer Should Know
50 Algorithms Every Programmer Should Know
Read more
Sep 2023538 pages
Full star icon4.5 (68)
eBook
eBook
$35.98$39.99
$49.99
$49.99
Event-Driven Architecture in Golang
Event-Driven Architecture in Golang
Read more
Nov 2022384 pages
Full star icon4.9 (11)
eBook
eBook
$35.98$39.99
$49.99
The Python Workshop Second Edition
The Python Workshop Second Edition
Read more
Nov 2022600 pages
Full star icon4.6 (22)
eBook
eBook
$36.99$41.99
$51.99
Template Metaprogramming with C++
Template Metaprogramming with C++
Read more
Aug 2022480 pages
Full star icon4.6 (14)
eBook
eBook
$33.99$37.99
$46.99
Domain-Driven Design with Golang
Domain-Driven Design with Golang
Read more
Dec 2022204 pages
Full star icon4.4 (19)
eBook
eBook
$31.99$35.99
$44.99
Right arrow icon

About the 3 authors

Left arrow icon
Profile icon Vilmart
Vilmart
Github icon
Florent Vilmart, M.Sc. is a full stack engineer in Montreal. Born in France, he moved to Montreal short before graduating looking for exciting opportunities in the francophone metropole of North-America. He honed his skills with Objective-C before jumping to Swift when it was released. Hes a polyglot, and you can find his open source work on the parse-community GitHub project where he is one of the core maintainers of the multiple projects. He also spoke at conferences like the GithubCodeConf in 2016 and most recently the Swift Summit in 2017. Currently, he is working full time with Swift, Javascript, and Kubernetes and leads a team of 6 engineers.
Read more
See other products by Vilmart
Profile icon Giordano Scalzo
Giordano Scalzo
LinkedIn iconGithub icon
Giordano Scalzo is a developer with 20 years of programming experience, since the days of ZX Spectrum. He has worked in Swift, Objective-C, C/C++, Java, .Net, Ruby, Python, and in a ton of other languages that he has forgotten the names of. After years of backend development, over the past 10 years, Giordano has developed extensively for iOS, releasing more than 20 apps—apps that he wrote for clients, enterprise applications, or on his own. He is currently a Tech Lead Consultant in London, where he leads mobile digital transformations through his company, Effective Code Ltd.
Read more
See other products by Giordano Scalzo
Profile icon De Simone
De Simone
Sergio De Simone has been working as a software engineer for over twenty years across a range of different projects and companies, including work environments such as Siemens, HP, and small start-ups. For the last few years, his focus has been on developing mobile platforms and related technologies. He is currently working for BigML, Inc., where he leads iOS and OS X development. Additionally, he likes writing about technology, as well as programming tools, techniques, and languages, with a special focus on Swift evolution, for InfoQ.
Read more
See other products by De Simone
Right arrow icon
Getfree access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is the digital copy I get with my Print order?Chevron down iconChevron up icon

When you buy any Print edition of our Books, you can redeem (for free) the eBook edition of the Print Book you’ve purchased. This gives you instant access to your book when you make an order via PDF, EPUB or our online Reader experience.

What is the delivery time and cost of print book?Chevron down iconChevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium:Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela
What is custom duty/charge?Chevron down iconChevron up icon

Customs duty are charges levied on goods when they cross international borders. It is a tax that is imposed on imported goods. These duties are charged by special authorities and bodies created by local governments and are meant to protect local industries, economies, and businesses.

Do I have to pay customs charges for the print book order?Chevron down iconChevron up icon

The orders shipped to the countries that are listed under EU27 will not bear custom charges. They are paid by Packt as part of the order.

List of EU27 countries:www.gov.uk/eu-eea:

A custom duty or localized taxes may be applicable on the shipment and would be charged by the recipient country outside of the EU27 which should be paid by the customer and these duties are not included in the shipping charges been charged on the order.

How do I know my custom duty charges?Chevron down iconChevron up icon

The amount of duty payable varies greatly depending on the imported goods, the country of origin and several other factors like the total invoice amount or dimensions like weight, and other such criteria applicable in your country.

For example:

  • If you live inMexico, and the declared value of your ordered items is over $ 50, for you to receive a package, you will have to pay additional import tax of 19% which will be $ 9.50 to the courier service.
  • Whereas if you live inTurkey, and the declared value of your ordered items is over € 22, for you to receive a package, you will have to pay additional import tax of 18% which will be € 3.96 to the courier service.
How can I cancel my order?Chevron down iconChevron up icon

Cancellation Policy for Published Printed Books:

You can cancel any order within 1 hour of placing the order. Simply contact customercare@packt.com with your order details or payment transaction id. If your order has already started the shipment process, we will do our best to stop it. However, if it is already on the way to you then when you receive it, you can contact us at customercare@packt.com using the returns and refund process.

Please understand that Packt Publishing cannot provide refunds or cancel any order except for the cases described in our Return Policy (i.e. Packt Publishing agrees to replace your printed book because it arrives damaged or material defect in book), Packt Publishing will not accept returns.

What is your returns and refunds policy?Chevron down iconChevron up icon

Return Policy:

We want you to be happy with your purchase from Packtpub.com. We will not hassle you with returning print books to us. If the print book you receive from us is incorrect, damaged, doesn't work or is unacceptably late, please contact Customer Relations Team on customercare@packt.com with the order number and issue details as explained below:

  1. If you ordered (eBook, Video or Print Book) incorrectly or accidentally, please contact Customer Relations Team on customercare@packt.com within one hour of placing the order and we will replace/refund you the item cost.
  2. Sadly, if your eBook or Video file is faulty or a fault occurs during the eBook or Video being made available to you, i.e. during download then you should contact Customer Relations Team within 14 days of purchase on customercare@packt.com who will be able to resolve this issue for you.
  3. You will have a choice of replacement or refund of the problem items.(damaged, defective or incorrect)
  4. Once Customer Care Team confirms that you will be refunded, you should receive the refund within 10 to 12 working days.
  5. If you are only requesting a refund of one book from a multiple order, then we will refund you the appropriate single item.
  6. Where the items were shipped under a free shipping offer, there will be no shipping costs to refund.

On the off chance your printed book arrives damaged, with book material defect, contact our Customer Relation Team on customercare@packt.com within 14 days of receipt of the book with appropriate evidence of damage and we will work with you to secure a replacement copy, if necessary. Please note that each printed book you order from us is individually made by Packt's professional book-printing partner which is on a print-on-demand basis.

What tax is charged?Chevron down iconChevron up icon

Currently, no tax is charged on the purchase of any print book (subject to change based on the laws and regulations). A localized VAT fee is charged only to our European and UK customers on eBooks, Video and subscriptions that they buy. GST is charged to Indian customers for eBooks and video purchases.

What payment methods can I use?Chevron down iconChevron up icon

You can pay with the following card types:

  1. Visa Debit
  2. Visa Credit
  3. MasterCard
  4. PayPal
What is the delivery time and cost of print books?Chevron down iconChevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium:Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela

[8]ページ先頭

©2009-2025 Movatter.jp