How the VIP cycle fully encapsulate the UserDefaults details from the rest of your app

One of my students asked in Slack the other day about how to use UserDefaults properly in a Clean Swift architecture. I’ve seen multiple takes on this question in other blogs. Here’s my take:

Question: I’m a beginner programmer and the CleanStore sample app is still a little complicated for me. Do you have a basic example that takes a number input and saves it to UserDefaults so I can follow the logic of the VIP architecture? I.E One scene sets a few properties of an object in the UserDefaults and another view gets the properties from the UserDefaults store.

Answer: It’s important to understand there is a difference between high-level use case and low-level implementation detail. For example, ‘saving the user ID’ is a high-level use case, whereas ‘saving a number to UserDefaults’ is a low-level implementation detail. You can save the user ID to a file, plist, UserDefaults, or Core Data, …etc. That’s an implementation detail, and it should be able to change without affecting the rest of your app. So we don’t create and name a use case as SaveNumberToNSUserDefaults. Instead, we simply call it Login. When you trigger the use case, you know the user ID is saved for later use. But you don’t care the mechanism by which it is saved. If the mechanism changes to save to a plist, you still trigger the same Login use case. The invocation shouldn’t need to change.

Let’s create a hypothetical scenario. In the Welcome scene, the user is prompted to enter the userID and password. After the user is authenticated, he’ll be shown the Home scene which displays a greeting containing his userID. In this example, we can save the userID to UserDefaults in the Login use case of the Welcome scene. After login, we’ll then retrieve the userID in the ShowGreeting use case of the Home scene.

Here is a sample project to demonstrate a simple login system using the VIP cycle. It saves the userID in UserDefaults.

Get the Clean Swift Xcode Templates

Subscribe below to get my Xcode templates and learn how to apply the VIP cycle to your projects, extract business and presentation logic into interactor and presenter, navigate to different scenes using multiple storyboards, and write fast, maintainable tests with confidence to make changes.

I promise I'll never send you spam. You can unsubscribe at any time.

Testing Asynchronous Operations

How would you go about unit testing your networking calls? They are asynchronous, so the results won’t come back right away. The following is an excerpt of the latest chapter I just added to my book Effective Unit Testing on this exact topic.

All the unit tests that you’ve seen so far are for testing synchronous operations. That is, the outputs can be observed and verified immediately after invoking the method on the test subject. The outputs can be function return values, state changes, or methods invoked on a dependency. All of these happen right away in the same thread. When you write the assertions in the Then phase, you’re guaranteed that the outputs have already been set so that you can safely compare the actual v.s. expected. You don’t have to worry about whether the outputs are ready or not. They are ready.

However, a lot of stuff that we do in modern iOS development are asynchronous operations, such as Core Data, networking, or even some expensive drawing code or events. The results will come, but not right away. It takes time for the task to finish, and the outputs be set. In iOS, these asynchronous operations are usually coded in one of two ways:

  • Completion handler block
  • Delegate method

Asynchronous operations present a challenge to unit testing. In the Then phase, the results may or may not have been set to the outputs for you to observe and verify. When you write your assertions, the test may pass this time (if the outputs have been set), but fail at another time (if the outputs haven’t been set).

The most common asynchronous operation is networking. Fetching data from an API over the network has latency, because data takes time to travel through wires around the globe. There can also be numerous points of failures. The server may be down. Packets can get dropped. Multiplexing can produce errors. Connection may be lost. There are many variables that can result in errors and/or delays. As a result, asynchronous testing necessitates some special handling. Fortunately, Xcode has built-in support to help with that.

As quoted in Apple’s URL Session Programming Guide:

Like most networking APIs, the NSURLSession API is highly asynchronous. If you use the default, system-provided delegate, you must provide a completion handler block that returns data to your app when a transfer finishes successfully or with an error. Alternatively, if you provide your own custom delegate objects, the task objects call those delegates’ methods with data as it is received from the server (or, for file downloads, when the transfer is complete).

Let’s look at completion handler blocks and delegate methods, separately, in more details. We’ll use a networking example that fetches public gists from the GitHub API to illustrate how to write unit tests for asynchronous operations.

Continue reading →

How code isolation helps refactoring

Single responsibility. Separation of concerns. Code isolation. Data encapsulation.

These concepts are so universal. They are easy to accept and take for granted. But do you actually know what they mean? Can you explain them?

I’ll attempt to crystalize these buzzwords/theories/principles by taking a different angle.

There are two types of isolations.

  1. Isolating components using protocols to declare inputs and outputs, and expose only external behaviors.
  2. Isolating features using structs to encapsulate data in separate requests, responses, and view models.

Let’s explore each to understand how clean architecture can facilitate unit testing.

Continue reading →

Why does your app always seem to go awry?

The following story is horrifying, yet very familiar to any professional developer.

Chris the Client: How’s the app coming along? Are we ready to release it?

Dave the Developer: Great! All the features are working perfectly. And I’ve designed these classes to be small and reusable. It’ll be easy to maintain going forward.

Chris the Client: That’s fantastic! Let’s release this version 1 to the App Store.

Dave the Developer: Awesome!

Chris the Client: We’ve got some initial feedback from our users. And we want to add this new feature, and change this to that. How long will it take?

Dave the Developer: Hmm… If we’re going to do that, I’ll need to refactor this part of the app. It’ll take maybe a week.

Chris the Client: Why? I thought you said it would be easy to maintain.

Dave the Developer: Yeah, but your proposed changes will dramatically alter the way how things are working now. It’s not that simple.

Chris the Client: Okay, do whatever it takes. We want to get this out the door as soon as possible.

Dave the Developer: I’ll do my best.

Chris the Client: Some of our users are seeing crashes when they do this. Can you fix it?

Dave the Developer: Let me take a look… Ah, I found where the problem is. It’ll take a couple days to look into fixing it.

Chris the Client: Thanks.

Dave the Developer: This bug is really tricky. But it has been fixed. And I’ve released 1.1 to the App Store.

Chris the Client: Our users are still seeing crashes when they do this. I thought you said it was fixed!?

Dave the Developer: Yes, it was fixed. I’m not sure why it’s still happening. I can take a look.

Chris the Client: Yes, please do.

Continue reading →

Unit Testing and TDD Terminology

This is part 2 of this terminology series:

This method is difficult to test.

How do I mock this class? With a stub or mock?

Should I test private methods?

Testing in the iOS world has been gaining more attention for the last several years. However, it is still in the minority. There’re still many who are just getting their feet wet. If you’re also wondering about these questions, I’ll demystify these vocabularies for you, and give you an overview of what unit testing is, its process, and practice.

Continue reading →