The Clean Swift Handbook is an updated version of this blog post, plus additional chapters to cover the entire CleanStore sample app. It also works with the latest versions of the Clean Swift templates, Xcode, and Swift. Check out the handbook for even more bonus materials.

Your client asks for an estimate to fix a bug. You tell him maybe a couple hours. But you know deep down in your gut that you just aren’t sure. You just have to give a number.

If it takes longer than your guesstimate, it comes back to bite you. “I thought you said it would be done in a couple hours.”

Promise broken. Trust lost. Resentment developed. Client fired. Money lost.

To make matters worse, as developers, we have a tendency to underestimate how long something will take.

Maybe the problem lies in the client? They just don’t understand how development works.

They don’t know how complicated that thing is.

They need to stop changing requirements.

They should focus on functions, not UI first.

If only they had asked me to do that 2 months ago while the code was still fresh, it would have taken less time.

Add your own favorite lines here

You have to understand the code. Navigate through 50 classes, protocols, and methods. Trace through various conditionals and loops. Identify the relevant line. Reproduce the bug. Make a change to see how it behaves differently. Rinse and repeat until you fix the bug.

While doing all of these, you fear you may break something else. Because there is no unit tests to prevent regression.

This same vicious cycle happens every time you need to fix a bug or add a new feature. Thanks to Massive View Controller.

Failed Attempt at Solving the MVC Problem

If this sounds familiar to you, you may have already attempted to fix the situation. You read a lot about design patterns and how to refactor your view controllers. Extract your data sources and delegates. Put your business logic in the models. How that is supposed to help you write unit tests.

But you are still not doing it. It is 2015. Swift hits 2.0 and iOS 9 is released next month.

So far, the things you read and tried are what I call the first aid kit. Damage has already been done. You are just putting bandages on your wounds. You are treating the symptoms, not attacking the root cause.

Can we prevent the damage in the first place, instead of treating the wounds afterward? Why does it have a “re” in refactoring? Can we just write factored code from the start so we never have to refactor?

The Root Cause

About 2 years ago, I decided to do something serious about it. I want to find out why we are still battling massive view controllers. Nonetheless, refactoring and testing are generating all the buzz these days. Is it really possible to write factored code and never have to refactor?

Enter architecture. If a building has a shaky foundation, it eventually topples. If your codebase has a shaky architecture, it’ll eat you alive. Until you have enough. Let’s just rewrite it. We all know how expensive a rewrite is.

I’ve researched on various iOS architectures out there such as MVC, MVVM, ReactiveCocoa, and VIPER. I’ve also experimented with different testing and mocking frameworks. I’ll write more about these topics in future posts. In order to help you today, I want to focus on how to apply Uncle Bob’s Clean Architecture to iOS development using Swift. I’ll just call this Clean Swift. After reading this post, you’ll learn how to:

Find and fix bugs faster and easier.

Change existing behaviors with confidence.

Add new features easily.

Write shorter methods with single responsibility.

Decouple class dependencies with established boundaries.

Extract business logic from view controllers into interactors.

Build reusable components with workers and service objects.

Write factored code from the start.

Write fast and maintainable unit tests.

Have confidence in your tests to catch regression.

Apply what you learn to new and existing projects of any size.

What if you know exactly which file to open and which method to look? How much more productive can you be? Imagine your tests run in seconds instead of minutes or hours. And you don’t need to learn any testing or mocking frameworks. No CocoaPods to install.

You just follow a simple system and your code just logically falls into place. That’s how you know exactly where to look when you look back at your code 3 months later.

In order to make it even easier for you to get started, I created a set of Xcode templates. You can generate the Clean Swift components with just Xcode’s New File command. When you subscribe to my list below, you’ll receive the templates in your inbox and updates. I’ll also send you future posts on leveling up your architecture. We’ll examine the details of interactors, workers, and service objects, look at how routing works with multiple storyboards (yes, it works like a charm with multiple storyboards). Subscribe below and you’ll receive all these goodies.

Make sure to also read about the latest updates to the templates for Xcode 8.3.3 & 9.0 and Swift 3 & 4.

Introducing Clean Swift – Clean Architecture for iOS

The Clean Swift architecture is derived from the Clean Architecture proposed by Uncle Bob. They share many common concepts such as the components, boundaries, and models. I’m going to implement the same create order use case in one of Uncle Bob’s talks. While Uncle Bob demonstrates the Clean Architecture using Java in web applications, I’ll show you how to apply Clean Architecture using Swift in iOS projects.

Before we begin, make sure you subscribe to my list in the form above to get my Xcode templates. Why bother writing all the boilerplate code by hand when you can click a button to generate them?

If you aren’t ready for Swift yet, I also have an Objective-C version of my Xcode templates and am looking for people to test it with more project types. If this is you, please email me after you subscribe. I’ll send you the Objective-C version. Your feedback will help guide the design of the components as there are differences in the languages.

The “Create Order” Use Case

This use case was presented in Uncle Bob’s Why can’t anyone get Web architecture right? talk. It originates from Ivar Jacobson’s book Object Oriented Software Engineering: A Use Case Driven Approach. It is a very good example as it encompasses most of the features of Clean Swift except routing. I’ll write a post on all the details on routing later.

Data:

– Customer-id

– Customer-contact-info

– Shipment-destination

– Shipment-mechanism

– Payment-information

Primary Course:

1. Order clerk issues “Create Order” command with above data.

2. System validates all data.

3. System creates order and determines order-id.

4. System delivers order-id to clerk.

Exception Course: Validation Error

1. System delivers error message to clerk.

We’ll model the data in the use case in our model layer and create special request, response, and view models to pass around at the boundaries between the view controller, interactor, and presenter components. We’ll implement each of the primary course item and validation as business logic in the interactor and, if necessary, workers.

To avoid getting lost, let’s first look at how we organize our code in the Xcode project.

Organizing Your Code in Xcode

We’ll create a new Xcode project and name it CleanStore. Just choose Single View Application and iPhone only for simplicity’s sake. Make sure the language is set to Swift. Next, create a nested sub-group Scenes -> CreateOrder. When we implement a delete order use case in the future, we’ll create a new sub-group Scenes -> DeleteOrder.

In a typical Xcode project, it is common to see files organized into model, view, and controller groups. But every iOS developer knows MVC. It doesn’t tell you anything specific about the project. As Uncle Bob pointed out, group and file names should reveal your intentions for the use cases. It should not reflect the underlying framework structure. So we’ll organize each use case under a new group nested within Scenes.

Inside the CreateOrder group, you can expect all files have something to do with creating an order. Likewise, under the DeleteOrder group, you’ll find code that deals with deleting an order. If you see a new ViewOrderHistory group created by another developer, you already know what to expect.

This organization tells you far more than the model, view, and controller groups you are used to see. Over time, you’ll accumulate 15 models, 27 view controllers, and 17 views. What do they do? You simply don’t know before you inspect each file.

You may ask. What about the shared classes and protocols used by CreateOrder, DeleteOrder and ViewOrderHistory? Well, you can put them in a separate group called Common -> Order. How about that for simplicity?

Back to our use case.

Under the new CreateOrder group, we’ll create the following Clean Swift components. As we work through the use case, we’ll add methods to the component’s input and output protocols and then implement them in the components. Make sure you join my email list, so you can use my Xcode templates to create all these components automatically for you with a few clicks.

Watch this screencast now to see how to generate the seven Clean Swift components using the Xcode templates to save a lot of time!

You can now compile your project and start building your UI in storyboard. But let’s briefly examine the components you’ve just created first.

The VIP Cycle

The view controller, interactor, and presenter are the three main components of Clean Swift. They act as input and output to one another as shown in the following diagram.

The view controller’s output connects to the interactor’s input. The interactor’s output connects to the presenter’s input. The presenter’s output connects to the view controller’s input. We’ll create special objects to pass data through the boundaries between the components. This allows us to decouple the underlying data models from the components. These special objects consists of only primitive types such as Int, Double, and String. We can create structs, classes, or enums to represent the data but there should only be primitive types inside these containing entities.

This is important because when the business rules change that result in changes in the underlying data models. We don’t need to update all over the codebase. The components act as plugins in Clean Swift. That means we can swap in different components provided they conform to the input and output protocols. The app still works as intended.

A typical scenario goes like. The user taps a button in the app’s user interface. The tap gesture comes in through the IBActions in the view controller. The view controller constructs a request object and sends it to the interactor. The interactor takes the request object and performs some work. It then puts the results in a response object and sends it to the presenter. The presenter takes the response object and formats the results. It then puts the formatted result in a view model object and sends it back to the view controller. Finally, the view controller displays the results to the user.

1. View Controller

What should a view controller do in an iOS app? The base class name UITableViewController should tell you something. You want to put code there to control UITableView and UIView subclasses. But what does this control code look like? What qualifies as control code and what doesn’t?

Let’s dive in.

import UIKit protocol CreateOrderViewControllerInput { func displaySomething(viewModel: CreateOrderViewModel) } protocol CreateOrderViewControllerOutput { func doSomething(request: CreateOrderRequest) } class CreateOrderViewController: UITableViewController, CreateOrderViewControllerInput { var output: CreateOrderViewControllerOutput! var router: CreateOrderRouter! // MARK: Object lifecycle override func awakeFromNib() { super.awakeFromNib() CreateOrderConfigurator.sharedInstance.configure(self) } // MARK: View lifecycle override func viewDidLoad() { super.viewDidLoad() doSomethingOnLoad() } // MARK: Event handling func doSomethingOnLoad() { // NOTE: Ask the Interactor to do some work let request = CreateOrderRequest() output.doSomething(request) } // MARK: Display logic func displaySomething(viewModel: CreateOrderViewModel) { // NOTE: Display the result from the Presenter // nameTextField.text = viewModel.name } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 import UIKit protocol CreateOrderViewControllerInput { func displaySomething ( viewModel : CreateOrderViewModel ) } protocol CreateOrderViewControllerOutput { func doSomething ( request : CreateOrderRequest ) } class CreateOrderViewController : UITableViewController , CreateOrderViewControllerInput { var output : CreateOrderViewControllerOutput ! var router : CreateOrderRouter ! // MARK: Object lifecycle override func awakeFromNib ( ) { super . awakeFromNib ( ) CreateOrderConfigurator . sharedInstance . configure ( self ) } // MARK: View lifecycle override func viewDidLoad ( ) { super . viewDidLoad ( ) doSomethingOnLoad ( ) } // MARK: Event handling func doSomethingOnLoad ( ) { // NOTE: Ask the Interactor to do some work let request = CreateOrderRequest ( ) output . doSomething ( request ) } // MARK: Display logic func displaySomething ( viewModel : CreateOrderViewModel ) { // NOTE: Display the result from the Presenter // nameTextField.text = viewModel.name } }

CreateOrderViewControllerInput and CreateOrderViewControllerOutput protocols

The CreateOrderViewControllerInput protocol specifies the inputs to the CreateOrderViewController component (it conforms to the protocol). The CreateOrderViewControllerOutput protocol specifies the outputs. You’ll see this same pattern in the interactor and presenter later.

There is one method doSomething() in the output protocol. If another component wants to act as the output of CreateOrderViewController , it needs to support doSomething() in its input.

From the VIP cycle you saw earlier, we know this output is going to be the interactor. But notice in CreateOrderViewController.swift , there is no mention of CreateOrderInteractor . This means we can swap in another component to be the output of CreateOrderViewController as long as it supports doSomething() in its input protocol.

The argument to doSomething() is a request object that is passed through the boundary from the view controller to the interactor. This request object is a CreateOrderRequest struct. It consists of primitive types, not the whole order data that we identified earlier. This means we have decoupled the underlying order data model from the view controller and interactor. When we make changes to the order data model in the future (for example, add an internal order ID field), we don’t need to update anything in the Clean Swift components.

I’ll come back to the displaySomething() method in the output protocol later when we finish the VIP cycle.

output and router variables

The output variable is an object that conforms to the CreateOrderViewControllerOutput protocol. Although we know it is going to be the interactor, but it doesn’t need to be.

The router variable is a reference to CreateOrderRouter and is used to navigate to different scenes.

configure() method

We’ll make a call to CreateOrderConfigurator.sharedInstance.configure(self) in awakeFromNib() to ask the configurator to set up the VIP chain. There is no configurator in Uncle Bob’s Clean Architecture or VIPER (which does all the setup in the app delegate). I don’t really want to litter my VIP code with this extraneous setup code, so I extracted it to the configurator. We’ll take a look at the configurator later.

Flow of control

In viewDidLoad() , we have some business logic to run, so we call doSomethingOnLoad() . In doSomethingOnLoad() , we create a CreateOrderRequest() object and invoke doSomething(request) on the output (the interactor). That’s it. We ask the output to perform our business logic. The view controller doesn’t and shouldn’t care who and how it is done.

2. Interactor

The interactor contains your app’s business logic. The user taps and swipes in your UI in order to interact with your app. The view controller collects the user inputs from the UI and passes it to the interactor. It then retrieves some models and asks some workers to do the work.

import UIKit protocol CreateOrderInteractorInput { func doSomething(request: CreateOrderRequest) } protocol CreateOrderInteractorOutput { func presentSomething(response: CreateOrderResponse) } class CreateOrderInteractor: CreateOrderInteractorInput { var output: CreateOrderInteractorOutput! var worker: CreateOrderWorker! // MARK: Business logic func doSomething(request: CreateOrderRequest) { // NOTE: Create some Worker to do the work worker = CreateOrderWorker() worker.doSomeWork() // NOTE: Pass the result to the Presenter let response = CreateOrderResponse() output.presentSomething(response) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import UIKit protocol CreateOrderInteractorInput { func doSomething ( request : CreateOrderRequest ) } protocol CreateOrderInteractorOutput { func presentSomething ( response : CreateOrderResponse ) } class CreateOrderInteractor : CreateOrderInteractorInput { var output : CreateOrderInteractorOutput ! var worker : CreateOrderWorker ! // MARK: Business logic func doSomething ( request : CreateOrderRequest ) { // NOTE: Create some Worker to do the work worker = CreateOrderWorker ( ) worker . doSomeWork ( ) // NOTE: Pass the result to the Presenter let response = CreateOrderResponse ( ) output . presentSomething ( response ) } }

CreateOrderInteractorInput and CreateOrderInteractorOutput protocols

The CreateOrderInteractorInput protocol specifies the inputs to the CreateOrderInteractor component (it conforms to the protocol). The CreateOrderInteractorOutput protocol specifies the outputs.

We see the same doSomething() method here in the CreateOrderInteractorInput protocol as in the CreateOrderViewControllerOutput protocol. The output of CreateOrderViewController is connected to the input of the CreateOrderInteractor .

The output protocol has one method presentSomething() . The output of CreateOrderInteractor needs to support presentSomething() in order to act as the output. Hint: The output is going to be the P in VIP.

Another thing to note here is the argument to doSomething() is the request object of type CreateOrderRequest . This is the same method signature in the CreateOrderViewControllerOutput protocol. The interactor peeks inside this request object to retrieve any necessary data to do its job.

Similarly, the argument to presentSomething() is the response object of type CreateOrderResponse.

output and worker variables

The output variable is an object that conforms to the CreateOrderInteractorOutput protocol. Although we know it is going to be the presenter, but it doesn’t need to be.

The worker variable of type CreateOrderWorker is a specialized object that will actually create the new order. Since creating the order likely involves persistence in Core Data and making network calls. It is simply too much work for the interactor to do alone. Keep in mind the interactor also has to validate the order form and this is likely going to be extracted into its own worker.

Flow of control

When the input to CreateOrderInteractor (i.e. CreateOrderViewController ) invokes doSomething() , it first creates the worker object and asks it to do some work by calling doSomeWork() . It then constructs a response object and invokes presentSomething() on the output.

Let’s take a quick look at the worker next.

3. Worker

A profile view may need to fetch the user from Core Data, download the profile photo, allows users to like and follow, …etc. You don’t want to swamp the interactor with doing all these tasks. Instead, you can break it down into many workers, each doing one thing. You can then reuse the same workers elsewhere.

The CreateOrderWorker is very simple as it just provides an interface and implementation of the work it can do to the interactor.

import UIKit class CreateOrderWorker { // MARK: Business Logic func doSomeWork() { // NOTE: Do the work } } 1 2 3 4 5 6 7 8 9 10 11 12 import UIKit class CreateOrderWorker { // MARK: Business Logic func doSomeWork ( ) { // NOTE: Do the work } }

4. Presenter

After the interactor produces some results, it passes the response to the presenter. The presenter then marshal the response into view models suitable for display. It then passes the view models back to the view controller for display to the user.

import UIKit protocol CreateOrderPresenterInput { func presentSomething(response: CreateOrderResponse) } protocol CreateOrderPresenterOutput: class { func displaySomething(viewModel: CreateOrderViewModel) } class CreateOrderPresenter: CreateOrderPresenterInput { weak var output: CreateOrderPresenterOutput! // MARK: Presentation logic func presentSomething(response: CreateOrderResponse) { // NOTE: Format the response from the Interactor and pass the result back to the View Controller let viewModel = CreateOrderViewModel() output.displaySomething(viewModel) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import UIKit protocol CreateOrderPresenterInput { func presentSomething ( response : CreateOrderResponse ) } protocol CreateOrderPresenterOutput : class { func displaySomething ( viewModel : CreateOrderViewModel ) } class CreateOrderPresenter : CreateOrderPresenterInput { weak var output : CreateOrderPresenterOutput ! // MARK: Presentation logic func presentSomething ( response : CreateOrderResponse ) { // NOTE: Format the response from the Interactor and pass the result back to the View Controller let viewModel = CreateOrderViewModel ( ) output . displaySomething ( viewModel ) } }

CreateOrderPresenterInput and CreateOrderPresenterOutput protocols

The CreateOrderPresenterInput protocol specifies the inputs to the CreateOrderPresenter component (it conforms to the protocol). The CreateOrderPresenterOutput protocol specifies the outputs.

By now, the presentSomething() and displaySomething() methods don’t need to be explained. The CreateOrderResponse argument is passed through the interactor-presenter boundary whereas the CreateOrderViewModel argument is passed through the presenter-view controller boundary as the VIP cycle completes.

output variable

The output variable is an object that conforms to the CreateOrderPresenterOutput protocol. Although we know it is going to be the view controller, but it doesn’t need to be. A subtle difference here is we make output a weak variable to avoid a reference cycle when this CreateOrder scene is no longer needed and the components are deallocated.

Flow of control

Since the output of CreateOrderInteractor is connected to the input of CreateOrderPresenter , the presentSomething() method will be called after the interactor finishes doing its work. It simply constructs the view model object and invokes displaySomething() on the output.

I promised you that we would come back to the displaySomething() method in the view controller. This is the last step in the VIP cycle. It takes any data in the view model object and displays it to the user. For example, we may want to display the customer’s name in a text field: nameTextField.text = viewModel.name .

Congratulations! You just learned the essence of Clean Swift. You should now be able to extract business and presentation logic from your user interface code. But don’t worry. I won’t leave you without an example. But let’s finish talking about the rest of the Clean Swift components first.

5. Router

When the user taps the next button to navigate to the next scene in the storyboard, a segue is trigged and a new view controller is presented. A router extracts this navigation logic out of the view controller. It is also the best place to pass any data to the next scene. As a result, the view controller is left with just the task of controlling views.

import UIKit protocol CreateOrderRouterInput { func navigateToSomewhere() } class CreateOrderRouter { weak var viewController: CreateOrderViewController! // MARK: Navigation func navigateToSomewhere() { // NOTE: Teach the router how to navigate to another scene. Some examples follow: // 1. Trigger a storyboard segue // viewController.performSegueWithIdentifier("ShowSomewhereScene", sender: nil) // 2. Present another view controller programmatically // viewController.presentViewController(someWhereViewController, animated: true, completion: nil) // 3. Ask the navigation controller to push another view controller onto the stack // viewController.navigationController?.pushViewController(someWhereViewController, animated: true) // 4. Present a view controller from a different storyboard // let storyboard = UIStoryboard(name: "OtherThanMain", bundle: nil) // let someWhereViewController = storyboard.instantiateInitialViewController() as! SomeWhereViewController // viewController.navigationController?.pushViewController(someWhereViewController, animated: true) } // MARK: Communication func passDataToNextScene(segue: UIStoryboardSegue) { // NOTE: Teach the router which scenes it can communicate with if segue.identifier == "ShowSomewhereScene" { passDataToSomewhereScene(segue) } } func passDataToSomewhereScene(segue: UIStoryboardSegue) { // NOTE: Teach the router how to pass data to the next scene // let someWhereViewController = segue.destinationViewController as! SomeWhereViewController // someWhereViewController.output.name = viewController.output.name } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 import UIKit protocol CreateOrderRouterInput { func navigateToSomewhere ( ) } class CreateOrderRouter { weak var viewController : CreateOrderViewController ! // MARK: Navigation func navigateToSomewhere ( ) { // NOTE: Teach the router how to navigate to another scene. Some examples follow: // 1. Trigger a storyboard segue // viewController.performSegueWithIdentifier("ShowSomewhereScene", sender: nil) // 2. Present another view controller programmatically // viewController.presentViewController(someWhereViewController, animated: true, completion: nil) // 3. Ask the navigation controller to push another view controller onto the stack // viewController.navigationController?.pushViewController(someWhereViewController, animated: true) // 4. Present a view controller from a different storyboard // let storyboard = UIStoryboard(name: "OtherThanMain", bundle: nil) // let someWhereViewController = storyboard.instantiateInitialViewController() as! SomeWhereViewController // viewController.navigationController?.pushViewController(someWhereViewController, animated: true) } // MARK: Communication func passDataToNextScene ( segue : UIStoryboardSegue ) { // NOTE: Teach the router which scenes it can communicate with if segue . identifier == "ShowSomewhereScene" { passDataToSomewhereScene ( segue ) } } func passDataToSomewhereScene ( segue : UIStoryboardSegue ) { // NOTE: Teach the router how to pass data to the next scene // let someWhereViewController = segue.destinationViewController as! SomeWhereViewController // someWhereViewController.output.name = viewController.output.name } }

CreateOrderRouterInput protocol

The CreateOrderRouterInput protocol specifies its routes – the scenes it can navigates to – to the view controller. The method navigateToSomewhere() tells the view controller that: If you use me as your router, I know how to navigate to a scene called somewhere.

As you can see in the comment inside the navigateToSomewhere() method, the router is very flexible in the number of ways it can navigate to another scene. I’ll talk all about the router in another post.

viewController variable

The viewController variable is just a reference to the view controller that uses this router. It is a weak variable to avoid the reference cycle problem, and is set up by the configurator as you’ll soon see. The Apple way of transitioning between segues adds all the present* and push* methods to the UIViewController class. So we need to have viewController here so we can call those methods in the router.

passDataToNextScene() and passDataToSomewhereScene()

The passDataToNextScene() and passDataToSomewhereScene() methods provide a way for you to pass data to the next scene. You put pretty much the same code there as you would normally do for prepareForSegue() . passDataToNextScene() tries to match the segue identifier to dispatch to the more specific passDataToSomewhereScene() which does the actual data passing.

6. Configurator

The configurator’s job is to hook up all the Clean Swift components above. I made a point to extract all setup code into the configurator so that you can focus on writing your app code. It is unlikely that you ever need to look at the configurator at all. But I’ll uncover the magic for you.

import UIKit // MARK: Connect View, Interactor, and Presenter extension CreateOrderViewController: CreateOrderPresenterOutput { override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { router.passDataToNextScene(segue) } } extension CreateOrderInteractor: CreateOrderViewControllerOutput { } extension CreateOrderPresenter: CreateOrderInteractorOutput { } class CreateOrderConfigurator { // MARK: Object lifecycle class var sharedInstance: CreateOrderConfigurator { struct Static { static var instance: CreateOrderConfigurator? static var token: dispatch_once_t = 0 } dispatch_once(&Static.token) { Static.instance = CreateOrderConfigurator() } return Static.instance! } // MARK: Configuration func configure(viewController: CreateOrderViewController) { let router = CreateOrderRouter() router.viewController = viewController let presenter = CreateOrderPresenter() presenter.output = viewController let interactor = CreateOrderInteractor() interactor.output = presenter viewController.output = interactor viewController.router = router } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 import UIKit // MARK: Connect View, Interactor, and Presenter extension CreateOrderViewController : CreateOrderPresenterOutput { override func prepareForSegue ( segue : UIStoryboardSegue , sender : AnyObject ? ) { router . passDataToNextScene ( segue ) } } extension CreateOrderInteractor : CreateOrderViewControllerOutput { } extension CreateOrderPresenter : CreateOrderInteractorOutput { } class CreateOrderConfigurator { // MARK: Object lifecycle class var sharedInstance : CreateOrderConfigurator { struct Static { static var instance : CreateOrderConfigurator ? static var token : dispatch_once_t = 0 } dispatch_once ( & Static . token ) { Static . instance = CreateOrderConfigurator ( ) } return Static . instance ! } // MARK: Configuration func configure ( viewController : CreateOrderViewController ) { let router = CreateOrderRouter ( ) router . viewController = viewController let presenter = CreateOrderPresenter ( ) presenter . output = viewController let interactor = CreateOrderInteractor ( ) interactor . output = presenter viewController . output = interactor viewController . router = router } }

The extensions

Remember I told you there is no mention of the interactor in the view controller. The output variable is defined to be of type CreateOrderViewControllerOutput . You are also free to swap in another object to replace the interactor as the view controller’s output. But somehow the view controller needs to be hooked up to the interactor, right? That’s exactly what the three extensions at the beginning do.

The CreateOrderViewController extension adds the CreateOrderPresenterOutput protocol conformance. This means the presenter’s output is connected to the view controller. Similarly, the CreateOrderInteractor extension adds the CreateOrderViewControllerOutput protocol conformance. The CreateOrderPresenter extension adds the CreateOrderInteractorOutput protocol conformance.

Singleton

There should be only one configurator for each scene, and the connection setup code should run once and only once. I create a sharedInstance class variable and invoke CreateOrderConfigurator.sharedInstance.configure(self) in the view controller’s awakeFromNib() method. This is the very first opportunity I have to do stuff right after the view controller object has been loaded from the storyboard. Unless I override the UIViewController base class, and enforce you derive from it. But I didn’t want to do that.

configure() method

This is where the arrows are drawn in the VIP cycle. Note the arrows are uni-directional. This consistent flow of control makes things very clear. It is the reason why you know exactly which file and method to look for when you are fixing bugs.

Only the view controller is loaded from the storyboard. We need to actually create the interactor, presenter, and router instances manually. The configure() method does this, and then assigns the corresponding references to the output , router , and viewController variables.

The important thing to remember here is the VIP cycle:

The output of the view controller is connected to the input of the interactor. The output of the interactor is connected to the input of the presenter. The output of the presenter is connected to the input of the view controller. This means the flow of control is always unidirectional.

Remembering the VIP cycle will become very handy when you implement features and fix bugs. You’ll know exactly which file and method to look for.

It also simplifies your dependency graph. You don’t want objects to have references to one another whenever they please. It may seem convenient for the view controller to ask the presenter directly to format a string. But over time, you’ll end up with a mess in your dependency graph. Keep this in mind at all times to avoid any unnecessary coupling.

This will become very clear when we implement the create order use case.

7. Models

In order to completely decouple the Clean Swift components, we need to define data models to pass through the boundaries between them, instead of just using raw data models. There are 3 primary types of models:

Request – The view controller constructs a request model and passes it to the interactor. A request model contains mostly user inputs, such as text entered in text fields and values chosen in pickers.

– The view controller constructs a request model and passes it to the interactor. A request model contains mostly user inputs, such as text entered in text fields and values chosen in pickers. Response – After the interactor finishes doing work for a request, it encapsulates the results in a response model and then passes it to the presenter.

View Model – After the presenter receives the response from the interactor, it formats the results into primitive data types such as String and Int, and stuff them in the view model. It then passes the view model back to the view controller for display.

import UIKit struct CreateOrderRequest { } struct CreateOrderResponse { } struct CreateOrderViewModel { } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import UIKit struct CreateOrderRequest { } struct CreateOrderResponse { } struct CreateOrderViewModel { }

In the code generated by the templates, we don’t actually have any data in these models. So they are just empty. But you’ll see actual data when we implement the create order use case. Let’s see what this data looks like next.

Tired of all these boilerplate code? Subscribe below to get my Xcode templates to generate all these for you automagically.

“Create Order” Data

Let’s break down the create order use case to come up with the data required to create a new order. We’ll then create a form using table view and text fields in the app to collect this data from the user.

Customer ID Integer

Customer Contact Info First name Last name Phone Email

Shipment Destination Shipping address Street 1 Street 2 City State ZIP

Shipment Mechanism Shipping method Next day 3 to 5 days Ground

Payment Information Credit card number Expiration date CVV Billing address Street 1 Street 2 City State ZIP



That’s a gigantic form! In a more realistic app, we’ll likely have some of this data already after the user has logged in such as name, phone, email, shipping and billing address, and maybe credit card info. So this form will be dramatically slimmed down.

“Create Order” Business Logic

Now, let’s see what business logic we can come up with from the use case’s requirements. This can serve as pseudocode that we’ll later write acceptance tests for.

Order clerk issues “Create Order” command with above data. Display a form to collect data from user.

Form uses text fields to collect text data.

Form uses a picker to collect Shipping method and Expiration data.

Form uses a switch to auto-fill Billing address from Shipping address.

Form uses a button to issue the “Create Order” command. System validates all data. Ensure all fields except Street 2 are not blank.

If valid, display a “valid” message below the button.

If invalid, display error messages next to the invalid fields. System creates order and determines order-id. Generate an unique order ID.

Create and store a new order in Core Data. System delivers order-id to clerk. Display order ID on screen to user.

This is a good set of initial features to demonstrate how Clean Swift works and its benefits. In future posts, we’ll expand this feature set to show how you can adapt to changes easily. Some future requirements may be:

Use Core Location to reverse geocode current lat/lng to address to pre-fill shipping address and billing address.

Integrate Stripe API to collect credit card info.

Validate fields as data is entered instead of after the button is tapped.

Add country to shipping and billing addresses to expand business overseas.

Format phone number, credit card number, and expiration date.

Now we are ready to implement the create order use case.

Design the Create Order Form in Storyboard and View Controller

Where should we begin?

You want to start by collecting user inputs such as text and taps in the view controller. The view controller then passes the inputs to the interactor to get some work done. Next, the interactor passes the output to the presenter for formatting. Finally, the presenter asks the view controller to display the results.

This is the flow of control and it is always in one direction. The VIP cycle is not named VIP because of a very important person. It is because there is an order to things. V then I then P.

Let’s start by creating the create order form. In the storyboard, embed the CreateOrderViewController in a UINavigationController . Add a title Create Order to give it some context.

Make the table view use static cells. Add a new section for each group of data and a new cell for each piece of data required in the create order form. For each cell, add a UILabel and UITextField .

Make the following IBOutlet connections:

Connect all the text fields to the textFields IBOutlet collection. Also set their delegates to be our CreateOrderViewController .

IBOutlet collection. Also set their delegates to be our . Connect the text field for shipping method to the shippingMethodTextField IBOutlet.

IBOutlet. Connect the text field for expiration date to the expirationDateTextField IBOutlet.

Drag a UIPickerView and UIDatePicker from the Object Library to the scene. Then make the following IBOutlet connections:

Connect the UIPickerView to the shippingMethodPicker IBOutlet. Also set the data source and delegate to be our CreateOrderViewController .

to the IBOutlet. Also set the data source and delegate to be our . Connect the UIDatePicker to the expirationDatePicker IBOutlet. Also control-drag from the UIDatePicker to the CreateOrderViewController in the assistant editor to create an IBAction for the value change event and name it expirationDatePickerValueChanged() .

Your form should look like:

It won’t win any design award, but it satisfies our use case requirements. The beauty of Clean Swift is you can modify the view later without affecting other parts of the app. For example, we can add country to shipping and billing addresses to expand the client’s business overseas. Or we can hire a professional designer to style the form.

After setting up the IBOutlets and IBActions, your CreateOrderViewController should also contain the following code:

// MARK: Text fields @IBOutlet var textFields: [UITextField]! // MARK: Shipping method @IBOutlet weak var shippingMethodTextField: UITextField! @IBOutlet var shippingMethodPicker: UIPickerView! // MARK: Expiration date @IBOutlet weak var expirationDateTextField: UITextField! @IBOutlet var expirationDatePicker: UIDatePicker! @IBAction func expirationDatePickerValueChanged(sender: AnyObject) { } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // MARK: Text fields @ IBOutlet var textFields : [ UITextField ] ! // MARK: Shipping method @ IBOutlet weak var shippingMethodTextField : UITextField ! @ IBOutlet var shippingMethodPicker : UIPickerView ! // MARK: Expiration date @ IBOutlet weak var expirationDateTextField : UITextField ! @ IBOutlet var expirationDatePicker : UIDatePicker ! @ IBAction func expirationDatePickerValueChanged ( sender : AnyObject ) { }

Now the user interface is all set. Let’s tackle user interaction next. When the user taps the next button in the keyboard, we want him to be able to enter text for the next text field. Make the CreateOrderViewController conform to the UITextFieldDelegate protocol. Then add the textFieldShouldReturn() method.

func textFieldShouldReturn(textField: UITextField) -> Bool { textField.resignFirstResponder() if let index = textFields.indexOf(textField) { if index < textFields.count - 1 { let nextTextField = textFields[index + 1] nextTextField.becomeFirstResponder() } } return true } 1 2 3 4 5 6 7 8 9 10 11 12 func textFieldShouldReturn ( textField : UITextField ) -> Bool { textField . resignFirstResponder ( ) if let index = textFields . indexOf ( textField ) { if index < textFields . count - 1 { let nextTextField = textFields [ index + 1 ] nextTextField . becomeFirstResponder ( ) } } return true }

When the user taps the table view cell (not the text field directly), we still want the user to be able to edit the text field. After all, using UITextBorderStyleNone makes it impossible to see the boundary of the text field. Plus, it just feels better and a common behavior to be able to tap a label and edit the field. To achieve this behavior, add the tableView:didSelectRowAtIndexPath() method.

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { if let cell = tableView.cellForRowAtIndexPath(indexPath) { for textField in textFields { if textField.isDescendantOfView(cell) { textField.becomeFirstResponder() } } } } 1 2 3 4 5 6 7 8 9 10 11 override func tableView ( tableView : UITableView , didSelectRowAtIndexPath indexPath : NSIndexPath ) { if let cell = tableView . cellForRowAtIndexPath ( indexPath ) { for textField in textFields { if textField . isDescendantOfView ( cell ) { textField . becomeFirstResponder ( ) } } } }

Implement Shipping Methods Business Logic in the Interactor

Getting the pickers for shipping method and expiration date to work is pretty straight forward. But more importantly, this is where we encounter our first business logic.

Let’s look at shipping method first.

The available shipping methods can change in the client’s business as it partners with different shippers over time. We don’t want to leave it in the view controller. So, we’ll extract this business logic to the interactor.

Start by adding the configurePickers() method and invoke it in viewDidLoad() . When the user taps the shippingMethodTextField , the correct picker UI will be shown instead of the standard keyboard.

override func viewDidLoad() { super.viewDidLoad() configurePickers() } func configurePickers() { shippingMethodTextField.inputView = shippingMethodPicker } 1 2 3 4 5 6 7 8 9 10 11 override func viewDidLoad ( ) { super . viewDidLoad ( ) configurePickers ( ) } func configurePickers ( ) { shippingMethodTextField . inputView = shippingMethodPicker }

Next, make CreateOrderViewController conform to the UIPickerViewDataSource and UIPickerViewDelegate protocols, and add the following methods.

func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int { return 1 } func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return output.shippingMethods.count } func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return output.shippingMethods[row] } func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { shippingMethodTextField.text = output.shippingMethods[row] } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func numberOfComponentsInPickerView ( pickerView : UIPickerView ) -> Int { return 1 } func pickerView ( pickerView : UIPickerView , numberOfRowsInComponent component : Int ) -> Int { return output . shippingMethods . count } func pickerView ( pickerView : UIPickerView , titleForRow row : Int , forComponent component : Int ) -> String ? { return output . shippingMethods [ row ] } func pickerView ( pickerView : UIPickerView , didSelectRow row : Int , inComponent component : Int ) { shippingMethodTextField . text = output . shippingMethods [ row ] }

Since the shipping method business logic is moved to the interactor, we can invoke it using output.shippingMethods in the view controller. We then also need to add the shippingMethods variable to the CreateOrderViewControllerOutput and CreateOrderInteractorInput protocols. We need to add it to both protocols because the configurator has connected the CreateOrderViewController output to the CreateOrderInteractor input. For now, we’ll keep things simple by assuming it is an array of fixed literal strings. In the future, we can retrieve the available shipping methods dynamically in Core Data or over the network.

In CreateOrderViewController :

protocol CreateOrderViewControllerOutput { var shippingMethods: [String] { get } } 1 2 3 4 5 protocol CreateOrderViewControllerOutput { var shippingMethods : [ String ] { get } }

In CreateOrderInteractor :

protocol CreateOrderInteractorInput { var shippingMethods: [String] { get } } class CreateOrderInteractor: CreateOrderInteractorInput { var output: CreateOrderInteractorOutput! var worker: CreateOrderWorker! var shippingMethods = [ "Standard Shipping", "Two-Day Shipping ", "One-Day Shipping " ] } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 protocol CreateOrderInteractorInput { var shippingMethods : [ String ] { get } } class CreateOrderInteractor : CreateOrderInteractorInput { var output : CreateOrderInteractorOutput ! var worker : CreateOrderWorker ! var shippingMethods = [ "Standard Shipping" , "Two-Day Shipping " , "One-Day Shipping " ] }

Implement Expiration Date Business Logic in the Interactor

The expiration date can be formatted differently depending on the user’s language and location. We’ll move this presentation logic to the presenter.

Let’s take care of the expiration date picker in configurePickers() first.

func configurePickers() { shippingMethodTextField.inputView = shippingMethodPicker expirationDateTextField.inputView = expirationDatePicker } 1 2 3 4 5 6 func configurePickers ( ) { shippingMethodTextField . inputView = shippingMethodPicker expirationDateTextField . inputView = expirationDatePicker }

Next, modify the expirationDatePickerValueChanged() method to look like:

@IBAction func expirationDatePickerValueChanged(sender: AnyObject) { let date = expirationDatePicker.date let request = CreateOrder_FormatExpirationDate_Request(date: date) output.formatExpirationDate(request) } 1 2 3 4 5 6 7 @ IBAction func expirationDatePickerValueChanged ( sender : AnyObject ) { let date = expirationDatePicker . date let request = CreateOrder_FormatExpirationDate_Request ( date : date ) output . formatExpirationDate ( request ) }

After the user chooses an expiration date in the picker, the expirationDatePickerValueChanged() method is invoked. We retrieve the date from the picker, and stuffs it in this weird looking thing called CreateOrder_FormatExpirationDate_Request . It is simply a Swift struct that we define in CreateOrderModels.swift . It has one data member named date of NSDate type.

struct CreateOrder_FormatExpirationDate_Request { var date: NSDate } 1 2 3 4 5 struct CreateOrder_FormatExpirationDate_Request { var date : NSDate }

You may wonder why I use _ here. Objective-C and Swift naming convention suggests using upper camel case for token names such as class, struct, and enum. Before you scream at me for breaking convection. Let me explain why I used _.

CreateOrder is the name of the scene. FormatExpirationDate is the intention or business rule. Request indicates the boundary between the view controller and interactor. That’s why you get CreateOrder_FormatExpirationDate_Request .

Contrast CreateOrder_FormatExpirationDate_Request with CreateOrderFormatExpirationDateRequest . Which one tells you about the scene, intention, and boundary more clearly? So I chose clarity over dogma.

Now, back to the expirationDatePickerValueChanged() method.

After we create the request object and initialize it with the date the user has picked, we simply ask the output to format the expiration date by calling output.formatExpirationDate(request) .

Yes, you guessed it right. We do need to add this new method to the CreateOrderViewControllerOutput and CreateOrderInteractorInput protocols.

In CreateOrderViewController :

protocol CreateOrderViewControllerOutput { var shippingMethods: [String] { get } func formatExpirationDate(request: CreateOrder_FormatExpirationDate_Request) } 1 2 3 4 5 6 protocol CreateOrderViewControllerOutput { var shippingMethods : [ String ] { get } func formatExpirationDate ( request : CreateOrder_FormatExpirationDate_Request ) }

In CreateOrderInteractor :

protocol CreateOrderInteractorInput { var shippingMethods: [String] { get } func formatExpirationDate(request: CreateOrder_FormatExpirationDate_Request) } class CreateOrderInteractor: CreateOrderInteractorInput { // MARK: Expiration date func formatExpirationDate(request: CreateOrder_FormatExpirationDate_Request) { let response = CreateOrder_FormatExpirationDate_Response(date: request.date) output.presentExpirationDate(response) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 protocol CreateOrderInteractorInput { var shippingMethods : [ String ] { get } func formatExpirationDate ( request : CreateOrder_FormatExpirationDate_Request ) } class CreateOrderInteractor : CreateOrderInteractorInput { // MARK: Expiration date func formatExpirationDate ( request : CreateOrder_FormatExpirationDate_Request ) { let response = CreateOrder_FormatExpirationDate_Response ( date : request . date ) output . presentExpirationDate ( response ) } }

Implement Expiration Date Presentation Logic in the Presenter

In the formatExpirationDate() method, we create the CreateOrder_FormatExpirationDate_Response response object as defined in CreateOrderModels.swift .

struct CreateOrder_FormatExpirationDate_Response { var date: NSDate } 1 2 3 4 5 struct CreateOrder_FormatExpirationDate_Response { var date : NSDate }

We initialize the response object with the date we get from the request model. The interactor does not do anything with the date and just passes is straight through by invoking output.presentExpirationDate(response) . There is currently no business logic associated with the expiration date. Later, when we add validation to make sure the expiration date doesn’t fall in the past, we’ll add that business rule here in the interactor.

Let’s continue by adding the presentExpirationDate() method to the CreateOrderInteractorOutput and CreateOrderPresenterInput protocols.

In CreateOrderInteractor :

protocol CreateOrderInteractorOutput { func presentExpirationDate(response: CreateOrder_FormatExpirationDate_Response) } 1 2 3 4 5 protocol CreateOrderInteractorOutput { func presentExpirationDate ( response : CreateOrder_FormatExpirationDate_Response ) }

In CreateOrderPresenter :

protocol CreateOrderPresenterInput { func presentExpirationDate(response: CreateOrder_FormatExpirationDate_Response) } class CreateOrderPresenter: CreateOrderPresenterInput { weak var output: CreateOrderPresenterOutput! let dateFormatter: NSDateFormatter = { let dateFormatter = NSDateFormatter() dateFormatter.dateStyle = .ShortStyle dateFormatter.timeStyle = NSDateFormatterStyle.NoStyle return dateFormatter }() // MARK: Expiration date func presentExpirationDate(response: CreateOrder_FormatExpirationDate_Response) { let date = dateFormatter.stringFromDate(response.date) let viewModel = CreateOrder_FormatExpirationDate_ViewModel(date: date) output.displayExpirationDate(viewModel) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 protocol CreateOrderPresenterInput { func presentExpirationDate ( response : CreateOrder_FormatExpirationDate_Response ) } class CreateOrderPresenter : CreateOrderPresenterInput { weak var output : CreateOrderPresenterOutput ! let dateFormatter : NSDateFormatter = { let dateFormatter = NSDateFormatter ( ) dateFormatter . dateStyle = . ShortStyle dateFormatter . timeStyle = NSDateFormatterStyle . NoStyle return dateFormatter } ( ) // MARK: Expiration date func presentExpirationDate ( response : CreateOrder_FormatExpirationDate_Response ) { let date = dateFormatter . stringFromDate ( response . date ) let viewModel = CreateOrder_FormatExpirationDate_ViewModel ( date : date ) output . displayExpirationDate ( viewModel ) } }

In CreateOrderPresenter , we define a NSDateFormatter constant. The presentExpirationDate() method simply asks this date formatter object to convert the expiration date from NSDate to String . It then stuffs this date string representation in the CreateOrder_FormatExpirationDate_ViewModel struct as defined in CreateOrderModels.swift .

Note that the date in the view model is a String , not NSDate . A presenter’s job is to marshal data into a format suitable for display to the user. Since our UI displays the date in a UITextField , we convert a date into a string. In fact, most of the time, the data in your view models will be either strings or numbers since that’s what human read.

struct CreateOrder_FormatExpirationDate_ViewModel { var date: String } 1 2 3 4 5 struct CreateOrder_FormatExpirationDate_ViewModel { var date : String }

It finally asks the view controller to display it by calling output.displayExpirationDate(viewModel) .

This also means we need to define the displayExpirationDate() method in the CreateOrderPresenterOutput and CreateOrderViewControllerInput protocols.

In CreateOrderPresenter :

protocol CreateOrderPresenterOutput: class { func displayExpirationDate(viewModel: CreateOrder_FormatExpirationDate_ViewModel) } 1 2 3 4 5 protocol CreateOrderPresenterOutput : class { func displayExpirationDate ( viewModel : CreateOrder_FormatExpirationDate_ViewModel ) }

In CreateOrderViewController :

protocol CreateOrderViewControllerInput { func displayExpirationDate(viewModel: CreateOrder_FormatExpirationDate_ViewModel) } class CreateOrderViewController: UITableViewController, CreateOrderViewControllerInput, UITextFieldDelegate, UIPickerViewDataSource, UIPickerViewDelegate { // MARK: Expiration date func displayExpirationDate(viewModel: CreateOrder_FormatExpirationDate_ViewModel) { let date = viewModel.date expirationDateTextField.text = date } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 protocol CreateOrderViewControllerInput { func displayExpirationDate ( viewModel : CreateOrder_FormatExpirationDate_ViewModel ) } class CreateOrderViewController : UITableViewController , CreateOrderViewControllerInput , UITextFieldDelegate , UIPickerViewDataSource , UIPickerViewDelegate { // MARK: Expiration date func displayExpirationDate ( viewModel : CreateOrder_FormatExpirationDate_ViewModel ) { let date = viewModel . date expirationDateTextField . text = date } }

In the displayExpirationDate() method, we just need to grab the date string from the view model and assign it to the textField, as in expirationDateTextField.text = date .

That’s it. This completes the VIP cycle.

You can find the complete code example at https://github.com/Clean-Swift/CleanStore.

You’ve got a working create order form. The user can enter text in the text fields and choose shipping method and expiration date in the pickers. Your business and presentation logic are extracted away from your view controller into the interactor and presenter. Custom boundary model structs are used to decouple the Clean Swift components at their boundaries.

In this example, we haven’t looked at the worker and router yet. When your business logic is more complicated, a little division of labor helps. An interactor’s job can be broken down into multiple smaller tasks, which can then be performed by individual workers. When you need to show a different scene after a new order is created, you’ll need to use the router to extract away the navigation logic.

Let’s recap.

What have you done so far?

In this post, you’ve learned:

The massive view controller problem is real.

MVC is not a suitable architecture for an iOS app.

Design patterns and refactoring are just techniques, not architectures.

Testing is only possible with a sound architecture.

Good architecture makes making changes easy.

Organize your code with intents.

The Clean Swift architecture and the VIP cycle.

Break down the create order use case into data and business logic.

Use Clean Swift to implement the use case.

And a reminder on the VIP cycle:

The view controller accepts an user event, constructs a request object, sends it to the interactor.

The interactor does some work with the request, constructs a response object, and sends it to the presenter.

The presenter formats the data in the response, constructs a view model object, and sends it to the view controller.

The view controller displays the results contained in the view model to the user.

In future posts, we’ll continue with this use case. You’ll learn:

How to implement the switch to auto-fill the billing address from the shipping address.

How to use workers to validate the create order form.

How to save the new order using Core Data when the user taps the Done button.

How to use the router to navigate to any scene in multiple storyboards.

How to use TDD to drive a feature.

You can subscribe below to get these future updates, and also get my Xcode templates to start using Clean Swift today.

You can buy The Clean Swift Handbook now to start writing clean swift code using the latest templates, Xcode, and Swift. The PDF ebook has an updated version of this post, and it covers the additional ListOrders and ShowOrder scenes, as well as topics on routing and passing data between scenes. There are also additional screencasts to help get you started, and diagrams to give you the big picture. There is currently a special BONUS for a one-month free access to my premium mentorship program. You’ll gain access to more screencasts and tutorials in our private Wistia group.

Finally, if you think this post has shed a new light on iOS architecture for you, I ask that you share this post with your developer friends.

I hope you’ve learned something new and start using it today!

You can find the full source code with tests at GitHub.