Table Of ContentCombine
Combine: Asynchronous Programming with
Swift
By Scott Gardner, Shai Mishali, Florent Pillet & Marin Todorov
Copyright ©2019 Razeware LLC.
Notice of Rights
All rights reserved. No part of this book or corresponding materials (such as text,
images, or source code) may be reproduced or distributed by any means without prior
written permission of the copyright owner.
Notice of Liability
This book and all corresponding materials (such as source code) are provided on an
“as is” basis, without warranty of any kind, express of implied, including but not
limited to the warranties of merchantability, fitness for a particular purpose, and
noninfringement. In no event shall the authors or copyright holders be liable for any
claim, damages or other liability, whether in action of contract, tort or otherwise,
arising from, out of or in connection with the software or the use of other dealing in
the software.
Trademarks
All trademarks and registered trademarks appearing in this book are the property of
their own respective owners.
raywenderlich.com 2
Combine
Table of Contents: Overview
About the Cover...................................................................................... 12
What You Need........................................................................................ 16
Book License............................................................................................. 17
Book Source Code & Forums............................................................. 18
Section I: Introduction to Combine.............................. 20
Chapter 1: Hello, Combine!.................................................... 21
Chapter 2: Publishers & Subscribers.................................. 37
Section II: Operators......................................................... 68
Chapter 3: Transforming Operators................................... 70
Chapter 4: Filtering Operators............................................. 90
Chapter 5: Combining Operators...................................... 113
Chapter 6: Time Manipulation Operators..................... 139
Chapter 7: Sequence Operators........................................ 166
Chapter 8: In Practice: Project "Collage"........................ 188
Section III: Combine in Action..................................... 212
Chapter 9: Networking.......................................................... 214
Chapter 10: Debugging......................................................... 220
Chapter 11: Timers................................................................. 225
Chapter 12: Key-Value Observing.................................... 230
Chapter 13: Resource Management................................ 237
Chapter 14: In Practice: Project "News"........................ 244
raywenderlich.com 3
Combine
Section IV: Advanced Combine................................... 263
Chapter 15: In Practice: Combine & SwiftUI................ 265
Chapter 16: Error Handling................................................. 295
Chapter 17: Schedulers......................................................... 322
Chapter 18: Custom Publishers & Handling
Backpressure............................................................................. 346
Chapter 19: Testing................................................................. 382
Section V: Building a Complete App.......................... 404
Chapter 20: In Practice: Building a Complete App..... 405
Conclusion.............................................................................................. 436
raywenderlich.com 4
Combine
Table of Contents: Extended
About the Cover . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
About the Authors. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
About the Artist. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
What You Need . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Book License. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Book Source Code & Forums . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Section I: Introduction to Combine . . . . . . . . . . . . . . . . 20
Chapter 1: Hello, Combine!. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Asynchronous programming. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Foundation of Combine. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Combine basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
What's the benefit of Combine code over "standard" code? . . . . . . . . . 32
App architecture. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
Book projects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
Key points. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Chapter 2: Publishers & Subscribers. . . . . . . . . . . . . . . . . . . . . . . . . 37
Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Hello Publisher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
Hello Subscriber . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Hello Cancellable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Understanding what’s going on. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
Creating a custom subscriber. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
Hello Future . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
Hello Subject. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Dynamically adjusting demand . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
Type erasure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
raywenderlich.com 5
Combine
Challenge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
Key points. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
Section II: Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Chapter 3: Transforming Operators . . . . . . . . . . . . . . . . . . . . . . . . . 70
Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
Collecting values. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
Mapping values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
Flattening publishers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
Replacing upstream output. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
Incrementally transforming output. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
Challenge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Key points. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
Chapter 4: Filtering Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
Getting started . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
Filtering basics. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
Compacting and ignoring. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
Finding values. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
Dropping values. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
Limiting values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
Challenge. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
Chapter 5: Combining Operators . . . . . . . . . . . . . . . . . . . . . . . . . . 113
Getting started. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
Prepending. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
Appending. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
Advanced combining . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
raywenderlich.com 6
Combine
Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
Chapter 6: Time Manipulation Operators. . . . . . . . . . . . . . . . . . 139
Getting started. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
Shifting time. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
Collecting values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
Collecting values (part 2). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
Holding off on events. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
Timing out. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
Measuring time. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
Challenge. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
Chapter 7: Sequence Operators. . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
Getting started. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
Finding values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
Querying the publisher. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
Chapter 8: In Practice: Project "Collage". . . . . . . . . . . . . . . . . . . 188
Getting started with "Collage" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
Talking to other view controllers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
Wrapping a callback function as a future. . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
Presenting a view controller as a future. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
Sharing subscriptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
Publishing properties with @Published . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
Operators in practice. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
Challenges. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
Section III: Combine in Action . . . . . . . . . . . . . . . . . . . . 212
raywenderlich.com 7
Combine
Chapter 9: Networking. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
URLSession extensions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
Codable support. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
Publishing network data to multiple subscribers . . . . . . . . . . . . . . . . . . . 216
Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
Chapter 10: Debugging. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
Printing events . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
Acting on events — performing side effects. . . . . . . . . . . . . . . . . . . . . . . . . 222
Using the debugger as a last resort. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
Chapter 11: Timers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
Using RunLoop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
Using the Timer class. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
Using DispatchQueue. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
Chapter 12: Key-Value Observing. . . . . . . . . . . . . . . . . . . . . . . . . . 230
Introducing publisher(for:options:) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
Preparing and subscribing to your own KVO-compliant properties 231
ObservableObject. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
Chapter 13: Resource Management . . . . . . . . . . . . . . . . . . . . . . . 237
The share() operator. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
The multicast(_:) operator. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
Future . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
raywenderlich.com 8
Combine
Chapter 14: In Practice: Project "News" . . . . . . . . . . . . . . . . . . . 244
Getting started with the Hacker News API . . . . . . . . . . . . . . . . . . . . . . . . . 245
Getting a single story. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
Multiple stories via merging publishers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
Getting the latest stories. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
Challenges. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
Section IV: Advanced Combine . . . . . . . . . . . . . . . . . . . 263
Chapter 15: In Practice: Combine & SwiftUI . . . . . . . . . . . . . . . 265
Hello, SwiftUI!. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 268
Getting started with "News" . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
A first taste of managing view state. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
Fetching the latest stories. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
Using ObservableObject for model types. . . . . . . . . . . . . . . . . . . . . . . . . . . 277
Displaying errors. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
Subscribing to an external publisher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
Initializing the app's settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
Editing the keywords list . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
Challenges. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
Chapter 16: Error Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
Getting started. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
Never. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
Dealing with failure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303
Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
Chapter 17: Schedulers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
An introduction to schedulers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
raywenderlich.com 9
Combine
Operators for scheduling. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324
Scheduler implementations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
Challenges. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
Chapter 18: Custom Publishers & Handling Backpressure 346
Creating your own publishers. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
Publishers as extension methods. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
The subscription mechanism. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
Publishers emitting values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
Publishers transforming values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359
Handling backpressure. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374
Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380
Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381
Chapter 19: Testing. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382
Getting started. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 383
Testing Combine operators. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384
Testing production code. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393
Challenges. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400
Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
Where to go from here?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
Section V: Building a Complete App . . . . . . . . . . . . . . 404
Chapter 20: In Practice: Building a Complete App . . . . . . . . . 405
Getting started. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 406
Setting goals. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 408
Implementing JokesViewModel. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 409
Wiring JokesViewModel up to the UI. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418
Implementing Core Data with Combine . . . . . . . . . . . . . . . . . . . . . . . . . . . . 424
Challenge. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
Key points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434
raywenderlich.com 10