Assume that you have a cross platform application. The application runs on Android and on iOS. Your shared language across both platforms is Java. Typically you would write your business logic in Java and all you UI specific part in Java (for Android) and Objective-C (for iOS).
Typically when you implement the MVP pattern in a cross platform, cross language application you would have the Model and the Presenter in Java and provide a Java interface for your Views which is known to your presenters. This way your shared Java presenters can communicate with whatever view implementation you use on the platform specific part.
Lets assume we want to write a iOS app with a Java part which could be shared later on with the same Android app. Here is a graphical representation of the design:
On the left side there is the Java part. In Java you write your models, controllers as well as your view interfaces. You make all the wiring using dependency injection. Then the Java code can be translated to Objective-C using J2objc.
On the right side you have the Objective-C part. Here your UIViewController's can implement the Java interfaces which where translated into ObjectiveC protocols.
Problem:
What I am struggling about is how navigation between views takes place. Assume you are on UIViewControllerA and you tap a button which should bring you to UIViewControllerB. What would you do?
Case 1:
You report the button tap to the Java ControllerA (1) of UIViewControllerA and the Java ControllerA calls Java ControllerB (2) which is linked to UIViewControllerB (3). Then you have the problem that you do not know from the Java Controller side how to insert the UIViewControllerB in the Objective-C View hierarchy. You cannot handle that from the Java side because you have only access to the View interfaces.
Case 2:
You can make the transition to UIViewControllerB whether it is modal or with a UINavigationController or whatever (1). Then, first you need the correct instance of UIViewControllerB which is bind to the Java ControllerB (2). Otherwise the UIViewControllerB could not interact which the Java ControllerB (2,3). When you have the correct instance you need to tell Java ControllerB that the View (UIViewControllerB) has been revealed.
I am still struggling with this problem of how to handle the navigation between different controllers.
How can I model the navigation between different Controllers and handle the cross platform View changes appropriately?
Short answer:
Here is how we do it:
For simple "normal" stuff (like a button that opens the device camera or opens another Activity/UIViewController without any logic behind the action) - ActivityA directly opens ActivityB. ActivityB is now responsible communicating with the app shared logic layer if needed.
For anything more complex or logic dependent we're using 2 options:
ActivityA calls a method of some UseCase which returns an enum or public static final int and takes some action accordingly -OR-
Said UseCase can call a method of a ScreenHandler we registered earlier which knows how to open common Activities from anywhere in the app with some supplied parameters.
Long answer:
I'm the lead developer in a company using a java library for the application's models, logic, and business rules which both mobile platforms (Android and iOS) implement using j2objc.
My design principles come directly from Uncle Bob and SOLID, I really dislike the usage of MVP or MVC when designing whole applications complete with inter-component communications because then you start linking each Activity with 1 and only 1 Controller which sometimes is OK but most of the times you end up with a God Object of a controller that tends to change as much as a View. This can lead to serious code smells.
My favorite way (and the one I find cleanest) of handling this is breaking everything up into UseCases each of which handles 1 "situation" in the app. Sure you can have a Controller that handles several of those UseCases but then all it knows is how to delegate to those UseCases and nothing more.
Additionally, I don't see a reason of linking every action of an Activity to a Controller sitting in the logical layer, if this action is a simple "take me to the map screen" or anything of this sort. The role of the Activity should be handling the Views it holds, and as the only "smart" thing living in the life cycle of the application, I see no reason it can't call the start of the next activity itself.
Furthermore, Activity/UIViewController lifecycle is too complex and too different from each other to be handled by the common java lib. It is something I view as a "detail" and not really "business rules", each platform needs to implement and worry about, thus making the code in the java lib more solid and not prone to change.
Again, my goal is to have each component of the app be as SRP (Single Responsibility Principle) as it can be, and this means linking as few things together as possible.
So an example of simple "normal" stuff:
(all examples are totally imaginary)
ActivityAllUsers displays a list of model object items. Those items came from calling AllUsersInteractor - a UseCase controllerin a back thread (which in turn also handled by the java lib with a dispatch to main thread when the request is completed). The user clicks on one of the items in this list. In this example the ActivityAllUsers already has the model object so opening ActivityUserDetail is a straightforward call with a bundle (or another mechanism) of this data model object. The new activity, ActivityUserDetail, is responsible of creating and using the correct UseCases if further actions are needed.
Example of complex logic call:
ActivityUserDetail has a button titled "Add as a friend" which when clicked calls the callback method onAddFriendClicked in the ActivityUserDetail:
public void onAddFriendClicked() {
AddUserFriendInteractor addUserFriend = new AddUserFriendInteractor();
int result = addUserFriend.add(this.user);
switch(result){
case AddUserFriendInteractor.ADDED:
start some animation or whatever
break;
case AddUserFriendInteractor.REMOVED:
start some animation2 or whatever
break;
case AddUserFriendInteractor.ERROR:
show a toast to the user
break;
case AddUserFriendInteractor.LOGIN_REQUIRED:
start the log in screen with callback to here again
break;
}
}
Example of even more complex call
A BroadcastReceiver on Android or AppDelegate on iOS receive a push notification. This is sent to NotificationHandler which is in the java lib logical layer. In the NotificationHandler constructor which is constructed once in the App.onCreate() it takes a ScreenHandler interface which you implemented in both platforms. This push notification is parsed and the correct method is called in the ScreenHandler to open the correct Activity.
The bottom line is: keep the View as dumb as you can, keep the Activity just smart enough to handle its own life cycle and handle its own views, and communicate with its own controllers (plural!), and everything else should be written (hopefully test-first ;) ) in the java lib.
Using these methods our app currently runs about 60-70% of its code in the java lib, with the next update should take it to the 70-80% hopefully.
I would recommend that you use some kind of slot mechanism. Similar to what other MVP frameworks use.
Definition: A slot is a part of a view where other views can be inserted.
In your presenter you can define as many slots as you want:
GenericSlot slot1 = new GenericSlot();
GenericSlot slot2 = new GenericSlot();
GenericSlot slot3 = new GenericSlot();
These slots must have a reference in the Presenter's view. You can implement a
setInSlot(Object slot, View v);
method. If you implement setInSlot in a view then the view can decide how it should be included.
Have a look at how slots are implemented here.
In cross platform development sharing what I call a "core" (the domain of your application written in Java), I tend to give to the UI ownership of which view to display next. That makes your application more flexible, adapting to the environment as needed (Using a UINavigationController on iOS, Fragments on Android and a single page with dynamic content on the web interface).
Your controllers should not be tied to a view, but instead fulfill a specific role (an accountController for logins/logouts, a recipeController for displaying and editing a recipe, etc).
You would have interfaces for your controllers instead of your views. Then you could use the Factory design pattern to instantiate your controllers on the domain side (your Java code), and the views on the UI side. The view factory has a reference to your domain's controller factory, and uses it to give to the requested view some controllers implementing specific interfaces.
Example:
Following a tap on a "login" button, a homeViewController asks the ViewControllerFactory for a loginViewController. That factory in turn asks the ControllerFactory for a controller implementing the accountHandling interface. It then instantiates a new loginViewController, gives it the controller it just received, and returns that freshly instantiated view controller to the homeViewController. The homeViewController then presents that new view controller to the user.
Since your "core" is environment-agnostic and only contains your domain & business logic, it should remain stable and less prone for edits.
You could take a look at this simplified demo project I made which illustrates this set-up (minus the interfaces).
Related
I am developing a simple application.
The user navigates through the elements of the RecyclerView to the end screen.
When scrolling LastFragment to the very bottom of the fragment, the SharedPref saves the status to true. Then the next element becomes available.
LastFragment has a button that plays a sound when clicked
I'm trying to build an application according to the principles of clean architecture and
I don't understand where UseCase is. Could you explain to me which UseCase should be used here?
You need to have something like:
PersistEndWasReachedUseCase
Or whatever name you like that will call the: "SaveToSharePrefs" Logic.
They should be part of your Domain Layer:
https://developer.android.com/topic/architecture/domain-layer
But at the same time, you need to be clean. This means that your logic should not know where it saves states. It might be a DB, SharePrefs, a remote server, etc.
That is why you should you a repository pattern:
https://developer.android.com/codelabs/basic-android-kotlin-training-repository-pattern#0
https://www.raywenderlich.com/24509368-repository-pattern-with-jetpack-compose
So you need to also have a Data Layer:
https://developer.android.com/topic/architecture/data-layer
But long story short - what clean means - You put your business logic in the inner layers. They depend on interfaces declared in these layers. And then outside layers implement these interfaces.
Basically this is an Inversion of control. Check SOLID.
https://miro.medium.com/max/1400/1*B4LEEv0PbmqvYolUH-mCzw.png
As per the image - you push the implementation details: Room, SharedPrefs, etc in the outer layers. And the inner layers are pure Kotlin/Java code. It knows nothing about implementation details - they are hidden by interfaces.
I am trying to implement MVP architecture using google sample code for mvp. I have an activity as View that has a presenter and model. On button click user can capture and save image in external storage. And on capture click, I also need to play a sound.
I am not sure which code should be put in which class because I cannot put camera capture and play sound code in Activity (which I treat as view) to keep the view as dumb as possible and I cannot put that code in Presenter because it uses Android framework classes (context etc).
So, the only option is to put it in model but in sample code, model only has Repositories (that I believe is relevant to data sources only local/remote).
How to put this code in model and how to link it with other components like View and Presenter? Any guidelines?
Here's one way you can go about solving an issue like that.
Your problem is that some code makes sense to go into a presenter, however it's too Androidy, so you can simply interface around it.
What you basically need is two things that can do certain functions.
interface CapturingSoundPlayer {
void playSound();
}
interface ImageCapturer {
void captureImage();
}
Please note that names and method signatures are up to you and what you need, I am merely using them to make a point.
Now it's perfectly safe for your application to have those two interfaces as dependencies, they have nothing to do with Android, we have simply take then technology out of the equation and only left the behaviour.
You need to pass these dependencies to your presenter and use them whenever needed.
class Presenter {
private final CapturingSoundPlayer soundPlayer;
private final ImageCapturer capturer;
Presenter(CapturingSoundPlayer soundPlayer, ImageCapturer capturer) {
this.soundPlayer = soundPlayer;
this.capturer = capturer;
}
void onCaptureButtonClicked() {
soundPlayer.playSound();
capturer.captureImage();
}
}
Now the implementation for those interfaces can be completely separate from your Activity, making your view still stupid.
Those interfaces (and their implementation) are simply logical units/entities that your presenter uses to split up the logic. I simply think of the functions of "capture" and "playSound" as simple actions, just like view.showLoading, and your presenter plays an orchestrator, it uses "something" to manipulate the view, which is the view interface, and it uses something else to manipulate sound, which is the interface for doing that.
if repositories are considered to be data sources, then in my opinion those helpers don't fit under that definition.
If you have a utility to retrieve the images that has been captured, that would basically can be considered as a repo for your images, but simply taking an image, is just another action.
You can structure/organise them as you feel fit, but just because you are using MVP, not every class you create has to be either one of those letters (M/V/P).
Sometimes any of those layers will need classes that logical units to do something, so that you have better separation of concerns.
Consider you have quite complex formatting logic, it would make sense to separate that in a separate class other than the presenter, but does that mean that the separate class is now model? It could, but it doesn't have to be.
I've read about MVP design pattern and have some question about it.
If we consider Android SDK we can suggest that an Activity is a Presenter which takes over event handling, lifecycle events executing and communication with data-layer which can be a SharedPreferences, SQLlite etc. View in that case is just xml- view description which doesn't contain any event handlers or other user-communication things.
But I'm doubt if my reasoning correct at all? Could you help me to understand?
Android also follow MVC architecture.
1) In Android activity is the controller where you write a code for handling input & response.
2) xml layouts represent the view where you describe the presentation part of the application.
3) & model is your java pojo classes. For instance Person class which has two attributes first name & last name.
Two questions about MVC:
1) What is difference between .net MVC and Android MVC?
2) Why need to use ViewModels in MVC?
I would appreciate if anyone can answer these questions.
reference from this
Model-View-Controller
In the MVC, the Controller is responsible for determining which View is displayed in response to any action including when the application loads. This differs from MVP where actions route through the View to the Presenter. In MVC, every action in the View correlates with a call to a Controller along with an action. In the web each action involves a call to a URL on the other side of which there is a Controller who responds. Once that Controller has completed its processing, it will return the correct View. The sequence continues in that manner throughout the life of the application:
Action in the View
-> Call to Controller
-> Controller Logic
-> Controller returns the View.
One other big difference about MVC is that the View does not directly bind to the Model. The view simply renders, and is completely stateless. In implementations of MVC the View usually will not have any logic in the code behind. This is contrary to MVP where it is absolutely necessary as if the View does not delegate to the Presenter, it will never get called.
MVC is a concept rather than a solid programming framework. You can implement your own MVC in any platforms.
Model: What to render
View: How to render
Controller: Events, user input
Question 1: Android MVC and .Net MVC
Android MVC:
In Android you don't have MVC, but you have the following:
You define your user interface in various XML files by resolution, hardware, etc.
You define your resources in various XML files by locale, etc.
You extend clases like ListActivity, TabActivity and make use of the XML file by inflaters.
You can create as many classes as you wish for your business logic.
A lot of Utils have been already written for you - DatabaseUtils, Html.
More Ref : MVC pattern on Android
.net MVC :
ASP.Net MVC is an open source web application framework that implements the model–view–controller (MVC) pattern.
More Ref : http://en.wikipedia.org/wiki/ASP.NET_MVC_Framework
Question 2: Why need to use ViewModels in MVC?
ViewModel help us to organize and manage data in a strongly-typed view with more flexible way than complex objects like models or ViewBag/ViewData objects.It allow you to shape multiple entities from one or more data models or sources into a single object, optimized for consumption and rendering by the view.
ViewModel contain fields that are represented in the view (for LabelFor,EditorFor,DisplayFor helpers)
ViewModel can have specific validation rules using data annotations or IDataErrorInfo.
ViewModel can have multiple entities or objects from different data models or data source.
More Ref: http://rachelappel.com/use-viewmodels-to-manage-data-amp-organize-code-in-asp.net-mvc-applications
I wonder why all Table Views (also in Android) have to have special object that provide them data ?
https://developer.apple.com/library/ios/documentation/uikit/reference/UITableView_Class/Reference/Reference.html#//apple_ref/occ/instp/UITableView/dataSource
Why Table Views can't be populated from controllers (or Activity class in the case of Android) ?
For example UILabel in iOS can be populated just by setting "text" property
https://developer.apple.com/library/ios/documentation/uikit/reference/UILabel_Class/Reference/UILabel.html#//apple_ref/occ/instp/UILabel/text
Could you explain what is the purpose of such design ?
This is a really great question.
We can break this into two reasons - a general one and a specific one that is a common example of it.
First, though, notice that it's even more than that. A UITableView can have two delegate controller objects, a UITableViewDataSourceDelegate and a UITableViewDelegate. The first provides the data; the second responds to actions taken on the displayed data. Why all these layers of abstraction? Why not just stick it in the UIViewController?
The general reason not to is suggested by the name 'UIViewController'. It's meant to control views. In iOS and I expect in android-land, there is a strong temptation to just 'stick it in the view controller' - leading to the Very Large View Controller antipattern, which you can read about a lot (eg here).
Far better for maintenance and organization for the view controller to lay out views and handle button presses, and some other controller object to handle the well-defined and really quite separate responsibility of providing the data.
It's not uncommon to have some sort of object that handles the data anyway - a shopping cart, or list of real estate properties, etc - that encapsulates additional business logic (providing subtotals, for example) but fundamentally already 'has' the data. In that case, provide a category or a lightweight controller that can 'bridge' between that data and the table view, and pass the model object, instead of an array, between view controllers.
This gets into the specific: in iOS, we have things like NSFetchedResultsController. This is a fantastic class that does a tremendous amount of data sorting, pagination, addition, deletion, slicing, dicing, folding, spindling, and mutilating for you - and it also speaks UITableView's language natively. Pass one of those around and you keep your view controllers clean and tidy and separate from your data.
TableViews do need a special or separate object but they do require an object implement the correct protocol so that it can find the data. In Apple's default implementation, it Uses a UTableViewCotnroller that is a new object responds to these methods as well as does some other things such as making the UITableView the root view of the view controller.
If you do not want to use that object for whatever reason you can still manually add a UITableView to your view controller, then sets its delegate to the view controller. Although it is a good to move it out of the view controller if possible to help keep the view controller small and the data reusable.