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

nirazo

02/26/2021, 3:35 AM
Hello! On my project, I make UseCases in Kotlin and execute UseCase’s  
execute
 method from APP to get value. The return values from UseCases are defined as Results, and puts it in Output of UseCase, and returns it to my APP. Define of Results:
Copy code
sealed class Results<out T, out S> {
    class Success<out T>(val data: T) : Results<T, Nothing>()
    class Failure<out S>(val cause: S) : Results<Nothing, S>()
}
Sample useCase and it’s IO:
Copy code
interface SampleUseCaseInterface {
    suspend fun execute(): sampleUseCaseIO.Output
}

class sampleUseCase() : SampleUseCaseInterface {
    override suspend fun execute(): sampleUseCaseIO.Output =
        sampleUseCaseIO.Output(Results.Success("success!"))
}

class sampleUseCaseIO {
    data class Output(val results: Results<String, Error>)
}
Now, I want to mock the execute method of UseCase and return an arbitrary Output in order to write a Unit Test in an iOS app. However, now the following error is displayed and it is not possible to create an arbitrary output. Both data and error are required to be included in Results, and the required Output cannot be created. Is there any way to avoid this? Also, is it possible to realize a mechanism like Either on the iOS side with Kotlin/Native?
y

Yev Kanivets

02/26/2021, 5:57 AM
Result
seems to use
inline
classes, which aren’t well supported by testing/mocking libraries and objc-c interop when used with KMP. So we deprecated use of
Result
in KMP code, and introduced own own substitute in our team.
👌 1
c

Cicero

02/26/2021, 7:20 AM
I tried using this railways system to "either" you can use it bu it will be weird
n

nirazo

02/26/2021, 10:47 AM
Oh, Result is not well supported.. 😢 @Yev Kanivets Would you please tell me what the substitute would be?
y

Yev Kanivets

02/26/2021, 10:48 AM
Just your own sealed class with different name,
Outcome
for example.
And there you can copy/paste methods from
Result
, which you need.
a

Anders

03/03/2021, 8:59 PM
The crux of the issue you're experiencing with your
Results
is perhaps that Swift does not support covariance and contravariance at all, despite them being present in Objective-C.
i.e., Swift doesn't consider
Results<Nothing, MyFailure>
and
Results<MyData, Nothing>
to be subtypes of
Results<MyData, MyFailure>
, despite it working in Kotlin and Objective-C since they both support covariance. For your
Results
type to work in Swift, you will likely need to change the
T
and
S
type parameters to be invariant instead of covariant. You can also try to do a
as!
force cast, assuming the Swift compiler won't stop you with its naunces around ObjC lightweight generics.
n

nirazo

03/22/2021, 11:52 AM
@Yev Kanivets @Anders I’m sorry for my late reply, and thank you for your advise. I finally defined Results as below.
Copy code
sealed class Results<out T, out S> {
    class Success<out T, out S>(val data: T) : Results<T, S>()
    class Failure<out T, out S>(val cause: S) : Results<T, S>()
}
In my unit test code on iOS, I can make stub for object for success result as below.
Copy code
let success = ResultsSuccess<ObjectForSuccess, ObjectForError>(data: someData)
2
c

Cicero

03/22/2021, 2:36 PM
Using somethig like:
Copy code
sealed class Either<out L, out R> {
    data class Left<out L>(val value: L) : Either<L, Nothing>()
    data class Right<out R>(val value: R) : Either<Nothing, R>()

    val isRight get() = this is Right<R>
    val isLeft get() = this is Left<L>

    fun <L> left(a: L) = Left(a)
    fun <R> right(b: R) = Right(b)
}

fun <L, R, T> Either<L, R>.fold(left: (L) -> T, right: (R) -> T): T =
    when (this) {
        is Either.Left -> left(value)
        is Either.Right -> right(value)
    }

fun <L, R, T> Either<L, R>.flatMap(f: (R) -> Either<L, T>): Either<L, T> =
    fold({ this as Either.Left }, f)

fun <L, R, T> Either<L, R>.map(f: (R) -> T): Either<L, T> =
    flatMap { Either.Right(f(it)) }
Doesn’t translate well to Objective-C, maybe I’m going to look at your approach 🙂
8 Views