
/ Original /
The syntax of the Kotlin language is a rather flexible thing and the conciseness of the code, which in Java can be achieved only with the help of code generation, in Kotlin is often implemented using standard language tools ( one , two ).
, Kotlin ( ), MVP- Summer Kotlin Multiplatform.
MVP ?
Android, Fragment Activity . , , , view view . , . MVP- , view . , , view, .
. , Moxy APT (Annotation Processing Tool). Moxy MVP-. , .
APT?
- Moxy
@InjectViewState
Fragment- , , . Moxy .
e: <...>/kapt3/stubs/debug/adev/TestFragment.java:16: error: cannot find symbol
public TestPresenterFactory presenterFactory;
^
symbol: class TestPresenterFactory
location: class TestFragment
e: <...>/kapt3/stubs/debug/adev/ui/TestFragment.java:22:
error: [ComponentProcessor:MiscError] dagger.internal.codegen.ComponentProcessor was unable to process this class because not all of its dependencies could be resolved. Check for compilation errors or a circular dependency with generated code.
public final class TestFragment extends adev.ui.BaseFragment implements adev.presentation.TestView {
^

, , Dagger , . 4 , , . , .
Fragment . . Clean Project , , .

. — AutoFactory, , .
Dagger. , , . , . APT .
APT Kotlin/MultiplatformKotlin/Multiplatform API . Annotation Processing . API .
?
Android-only APT. Koin/Kodein Dagger, FragmentArgs. Moxy . , Google MVVM, - , , . , , .
Kotlin/JS 2017 . property- VueJS , . : " Fragment/Activity?" . :
interface View {
var counter: Int
}
class Presenter : BasePresenter<View>() {
override fun createViewProxy(view: View) = object : View {
override var counter by state(view::counter, initial = 0)
}
override fun onEnter() {
viewProxy.counter = 1
}
}
view viewProxy
, view. view property delegate, state
. view view . , , view , . Map. view createViewProxy
state
Map .
:
createViewProxy
viewProxy
. . , BottomSheet .- , view.
. "".
№1.
, viewProxy
view. , , . viewProxy
view , , state
, view c .
:
interface View {
var counter: Int
}
class Presenter : BasePresenter<View>() {
override val viewProxy = object : View {
override var counter by state({ ::counter }, initial = 0)
}
override fun onCreate() {
viewProxy.counter = 1
}
}
abstract class BasePresenter<TView> {
fun <T> state(
rule: TView.() -> KMutableProperty0<T>
): StateDelegate<T> {}
}
Kotlin : obj::prop
. :
class A {
var b = 1
val prop = ::b
}
: ", , this
receiver- !".
class A {
var b = 1
}
fun A.foo() {
val prop = ::b
}
. ! !
:
interface View {
var counter: Int?
}
class Presenter : BasePresenter<View>() {
override val viewProxy = object : View {
override var counter by state({ ::counter }, initial = null)
}
}
...
e: <...>/adev/presentation/Presenter.kt: (7, 22): Type of 'counter' doesn't match the type of the overridden var-property 'public abstract var counter: Int? defined in adev.presentation.View'

, Android Studio . . ? , frontend Kotlin, , viewProxy
, , IDE, , , this
, state
. , counter
::counter
, this::counter
, .
view : this
.
abstract class BasePresenter<TView> {
fun <T> state(
rule: (TView) -> KMutableProperty0<T>
): StateDelegate<T> {}
}
:
interface View {
var counter: Int?
}
class Presenter : BasePresenter<View>() {
override val viewProxy = object : View {
override var counter by state({ it::counter }, initial = null)
}
}
. . , view . , viewProxy
Intellij IDEA/Android Studio.
: ?
:
. . .object View {
interface State {
var counter: Int
}
interface Methods {
fun showMessage(message: String)
}
}
class Presenter : BasePresenter<View.State, View.Methods>() {
override val viewStateProxy = object : View.State {
override var counter by state({ it::counter }, initial = 0)
}
override fun onEnter() {
viewStateProxy.counter = 1
viewMethods?.showMessage("Hello!")
}
}
, viewMethods
. , . :
. . .object View {
interface State {
var counter: Int
}
interface Methods {
fun showMessage(message: String)
}
}
interface Router {
fun toNext()
}
class Presenter : BasePresenter<View.State, View.Methods, Router>() {
override val viewStateProxy = object : View.State {
override var counter by state({ it::counter }, initial = 0)
}
override fun onEnter() {
viewStateProxy.counter = 1
viewMethods?.showMessage("Hello!")
router.toNext()
}
}
, , . , , . - . : Dagger, Retrofit Moxy.
Moxy . , view.
AddToEndSingleStrategy
view.
OneExecutionStrategy
, , view .
SkipStrategy
.
AddToEndSingleStrategy
Summer state
. .
, Kotlin , , .
interface View {
var counter: Int?
fun showMessage()
}
class Presenter : BasePresenter<View>() {
override val viewProxy = object : View {
override var counter by state({ it::counter }, initial = null)
override fun showMessage = event { it::showMessage }
}
}
abstract class BasePresenter<TView> {
fun event(
getAction: (TView) -> (() -> Unit)
) = Event(getAction)
}
class Event<TView>(
getAction: (TView) -> (() -> Unit)
) : () -> Unit
. :
interface View {
var counter: Int?
}
interface Events {
fun showMessage(message: String)
}
class Presenter : BasePresenter<View>() {
override val viewProxy = object : View {
override var counter by state({ it::counter }, initial = null)
}
val showMessage = event { it::showMessage }
}
, View . , , .
?
- -, — .
- . -, .
- .
– . - , . , , , . , . Kotlin , - . . , , , . , .
- . ?
Kotlin:
override fun showMessage(message: String) {
Toast.makeText(requireContext(), "message", Toast.LENGTH_LONG).show()
}
Kotlin:
override val showMessage = { message: String ->
Toast.makeText(requireContext(), "message", Toast.LENGTH_LONG).show()
}
Swift:
fun showMessage(message: String) {
printMessage(message)
}
Swift:
lazy var showMessage: (String) -> Void = { message in
self.printMessage(message)
}
(つ✧ω✧)つ
override val showMessage = { message: String -> Unit in
printMessage(message)
}
- . , . 5-6 , , .
№2.
view, . :
interface View {
val showMessage: () -> Unit
}
class Presenter : BasePresenter<View>() {
override val viewProxy = object : View {
override val showMessage = doOnlyWhenAttached { it.showMessage }
}
}
abstract class BasePresenter<TView> {
fun doOnlyWhenAttached(
getAction: (TView) -> (() -> Unit)
) = Event(getAction)
}
class Event<TView>(
action: (TView) -> (() -> Unit)
) : () -> Unit
. , Kotlin . vararg
, . view.
interface View {
val showMessage: (message: String) -> Unit
}
class Presenter : BasePresenter<View>() {
override val viewProxy = object : View {
override val showMessage = doOnlyWhenAttached { it.showMessage }
}
}
abstract class BasePresenter<TView> {
fun doOnlyWhenAttached(
getAction: (TView) -> (() -> Unit)
) = Event0(getAction)
fun <T1> doOnlyWhenAttached(
getAction: (TView) -> ((T1) -> Unit)
) = Event1(getAction)
}
class Event0<TView>(
action: (TView) -> (() -> Unit)
) : () -> Unit
class Event1<TView, T1>(
action: (TView) -> ((T1) -> Unit)
) : (T1) -> Unit
e: <...>/adev/presentation/View.kt: (10, 22): Type of 'showMessage' is not a subtype of the overridden property 'public abstract val showMessage: (message: String) -> Unit defined in adev.presentation.View'

Kotlin , . , Kotlin , . doOnlyWhenAttached
TView
, T1
. , .
"" – Event
2 :
TView
Event
interface View {
val showMessage: (message: String) -> Unit
}
class Presenter : BasePresenter<View>() {
override val viewProxy = object : View {
override val showMessage = event { it.showMessage }.doOnlyWhenAttached()
}
}
abstract class BasePresenter<TView> {
fun <TAction> event(getAction: (TView) -> TAction) = EventBuilder(getAction)
fun EventBuilder<TView, () -> Unit>.doOnlyWhenAttached() {
return Event0(this.getAction)
}
fun <T1> EventBuilder<TView, (T1) -> Unit>.doOnlyWhenAttached() {
return Event1(this.getAction)
}
}
inline class EventBuilder<TView, TAction>(
val getAction: (TView) -> TAction
)
class Event0<TView>(
action: (TView) -> (() -> Unit)
) : () -> Unit
class Event1<TView, T1>(
action: (TView) -> ((T1) -> Unit)
) : (T1) -> Unit
. shorthands . :
interface View {
val showMessage: (message: String) -> Unit
val showMessage1: (message: String) -> Unit
val showMessage2: (message: String) -> Unit
}
class Presenter : BasePresenter<View>() {
override val viewProxy = object : View {
override val showMessage = event { it.showMessage }.build(RepeatLastStrategy())
override val showMessage1 = event { it.showMessage1 }.doOnlyWhenAttached()
override val showMessage2 = event { it.showMessage2 }.doExactlyOnce()
}
}
, !
. , 30. , , production. 2- . , , !
Summer 5 production . 2 – . MaksimNovikov, metnyov deks1309 , . XanderZhu umpteenthdev mowenik iOS .
, Summer, . :
android.widget.View, UIView . , - . , . , .
interface View {}
class Presenter : SummerPresenter<View> {
override val viewProxy = object : View {}
val subPresenter = SubPresenter(
onAction = { doSomething() }
)
}
class Fragment : SummerFragment(R.layout.fragment), View {
private val presenter by bindPresenter { Presenter() }
private lateinit var popup: Popup
override fun onViewCreated(...) {
super.onViewCreated(...)
popup = Popup(presenter.subPresenter)
}
}
interface SubView {
var counter: Int
}
class SubPresenter(
private val onAction: () -> Unit
): SummerPresenter<SubView>() {
override val viewProxy = object : SubView {
override var counter by state({ it::counter }, initial = 0)
}
}
class Popup(
private val presenter: SubPresenter
) : <...>, SubView {
init {
bindPresenter { presenter }
}
}
- VueJS ReactJS.
sealed State
AddToEndSingleTagStrategy
Moxy, Summer state
sealed
.
sealed class ViewState {
object Loading : ViewState()
class Content(val counter) : ViewState()
}
interface View {
var state: ViewState
}
class Presenter : SummerPresenter<View>() {
override val viewProxy = object : View {
override var state by state({ it::state }, initial = ViewState.Loading)
}
}
MaksimNovikov .
didSet in Kotlin
Kotlin set
. didSet
didSetNotNull
, didSet
Swift.
:
data class Item(val id: Int, val count: Int)
interface View {
var items: List<Item>
}
class Presenter : SummerPresenter<View>() {
override val viewProxy = object : View {
override var items by state({ it::items }, initial = emptyList())
}
fun increment(id: Int) {
viewProxy.items = viewProxy.items.map { item ->
if (item.id == id)
item.copy(count = item.count + 1)
else
item
}
}
}
, view, . , state
, store
view .
Summer Kotlin/Multiplatform :
Kotlin/Multiplatform . iOS-, , , . , , . iOS 0. , .
Summer iOS-. XCode. Swift. Kotlin XCode TouchLab. , , iOS-. VIPER, Clean Swift .. MVP.
, Kotlin. Summer , unsafe casts , Kotlin . , , — iOS Swift view. .
, , APT, Summer, .
, viewProxy
boilerplate.
interface View {
var counter: Int
}
class Presenter : SummerPresenter<View>() {
override val viewProxy = object : View {
override var state by state({ it::counter }, initial = 0)
}
}
{ it::counter }
. , . , - . , lint .
viewstate
, , Swift. Kotlin didSet
didSetNotNull
view. Swift view . , , . nil
. — , , .
, , state
initial
, state
, event
RepeatLastStrategy
init
.
, Moxy Summer Summer Moxy . 15-20 . iOS Android- Summer 3-4 . , VIPER. ViewController-, iOS- . , Kotlin. iOS, , .
You can also translate 1-2 screens to try. Kotlin has a pretty good interop with Swift, and Swift doesn't matter which language the code is written in. Classes written in Kotlin look to Swift as if they were written in Objective-C with compatibility annotations put in the right places.
Conclusion
Thanks to everyone who read to the end! I hope you learned something new about Kotlin for yourself, and maybe even try Summer . I would be very grateful for the constructive feedback.