Combine 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