Modern SwiftUI: Dependencies & Testing, Part 1

Episode #219 • Jan 9, 2023 • Subscriber-Only

Uncontrolled dependencies can wreak havoc on a modern SwiftUI code base. Let’s explore why, and how we can begin to control them using a brand new library.

Dependencies & Testing, Part 1
Introduction
00:05
Uncontrolled dependencies
02:07
Controlling dependencies
13:15
Importing Dependencies
22:54
Next time: custom dependencies
31:16

Unlock This Episode

Our Free plan includes 1 subscriber-only episode of your choice, plus weekly updates from our newsletter.

Introduction

Alright, so we have now introduced 3 pretty complex effects into our application. We are dealing with timers that can be paused while alerts are up, we’re requesting speech authorization from the user and starting up a speech recognition task in parallel with the timer, and on top of all of that we are listening to any changes made to the array of standups, debouncing them for a second, and then persisting the data. Oh, and we also load that data on launch.

Without these effects our little demo was nothing more than a “cute” toy. Sure we had some fun interactions like sheets, drill downs and alerts, but everything was implemented with just simple state mutation. There was no interaction with the outside world. These effects have added a whole new dimension of behavior to the demo and turned it into a full blown application.

But with that new behavior comes new challenges. We have opened up Pandora’s box of complexity and unknowability in our codebase. We already saw this in concrete terms where we saw we have effectively broken the “record meeting” preview due to the fact that it interacts with Apple’s Speech framework directly, which does not work in Xcode previews. And we saw that when we added persistence we destroyed our ability to open up the application or preview into a state with a bunch of standups stubbed in because now that data has to come from the disk.

And if those problems weren’t bad enough, we also don’t have any hope of writing unit tests for any of those code. The Speech framework doesn’t work at all in unit tests, and because we have a real life timer we are going to have to wait for real life time in our tests, which will slow down the tests. And because we are reading and writing to the real disk we are going to have to be careful to clean up after tests, or else that data will start to leak across tests, causing mystifying test failures.

This is what motivates us to finally consider properly controlling our dependencies on things like timers, the Speech framework, and disk access. Doing so allows us to fix all of these problems and more.

So, let’s quickly look at all the problems that crop up when dealing with uncontrolled dependencies, and then let’s fix them.

This episode is for subscribers only.

Subscribe to Point-Free

Access this episode, plus all past and future episodes when you become a subscriber.

See plans and pricing

Already a subscriber? Log in

References

Getting started with Scrumdinger

Apple

Learn the essentials of iOS app development by building a fully functional app using SwiftUI.

Standups App

Brandon Williams & Stephen Celis

A rebuild of Apple’s “Scrumdinger” application that demosntrates how to build a complex, real world application that deals with many forms of navigation (e.g., sheets, drill-downs, alerts), many side effects (timers, speech recognizer, data persistence), and do so in a way that is testable and modular.

Dependencies

Brandon Williams & Stephen Celis • Sunday Jan 9, 2022

An open source library of ours. A dependency management library inspired by SwiftUI’s “environment.”

Getting started with Scrumdinger

Apple

Learn the essentials of iOS app development by building a fully functional app using SwiftUI.

Clocks

Brandon Williams & Stephen Celis • Wednesday Jun 29, 2022

An open source library of ours. A few clocks that make working with Swift concurrency more testable and more versatile.

SE-0374: Add `sleep(for:)` to `Clock`

Brandon Williams & Stephen Celis • Monday Sep 19, 2022

A Swift Evolution proposal from yours truly that introduced a sleep(for:) method to Clock, making it possible for clock existentials to sleep.

Packages authored by Point-Free

Swift Package Index

These packages are available as a package collection, usable in Xcode 13 or the Swift Package Manager 5.5.

Downloads