class in kotlin - LiveData.kt
/**
* Created by Aleksey Mikhailov <Aleksey.Mikhailov@icerockdev.com> on 09.07.2018.
*/
expect open class LiveData<T> {
open val value: T
fun <OT> map(function: (T) -> OT): LiveData<OT>
fun <OT> flatMap(function: (T) -> LiveData<OT>): LiveData<OT>
fun addObserver(observer: (T?) -> Unit)
fun removeObserver(observer: (T?) -> Unit)
}
after compilation we get - MultiplatrformFramework.h
__attribute__((swift_name("LiveData")))
@interface MPLLiveData : KotlinBase
- (instancetype)initWithInitialValue:(id _Nullable)initialValue __attribute__((swift_name("init(initialValue:)"))) __attribute__((objc_designated_initializer));
- (void)addObserverObserver:(MPLKotlinUnit *(^)(id _Nullable))observer __attribute__((swift_name("addObserver(observer:)")));
- (void)addObserverObserver_:(id<MPLLiveDataObserver>)observer __attribute__((swift_name("addObserver(observer_:)")));
- (void)changeValueValue:(id _Nullable)value __attribute__((swift_name("changeValue(value:)")));
- (MPLLiveData *)flatMapFunction:(MPLLiveData *(^)(id _Nullable))function __attribute__((swift_name("flatMap(function:)")));
- (MPLLiveData *)mapFunction:(id _Nullable (^)(id _Nullable))function __attribute__((swift_name("map(function:)")));
- (void)removeObserverObserver:(MPLKotlinUnit *(^)(id _Nullable))observer __attribute__((swift_name("removeObserver(observer:)")));
- (void)removeObserverObserver_:(id<MPLLiveDataObserver>)observer __attribute__((swift_name("removeObserver(observer_:)")));
@property (readonly) id _Nullable value;
@end;
__attribute__((swift_name("LiveDataObserver")))
@protocol MPLLiveDataObserver
@required
- (void)valueChangedLiveData:(MPLLiveData *)liveData value:(id _Nullable)value __attribute__((swift_name("valueChanged(liveData:value:)")));
@end;
for use in swift with types we cast via extensions - MPMLiveData+Bind.swift
class LiveObserverTyped<T>: NSObject, LiveDataObserver, RemovableObserver {
var closure: ((T?) -> Void)?
var mapping: ((Any?) -> T?)?
weak var liveData: LiveData?
func valueChanged(liveData: LiveData, value: Any?) {
self.liveData = liveData
closure?(mapping?(value) ?? (value as? T))
}
func removeSelfFromObservers() {
liveData?.removeObserver(observer_: self)
}
init(closure: ((T?) -> Void)?, _ mapping: ((Any?) -> T?)? = nil) {
self.closure = closure
self.mapping = mapping
}
}
another casts - LiveData+typed.swift
extension LiveData {
func type<T>(_ type: T.Type, action: @escaping (T) -> Void) -> LiveDataObserver {
guard let data = value as? T else { abort() }
action(data)
let observer = LiveObserverTyped<T>(closure: { data in
guard let data = data else { abort() }
action(data)
})
addObserver(observer_: observer)
return observer
}
}
in code usage:
stateLiveData.type(MultiPlatformLibrary.State.self, action: { state in
dataView.isHidden = !(state.isSuccess())
emptyView.isHidden = !(state.isEmpty())
loadingView.isHidden = !(state.isLoading())
errorView.isHidden = !(state.isError())
})
if in kotlin we change type of generic - swift side not fail at compilation. it fail in runtime.
in kotlin we have:
sealed class State<out T, out E> {
data class Data<out T, out E>(val data: T) : State<T, E>()
data class Error<out T, out E>(val error: E) : State<T, E>()
class Loading<out T, out E> : State<T, E>()
class Empty<out T, out E> : State<T, E>()
fun isLoading(): Boolean = this is Loading
fun isSuccess(): Boolean = this is Data
fun isEmpty(): Boolean = this is Empty
fun isError(): Boolean = this is Error
fun dataValue(): T? = (this as? Data)?.data
fun errorValue(): E? = (this as? Error)?.error
}
but we not want to create instances at swift side. all creating at kotlin side. but when we have in kotlin public api this:
val state: LiveData<State<String, Throwable>>
we want to use it from swift without casts & with compiler types checks (if we change in kotlin String
to Int
- swift side at compilation should fail)