This is a very common question. If you follow the MVC pattern, like all the tutorials and Apple sample code, your view controllers conform to a lot of protocols.
UITableViewDataSource
, UITableViewDelegate
, and UICollectionViewDataSource
, UICollectionViewDelegate
are the most common ones.
If your UI collects user inputs, you also have UIPickerViewDataSource
, UIPickerViewDelegate
, and UITextFieldDelegate
.
What if you show a map? You can count on having MKMapViewDelegate
and CLLocationManagerDelegate
.
Almost all apps fetch data from APIs, so you also have some of these: URLSessionDelegate
, URLSessionTaskDelegate
, URLSessionDataDelegate
, URLSessionDownloadDelegate
.
I can keep going, but let’s just stop here.
Your view controllers conform to many of these Apple provided protocols, plus any custom protocols you define for your app. Each of these protocols has several delegate methods your view controllers need to implement. It can easily get out of hand.
This is one reason contributing to your massive view controllers. Let’s see how Clean Swift tackles this problem.
Move ’em outta here
You may think: “Let’s just move these delegate methods out of the view controllers. Then my view controllers will have fewer lines of code.”
You can create a new, custom data source or delegate class to define an API. Then, you can instantiate an instance from your view controller, and invoke methods on.
Or, you can simply move these methods to a Swift extension in a separate file(s). This is super easy and doesn’t require any API design decisions. I wrote about how you can do that in more details in this post.
Although it cuts down the number of lines of code, but deep inside your mind, you know you’re just moving code around. You sense there is still something wrong. You want to do better. But you just don’t know how.
Yes, there’s a much better way.
This will never be a problem using Clean Swift
If you apply the Clean Architecture to your app, this is never a problem. You’ll never have too many delegate methods in your view controllers. Your view controllers conform only to protocols that should belong there.
How do you determine what protocols should belong in your view controllers?
When you implement your features using the VIP cycle, all your business logic naturally stay in your interactors and workers. As a result, any protocol without the UI
prefix are conformed to and have their delegate methods in the interactors and workers. And any protocol with the UI
prefix stay in your view controllers.
In particular, if the feature calls for adding a map to the UI, the VIP cycle drives your design. You’ll end up conforming to the MKMapViewDelegate
and CLLocationManagerDelegate
protocols in your interactor.
Your interactor will decide how to respond to events when the user moves or zooms in the map. It’ll then ask the presenter to format an appropriate response before handing it to the view controller to update the display.
You most likely deal with networking throughout your app. Fetch some feeds in this screen. Display a user profile in that screen. Create a post there. Using Clean Swift, you’ll create a shared worker(s) to encapsulate all your networking code. So, your worker will conform to those URLSessionDelegate
, URLSessionTaskDelegate
, URLSessionDataDelegate
, and URLSessionDownloadDelegate
.
Your worker creates a session configuration and a download task, and then call resume()
to start the task asynchronously. When the data becomes available, your callback is invoked. At that time, your worker notifies your interactor which in turn invokes your presenter to format the data for display in the view controller.
When it comes to your view controller, now that all those other protocols are out of the way, it’s suddenly not too bad to conform to the UI
prefixed data source and delegate protocols. The UI prefix means they have to deal with the UI.
Even if you have a super bloated UI, you can still create a custom class or pull the methods out to an extension.
What does this all mean? It means you’ll never have to refactor because of massive view controllers. Massive view controller is not a thing anymore.
First congrats on the update 🙂
Looks and feels way better that the “input and output” stuff 😉
One question:
I have a huge form handled by my controller, where would you put the form validation?
Put it in the interactor? But the isValid function needs to return a bool…
Hi Urkman,
Yes, I would put the validation logic in the interactor. The response can return a
Bool
which can be passed to the presenter. Your presenter can then:Bool
to the view controller so that it can decorate the text in red or green.NSAttributedString
for displaying to the user.Does it make sense?
Raymond,
Thank for the blog. Can you please help me with this?
If I have more than 2 UIPickerViews (let’s say country list and locality list). If I implement their data source in ViewController, I might need to check which picker view is asking the data. In MVVM, I used to have two classes which used to implements their data source separately.
How in VIP should I implement it?
Hi Tasin,
If it’s just two pickers, I wouldn’t try to over engineer it. But here’s a few thought.
Create a
CountryPickerDelegate
and aLocalityPickerDelegate
class to conform to theUIPickerViewDelegate
protocol. Instantiate them in your view controller. Then you can have a one-to-one picker delegate relationship.Another way is to simply create
CountryPicker
andLocalityPicker
asUIPickerView
subclasses and have them conform to their respectiveUIPickerViewDelegate
protocol. You can encapsulate all country picking and locality picking details all within your custom subclasses.You don’t really need any special tools to accomplish that.
Hi Raymond,
I have a question, where should we put our datasource like array? I assume it should be in Interactor, if this so then how can I access that in viewController’s tableview delegate?