https://kotlinlang.org logo
#multiplatform
Title
# multiplatform
r

Radoslaw Juszczyk

01/14/2022, 6:47 AM
Hey guys, I am trying to consume my multiplatform module in swift. But I have problem with classes and objects which are children of a sealed class. It looks more or less like that:
Copy code
interface SomeViewModelDelegate {
    fun consumeState(state: State)
}

sealed class State {
    object Loading: State()
    data class Loaded(
        val data: String,
    ): State()

    data class Error(val errorMessage: String): State()
}
In this case only State is exposed to swift but not Loading, Loaded, Error so I cannot type check nor cast it to these specific types. Any ideas how to get it exposed to swift?
as a workaround I was able to do it this way:
Copy code
interface SomeViewModelDelegate {
    fun consumeStateLoading(state: State.Loading)
    fun consumeStateLoaded(state: State.Loaded)
    fun consumeStateError(state: State.Error)
}
but I am not a fan of it
x

xxfast

01/14/2022, 11:25 AM
this is definitely doable. I have an sealed state like
Copy code
sealed class LoginState : Parcelable {
  abstract val version: String

  data class IdleState(
    override val version: String,
    val inputs: UsernamePasswordState,
  ) : LoginState() {
    // State copy extensions for native clients
    fun doCopyInputs(inputs: UsernamePasswordState) = copy(inputs = inputs)
  }

  @Parcelize
  data class LoadingState(
    override val version: String,
    val step: LoadingStep
  ) : LoginState()
}
which is accessible from swift like
Copy code
switch viewModelDelegate.state {
case let idleState as LoginState.IdleState :
    UsernamePasswordView(
        state: idleState.inputs,
        ...
    )

case let progressState as LoginState.LoadingState:
    switch progressState.step {
    case .checkingcredentials:
        ProgressView(..)
    ..
    }

default: ProgressView(MR.strings().login_loadingDefaultText.localised())
}
try addressing type as
State.Loading
?
a

Anton Afanasev

01/14/2022, 2:29 PM
Sealed classes have some limitations when translated to Swift. But in your case you should be able to use something like:
Copy code
switch state {
case _ as State.Loading:
   // handle loading here
case let loaded as State.Loaded:
   loaded.data.description // operate the `data` as a payload of state Loaded.
default:
    // On kotlin native sealed classes are not exhaustive so `default` case is a must
}
r

Radoslaw Juszczyk

01/14/2022, 2:32 PM
yeah but i dont get these types in the exported pod , I think the issue is that this sealed class is defined in another kmm module than the one which is exported if I have a method fun consume(state:State) only then the child classes are not exported but if I define three separate methods: consumeStateLoading(state: State.Loading), consumeStateLoaded(state: State.Loaded) ... then they got exported correctly
a

Anton Afanasev

01/14/2022, 2:35 PM
Are this states comes from 3-rd party library?
r

Radoslaw Juszczyk

01/14/2022, 2:36 PM
from my kmm module
a

Anton Afanasev

01/14/2022, 2:37 PM
Check your library exported
.h
file if it contains this states. They should be there (unless this states are package private / internal) If they are there, you should be able to work with them.
r

Radoslaw Juszczyk

01/14/2022, 2:47 PM
if i define that sealed interface inside the main kmp module then it is correclty present in swift and .h file if I define a sealed interface in another kmp module which the main module depends on then that sealed interface is not available neither in swift nor in .h file
but if i use that type somewhere in the public api of the main module then it is exported and visible in .h file
a

Anton Afanasev

01/14/2022, 2:51 PM
Just to get an idea of your project structure. • You have a multiplatform public api project with shared/ios/android modules. • Your shared depends on some other multiplatform project which has this sealed states. If so, it might be that you need to export this other module as a bitcode when you build your xcframework.
r

Radoslaw Juszczyk

01/14/2022, 2:51 PM
yesss!
it was exactly that
actually i added export(...) in wrong place I had it inside ios() { binaries.framewrok {} } instead of inside cocoapods { framework }
👍 1
thank you so much for help 🙂
23 Views