71

Shared client logic in multiplatform Kotlin project

 6 years ago
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.

0*FmBvyOm4ZgOWtBJC.

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:

1*FLfFp81mxH1UsjIrtt_4_w.gif
1*UzwbChSUMAi8yFpP8bqFWA.gif

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)

1*1FXqU6ZaLj6clmy4N3Xxhg.png

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:

1*kmXnsgzMv9LsERla3Tw2EQ.png

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:

1*QTVVeTKDOpxRhl3ziGTv1A.png

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:

1*n24Gktwnvy9MgbIk8aXWyA.png

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:

1*e2rd3gWMx-2nFL604wVeYw.png

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:

1*Q5qZZERGZ_TJG12z5kNGyw.png

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:

1*RDIa9cFH56hjVzgKBsUF7A.png

BasePresenter abstract class in common-client module

Where onDestroy is one of two life-cycle methods of Presenter:

1*IoiTkzJypwZKAz79Vx13Zg.png

Presenter interface in common-client module

1*K-f1laplrjQQAMlYKfLgHw.png

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:

1*0m2S8zSBTTdgacASq6ixsg.png

FeedbackRepository in common-client module

1*yhOVxdykvIliUpblgeBPtA.png

NewsRepository in common-client module

1*SpJ3ZUNjvQgSxrc1Ln1RiA.png

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:

1*HISF81ZdN9R7oDF2tN97QQ.png

RepositoriesProvider expected declaration in common-client module

1*B46TJI_UIuI14u5tJhKjOQ.png

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:

1*oahjtbEoUkusggvERKxIIQ.png

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:

1*qgA2Bp_B11sL1feElrObpw.png

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:

1*GUa9pXFVFz9Mii8vfuqj2Q.png

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:

1*mVTY8V0sgvFOkl3Xy9mn7Q.png

RegisterNotificationTokenPresenter in common-client module

And view declaration:

1*D17_mbZMYYxBe5loyUbS3Q.png

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.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK