Shared client logic in multiplatform Kotlin project
source link: https://blog.kotlin-academy.com/shared-client-logic-in-multiplatform-kotlin-project-2509bc36ff51?gi=1461a294023d
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Shared client logic in multiplatform Kotlin project
We’ve already presented the architecture for multiplatform Kotlin projects. Its key idea is how we define clients logic so it can be shared with all the clients. Here we will describe it in Kt. Academy application example.
In this part, we will describe common-client part of Kot. Academy application project. Next articles about Android, web, desktop, iOS, and others will strongly reference to this article.
The common module was already described in the article about whole architecture and in the article about extracting common modules in Android project.
Key objectives of this article are to:
- Give you knowledge what are presenters are and what is their purpose. Note that description of every presenter starts from a concrete set of responsibilities and a unit test that covers each of them. Those are specific business rules that those presenters satisfy.
- Give you understanding how presenters can be defined in the common-client module.
- Present and describe the common-client module so in the next articles we can base on this knowledge and describe how we can define Android, web, desktop or iOS in multiplatform Kotlin project.
You might have seen how this example application looks. Here is a demo of Android and web client:
The application currently realizes following functionalities:
- You can see all Kot. Academy posts
- You can add comments to news or to concrete posts
- Application is sending push notifications
These 3 core functionalities are implemented in 3 separate presenters: NewsPresenter
, FeedbackPresenter
and RegisterNotificationTokenPresenter
.
We will describe one after another (in a different order).
FeedbackPresenter
Feedback presenter is used in following view on web: (if you fill this form, I will get an email with your feedback)
It is really simple, because it only decides how to react when “Send” button is clicked. Its business logic rules are following:
- Send all data provided in form
- When sending feedback, the loader is displayed
- When repository returns error, it is shown on view
- After data are sent, view switches back to news list
Check out all these rules defined as unit tests for the presenter.
Whole this logic is implemented in the following implementation:
FeedbackPresenter in common-client module
As we can see, when onSendCommentClicked
is called, we set loading
to true
(display loading indicator), use commentRepository
to add the comment and call backToNewsAndShowSuccess
to get back to previous activity. When repository throws error we catch it and display on view. We always finish this method with hiding loading indicator.
FeedbackPresenter
uses FeedbackView
interface that represents view:
FeedbackView in common-client module
All views that display feedback form (page, Activity etc) needs to implement it (in next articles we will see it on more concrete examples). All the client’s views need to implement BaseView
:
BaseView interface in common-client module
Majority of onSendCommentClicked
methods is in launchUI
block. This block is used to launch scope in a coroutine. launchUI
is defined in Kt. Academy project, because coroutines are not yet supporting common modules. It is defined as expected declaration:
launchUI expected declaration in common-client module
We can treat it like JVM method launch(UI)
where UI
is UI thread of the application. In JS it uses just async
function because there is no such thing as main thread on web. Check out concrete implementation for JVM and JS, but have in mind that they will change soon and that we are going to publish the whole article about using coroutines in the common modules.
launch(UI)
returns Cancellable
:
Cancellable interface in common-client module
Thanks to that, we can cancel task when user leaves screen. This is very important, because this way we prevent data leaks and errors when presenter tries to call methods of Activity that is already destroyed. Jobs cancellation happen automatically for every class that extends BasePresenter
:
BasePresenter abstract class in common-client module
Where onDestroy
is one of two life-cycle methods of Presenter
:
Presenter interface in common-client module
Repositories
FeedbackPresenter
uses FeedbackRepository
to send feedback. Similarly NewsPresenter
uses NewsRepository
and RegisterNotificationTokenPresenter
uses NotificationRepository
. Concrete implementation of this repositories needs to be defined separately for each platform, but their interfaces are defined in the common-client module:
FeedbackRepository in common-client module
NewsRepository in common-client module
NotificationRepository in common-client module
They are injected to presenters using lightweight Dependency Injection substitution (although it will be probably replaced soon with Kodein). They are all created using RepositoriesProvider
expected declaration which provides platform-specific implementations in platform modules:
RepositoriesProvider expected declaration in common-client module
RepositoriesProvider actual declaration in common-client-js module and in common-client-jvm module
Concrete implementation uses platform-specific methods to make HTTP requests, and to serialize/deserialize data.
NewsPresenter
Next presenter to describe is NewsPresenter
. It controls views used to display news. Its business logic rules are following:
- After view is created, it loads and displays list of news. During that news loading, the loader is being displayed.
- When user request refreshes, news are loaded. During that refresh, refresh indicator is being displayed.
- News are refreshed quietly every 60 seconds.
- News are displayed in the descending occurrence order.
- When any news loading returns an error, loit is displayed.
Check out unit tests of this presenter.
All this logic is realized by the following implementation:
NewsPresenter in common-client module
During onCreate
phrase it displays loading, refreshes list and starts periodic refresh. Periodic refresh uses periodicCaller
to refresh every 60 s. Refresh is async job (so it is canceled when user leaves screen), and it gets news using newsRepository
, sorts them and if they changed then displays them. After all it hides both loading and refresh (if they are not shown then it does nothing). When uses request refresh (onRefresh
), refresh indicator is shown (view.refresh = true
) and refreshList
is called.
This is how we represent view that is controlled by this presenter:
NewsView in common-client module
NewsPresenter
uses PeriodicCaller
class. It has single responsibility — to call function from argument every concrete period of time. It is also tested separately. Here is its implementation:
PeriodicCaller in common-client module
Note that it is also injected using Provider
. This is why it can be easily mocked in unit tests. This shows how concrete functionalities can be extracted from presenters so they can be easily reused and tested separately. Thanks to the fact that it is extracted, we can mock it in NewsPresenter
tests so we don’t need to deal with time there.
RegisterNotificationTokenPresenter
The last presenter in the project (at the moment) is RegisterNotificationTokenPresenter
. It is used to send Firebase notification token to backend. It is tricky part because there is no single view used for sending token. It happens in the background. There are also different mechanisms used to get this token for Android and web. Those are the presenter responsibilities:
- Correctly sends token to backend
- When backend returns error, it is logged
- It is known when token is correctly registered
Check out unit tests of this presenter. Here is its implementation:
RegisterNotificationTokenPresenter in common-client module
And view declaration:
RegisterNotificationTokenView in common-client module
Note that this interface does not implement BaseView
. It is because we don’t need to implement unused showError
, and it is not provided by platform-specific base view because this view is not really displayed.
Except that, RegisterNotificationTokenPresenter
works similarly as FeedbackPresenter
. It is more interesting how it works, but this will be described in details in articles dedicated to Android, iOS and web.
Summary
Now you know how the common-client module works in Kt. Academy application. You should understand how generally such presenters should be defined and how they can be used.
To go deeper into presenters purpose and usage, check out this presentation. If you haven’t done it yet, you should also read the article about whole architecture for multiplatform development.
If you want to check yourself then you can contribute to this project. There are still a lot of tasks and a lot of clients to be implemented.
In next articles, we will describe how presenters from common-client can be used in Android, web, desktop, iOS and on other platforms. Subscribe if you don’t want to miss them.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK