Question 1: What is the correct way to build a Use Case (or more than one) with 2 ways to do the same action?
For example:
I have a 3 screens in
Question 1: What is the correct way to build a Use Case (or more than one) with 2 ways to do the same action?
In VIPER design, you can create two methods in the same Interactor suitable for each primary and alternates of the use case.
Question 2: (Based on bhavik's answer) So I don't need one presenter for one interactor precisely, I can have 1 interactor and 3 presenters/views.
Based on our discussion and your updates, I think I understand it better.
CameraView
.So, you should have single EditPlacePresenter/View
for EditPlaceInteractor
that pass data Place data with or without Photo.
Question 3: Should my boundary methods for the interactor always return void?
In bhavik's example they are returning something, but in the VIPER blog and the uncle Bob's video they always return void and the result comes in the form of another boundary method that the interactor calls on the presenter/controller.
I think you are referring to the below Presenter method that receives results from the Interactor.
- (void)foundUpcomingItems:(NSArray*)upcomingItems
For the above to work, the Interactor will have delegate instances that will be wired/patched through by the Presenter/Controller looking for result or data. This means the Presenter/Controller is tied to Interactor or their reference or return-function-pointer is passed in each Interactor method call. Is that by design?
I think, Interactor should return data as per the use case. For example Interactor should return the EditPlaceResult with success or failure.
This should be part of the use case. If not then, it should not return anything. It will return void and a separate Interactor will be queried by Presenter to check if Map Place was added successfully or not.
References in the blog:
Question 4: The VIPER way does not use a controller, only a presenter to talk to the interactor, when the uncle Bob's video uses a controller and a presenter for different interactions with the interactor. Which approach should I take?
You need to define VIPER routes for following navigation:
MapView
to CameraView
(Use Location)MapView
to EditPlaceView
(Use Coordinate)CameraView
to EditPlaceView
MapView
if successfulAs per the VIPER blog, view controllers and navigation controllers are used by Presenters and Wireframes.
VIPER Wireframe handles Navigation and makes view controllers become lean, mean, view controlling machines.
Basically Wireframe abstracts away navigation controller and instead provides route definition.
Wireframe
Presenter
Question 5: If my Use Case is something like "Go to other screen", should it even have an interactor? Since the current view will tell its presenter what button was pressed (what view to go to) and this current presenter will tell its wireframe "change to this other wireframe".
No. Navigation as part of use case may not need Interactor. It is transition only. The target Presenter may need an Interactor. For example, CameraView/Presenter
does not need Interactor but EditPlaceView
needs to save the place.
Overall: The idea behind architectural patterns is to divide a given software application into interconnected parts, so as to separate internal representations of information from the ways that information is presented to or accepted from the user. MVC, MVP, MVVM, VIPER all focus on isolating View, Logic and Navigation in one way or other.
Architectural patterns are limited in what they intend to decompose. We have to understand that architectural patterns do not decompose or isolate everything. Also if one architectural pattern delegates certain responsibilities to certain part, other does not do that at all probably or assign multiple responsibilities to single part.
We are allowed to extend or limit the isolation and decomposition to the extent that it justifies the cause and does not impose unnecessary separation of concerns that overrun the cost. You can choose to use navigation controllers and your presenter can take dependency on them without Wireframe routes defined. These controllers then will be responsible for the navigation between screens.
What to do if - Create, Edit and View actions end-up in the same ViewController?
Is it a good idea if MapViewController uses PlacesInteractor to retrieve the places and the CurrentLocationInteractor to request user's location authorization and getting the most updated coordinates?
It is not a problem to combine related logic into single Interactor. But it will not longer be an "Interactor". It will become a "service" or "manager" as in MapPlaceManager/MapPlaceService which will have methods such as:
canCreateMapPlace
createMapPlace(Details)
getMapPlaceCount
getMapPlaceIDs
getMapPlaceDetails(ID)
canUpdateMapPlace
updateMapPlace(ID, NewDetails)
I think the idea was to expose only the intended APIs per use case and hence Interactor - which can clearly state what the user of that Interactor is going to do with it. If it has multiple APIs that can do different things like create/edit/delete map places, then we have to check the method calls in the caller to know what the caller is going to do. Interactors in this sense to me are very high level - business/requirements level interfaces. You can hide your back-end services and managers inside these individual Interactors.
You can take this notion to as far as possible and/or feasible - Feasible rather possible. There will be an extreme where we draw the line, instead of following it too religiously. Business systems tend to be more formal and methodical to give you an example.
In your case, when a button is pressed on your main view that changes your MapPlaceView
into MapPlaceEditView
, you are changing the use case that the new view is going to satisfy. Such in-place view changes are appropriate view design considerations for mobile and also it is use friendly. However, often it encourages complex GUI and messy presenter logic. If it is manageable, cleaner and easier for your ViewController/Presenter to switch "modes" between "Create, View, Edit" - you are good to go. It is not perfect, but it is not wrong. They are Front-End-Participants and have the highest level of freedom and frequency of changes anyway.
An alternate good UI design I have found useful instead of in-place fields editing, is "flipping" the views or any such view transition effect. You can have a MainMapPlacePresenter/ViewController
and it has 3 sub views - for Create, Edit and View. This main view is then responsible for switching between these three views. It enables cleaner navigation, cleaner use case implementations and neat design.
Similarly for CurrentLocationInteractor
, it does two things - 1. request permission to use "device location service" and 2. use "device location service". Now, it seems it is not an Interactor at all. It is Front-End functionality. But you can use SaveAuthorizationInteractor
to save user's choice. But that is different thing. The more I think, Interactors are responsible for things that deal with your system and not with your user.
Presenter does all the "user-talking" and "decision-making" work - they may use device APIs if they need e.g. Location Service. You can create abstract interface ILocationService
and wrapper implementation called LocationService
that will absorb user's device location service - low level implementation and platform-specific detail.
In implementation terms: You can have:
MainPresenter/MainViewController
On Load - Show MapView along with Buttons for Edit and Create Map Place
MapPresenter/MapViewController
On Load - Show Map
Navigations - login, authorization, create, edit
Interactions - none
MapPlaceCreatePresenter/MapPlaceCreateViewController
On Load - call MapPlaceCreateInteractor.canCreateMapPlace - Response = {AllGood, UserNotLoggedIn, LocationIsNotAuthorized}
Interaction - MapPlaceCreateInteractor.createMapPlace - Responses = {PlaceCreatedSuccessfully}
Navigations - Login, Location Authorization, Back to Main View (With Response - UserLoginNeeded, UserAuthorizationForLocationAccessNeeded)
MapPlaceUpdatePresenter/MapPlaceUpdateViewController
On Load - call MapPlaceUpdateInteractor.canUpdateMapPlace
Interaction - MapPlaceUpdateInteractor.updateMapPlace(ExistingMapPlaceID, NewDetails) - Responses = {PlaceUpdatedSuccessfully}
Navigations - Login, Location Authorization, Back to Main View (With Response - UserLoginNeeded, UserAuthorizationForLocationAccessNeeded)
Question: What to do when 2 use cases are related to the same presenter/view? For example, I have a presenter/view that
Answer
It seems that you are dealing with non-trivial use-case modelling scenarios.
"Check user-login" does not need use case. Similarly, if location authorization is not being updated as part of configuration settings, then it is also not a use-case. They are not good candidates for use-case modelling but rather preconditions or "steps" in other complex use cases. I think they are more like preconditions then "steps" and certainly not individual "use-cases" themselves.
Preconditions should be accompanied by use-case exceptions and non-trivial "steps" hints use-case-reuse via include-dependencies. If preconditions fails, you can provide appropriate messages and options to fulfil those conditions. For complex steps, one use case redirect to other use-case.
The idea is to either opt for exceptions that will terminate the use case with valid message on failed preconditions OR include other use-cases that will ask user to log-in first and resume current use case.
The goal is to break down complex requirements and re-use them. Quoting from Use Case Reuse - Include Dependency. "You use include dependencies whenever one use case needs the behavior of another. Introducing a new use case that encapsulates similar logic that occurs in several use cases is quite common."
Thus,
CreatePlaceInteractor
should take dependency on UserLogInInteractor
and LocationAuthorizationInteractor
and call them when necessary.CreatePlaceInteractor
should send back data that Presenter can interpret and ask Wireframe to launch UserLoginPresenter
and/or LocationAuthorizationPresenter
.This will involve carefully designed interactions and navigation for control flow and data flow using Interactors, Presenters and Wireframes. In the process, you will be challenging lot of core assumptions and hidden unhandled system behaviour.
Since other use cases may also include user-login or authorization-access to finish their task, these use cases should be included but not necessarily executed all the time - conditional function call to UserLogInInteractor and LocationAuthorizationInteractor. If user is logged in and authorization access is already allowed, they will be skipped. When user chooses CameraView, check for user-login and location access. When user chooses EditView directly from MapView, check for for user-login only.
I think you should implement precondition logic into your Interactors and Presenter can use it to make Presentations and Navigations related decisions.
Considering following list of Use Cases:
Preconditions:
Steps:
Steps:
Steps:
Steps:
Steps:
Steps:
It appears that the two use cases seem to have identical end results that is "Create a Place with / without picture" with two ways - "With Camera and Location Service" vs "Manual Data Entry".
The "With Camera and Location Service" use case adds photo as well.
However, I am wondering if two ways of achieving the same result or identical result is considered single use case.
I would design the two as separate use cases if I can, otherwise I would make one as primary or default use case and other approach as an alternative to achieve the same / identical end result.
Use Case: Create Place
Basic flow: Use Camera and Location Service
User presses plus button.
App displays camera view.
User takes a picture.
App creates place with current location "and picture".
Alternate flow A: Use manual data entry
A.1. User “long press” on the map.
A.2. App drops a temporary pin and displays the place editing view.
A.3. User edits the place information and presses save button.
A. 4. App creates the place "without picture" and save it.
Does this make sense?
Updates with specific details
The View Controller responsible to deal with View in iOS, is in fact treated as View in VIPER. See the "View" paragraph. A UIViewController, or one of its subclasses, will implement the View protocol. Hence this controller is your View.
The idea is you need to isolate your Presenter from the knowledge of iOS View or iOS controller. It should deal with iOS specific view and controller via plain data structures like ViewModels. If you can do that, you have successfully isolated Presenter from iOS SDK specific dependencies and you can write and run TDD or Unit tests directly on your Presenters if you want.
However, more interestingly once you succeed in isolating Presenter from View and ViewController, you can isolate Interactor from Presenter easily. Presenter will have to pass data in the form that is acceptable to the Interactor. So Interactor does know nothing about Presenter. It's independent and you can this Interactor (Use case) in command-line, web or desktop-GUI app as easily.
I think there should be one Interactor per use case. If there are alternative flows to the use case, the interactor will have methods with those alternative data structures.
In your case the CreatePlaceInteractor will have two methods:
CreatePlaceWithManualDataEntryResult createPlaceWithManualDataEntry(CreatePlaceWithManualDataEntryRequest)
CreatePlaceWithCameraAndLocationServiceResult createPlaceWithCameraAndLocationService(CreatePlaceWithCameraAndLocationServiceRequest)
There will be three View/Presenters:
CreatePlaceChoicePresenter/View will capture user choice and send the request to the NavigationController or Wireframe as appropriate which will return new Presenter/View according to the user choice.
CreatePlaceWithManualDataEntryPresenter/View will create and convert CreatePlaceWithManualDataEntry ViewModel into CreatePlaceWithManualDataEntry Request and will receive CreatePlaceWithManualDataEntry Result and process accordingly to display the use case result on the view.
CreatePlaceWithCameraAndLocationServicePresenter/View will create and convert CreatePlaceWithCameraAndLocationService ViewModel into CreatePlaceWithCameraAndLocationService Request and will receive createPlaceWithCameraAndLocationService Result and process accordingly to display the use case result on the view.
Apologies for being verbose on request, resonse, viewmodels and method names.