MVP musim panas. Seberapa fleksibel Kotlin?


/ Asli /


Sintaksis bahasa Kotlin adalah hal yang agak fleksibel dan keringkasan kode, yang di Jawa hanya dapat dicapai dengan bantuan pembuatan kode, di Kotlin sering diimplementasikan menggunakan alat bahasa standar ( satu , dua ).


, Kotlin ( ), MVP- Summer Kotlin Multiplatform.


MVP ?


Android, Fragment Activity . , , , view view . , . MVP- , view . , , view, . . , Moxy APT (Annotation Processing Tool). Moxy MVP-. , .


APT?


  1. Moxy
  2. @InjectViewState Fragment
  3. , , . 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/Multiplatform

Kotlin/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 .


:


  1. createViewProxy viewProxy. . , BottomSheet .
  2. , 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 . , , .


?


  1. -, — .
  2. . -, .
  3. .

– . - , . , , , . , . 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 :


  1. TView
  2. 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 .


view

state , , 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, , .


Anda juga dapat menerjemahkan 1-2 layar untuk mencoba. Kotlin memiliki interop yang cukup bagus dengan Swift, dan Swift tidak terlalu peduli dalam bahasa apa kode ini ditulis. Kelas-kelas yang ditulis di Kotlin terlihat Swift seolah-olah mereka ditulis dalam Objective-C dengan penjelasan kompatibilitas diletakkan di tempat yang tepat.


Kesimpulan


Terima kasih kepada semua orang yang membaca sampai akhir! Saya harap Anda belajar sesuatu yang baru tentang Kotlin untuk diri sendiri, dan mungkin bahkan mencoba Musim Panas . Saya akan sangat berterima kasih atas umpan balik yang membangun.


All Articles