Chapters

Hide chapters

iOS Test-Driven Development by Tutorials

Second Edition · iOS 15 · Swift 5.5 · Xcode 13

13. Breaking Up Dependencies
Written by Michael Katz

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

It’s always safer to make a change when you have tests in place already. In the absence of existing tests, however, you may need to make changes just to add tests! One of the most common reasons for this is tightly-coupled dependencies: You can’t add tests to a class because it depends on other classes that depend on other classes… View controllers especially are often victims of this issue.

By creating a dependency map in the last chapter, you were able to find where you want to make changes and, in turn, where you really need to have tests.

This chapter will teach you how to break dependencies safely to add tests around where you want to change.

Getting started

As a reminder, in this chapter, you will build upon and improve the MyBiz app. The powers that be want to build a separate expense reporting app. In the interest of DRY (Don’t Repeat Yourself) they want to reuse the login view from your app in the new app. The best way to do that is to pull the login functionality into its own framework so it can be reused across projects.

The login view controller is the obvious place to start because it presents the login UI and uses all of the other code related to login. In the previous chapter, you built out a dependency map for the login view controller and identified some change points. You’ll use that map as a guide to break up the dependencies so login can stand alone.

Skin UIViewController +Alert Error ViewController AppDelegate Models Event Employee Announcement Product PurchaseOrder Configuration APIDelegate Token Validators Login ViewController API

Characterizing the system

Before moving any code, you want to make sure that the refactors won’t disturb the behavior of the app. To do that, start with a characterization test for the signIn(_:) function of LoginViewController. This is the main entry point for signing into the app and it’s crucial that it continues to work.

Breaking up the API/AppDelegate dependency

Now that there are some tests in place, it’s time to start breaking up the dependencies so you can move the code. Starting with the API <-> AppDelegate interdependency will make it easier to break up those classes from LoginViewController later.

Using a notification for communication

The next step is to fix the logout dependency. This method calls back to app delegate, but handling the post-logout state shouldn’t really live with an app delegate. You’ll use a Notification to pass the event in a general way. You won’t fix AppDelegate this time around, but you will make API ignorant of which class cares about it.

Reflecting on the breakup

This exercise illustrated two ways for detangling two objects:

Nsog IIGoafNicfniykad +Ujofz Ukrur CuoqYovyjeldow OctQahavoxi Fetijx Ijanq Oqtpunue Okdiezcikedy Qfuviks WolzlomoIrrec Qofqoqudimeev IFUFuqaceyu Buvum Xekabijutm Tuvoj NuotFezcjotxac OQA

Breaking the AppDelegate dependency

The next stop on the dependency-detangling train is removing AppDelegate from LoginViewController.

Injecting the API

In LoginViewController.swift, change the api variable to:

Detangling login success

If you look at loginSucceeded(userId:) on the LoginViewController, you’ll see that none of its contents really belong in the view controller — all of the work happens on the AppDelegate! The issue then becomes how to indirectly link the API action to a consequence in the AppDelegate. Well… last time you used a Notification and you can do so again.

Rpom UESoenJixrmiqxuz +Awafl Iqdej RiojJurwpodqod IpvWazomuna Yixopw Agehn Ayckiroe Ircaiylumipz Nmonokn YewwzasuUfden Bamciwoqisias UYIGakugape Mitoq Xikujavabm Ciriz WaazPihjlufyul IYE

Breaking the ErrorViewController dependency

Looking at the dependency map for red lines, it next makes sense to tackle the dependency on LoginViewController from ErrorViewController.

Removing login from error handling

Now that you’ve got the base behavior covered, you’re ready to go ahead and start breaking out the dependency. ErrorViewController has a “try again” function that calls back into the LoginViewController. This not only violates SOLID principles but it’s cumbersome to add this try again functionality to other screens since you’ll need to add to several switch statements and further tie in dependencies.

Btiy OEMuiqWefmvefnan +Eqoqp Izyal CaehRinljolvud EhqTuhogite Jamulx Aduws Ebdcijae Otjeivtiyihr Syexatf SugxkibaImkiv Qatjutapizues EBAKapizula Nayud Fevoxarigz Pumoq JaamVuygvagqil APU

Challenge

This chapter’s challenge is a simple one. You may have noticed that input validation was left out of the LoginViewControllerTests characterization tests. Your challenge is to add them now, so you will have a more robust test suite before moving the code into its own module in the next chapter. For an additional challenge, add unit tests for the Validators functions in MyBizTests.

Key Points

  • Dependency Maps are your guide to breaking dependencies.
  • Break up bad dependencies one at a time, using techniques like dependency inversion, command patterns, notifications and configuring objects from the outside.
  • Write tests before, during and after a large refactor.

Where to go from here?

Go to the next chapter to continue this refactoring project to break up dependencies. In that chapter, you’ll create a new framework so that Login can live in its own, reusable module.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now