
/ Original /
La sintaxis del lenguaje Kotlin es bastante flexible y la concisión del código, que en Java solo se puede lograr con la ayuda de la generación de código, en Kotlin a menudo se implementa utilizando herramientas de lenguaje estándar ( uno , dos ).
, 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, , .
También puedes traducir 1-2 pantallas para probar. Kotlin tiene una muy buena interoperabilidad con Swift, y Swift no importa en qué idioma esté escrito el código. Las clases escritas en Kotlin miran a Swift como si estuvieran escritas en Objective-C con anotaciones de compatibilidad en los lugares correctos.
Conclusión
¡Gracias a todos los que leyeron hasta el final! Espero que hayas aprendido algo nuevo sobre Kotlin por ti mismo, y tal vez incluso pruebes Summer . Estaría muy agradecido por los comentarios constructivos.