Last active
July 30, 2023 16:25
-
-
Save saket/62b1ccfc1a92c393be60a1cfd5357d98 to your computer and use it in GitHub Desktop.
SwiftUI with Reaktive presenters written with Kotlin Multiplatform
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import Combine | |
import Common // Code shared through Kotlin Multiplaform. | |
import CombineExt // https://github.com/CombineCommunity/CombineExt | |
/// A convenience pass-through View to hide away the verbosity | |
/// of subscribing to a presenter's stream. Usage: | |
/// | |
/// struct FooView: View { | |
/// let presenter: FooPresenter | |
/// | |
/// var body: some View { | |
/// Present(presenter) { model -> | |
/// Text(model.name) | |
/// } | |
/// } | |
/// } | |
struct Present<Content: View, Event: AnyObject, Model: AnyObject>: View { | |
@State var models: AnyPublisher<Model, Never> | |
@State var currentModel: Model | |
private let content: (Model) -> Content | |
// Presenter is an interface that provides a stream of View models. | |
public init( | |
_ presenter: Presenter<Event, Model>, | |
@ViewBuilder content: @escaping (Model) -> Content | |
) { | |
self.content = content | |
self.currentModel = presenter.initialModel() | |
self.models = ReaktiveInterop.toCombinePublisher(reaktive: presenter.viewModels()) | |
.assertNoFailure() | |
.receive(on: RunLoop.main) | |
.eraseToAnyPublisher() | |
} | |
var body: some View { | |
// onReceive() will manage the lifecycle of this stream. | |
content(currentModel).onReceive(models) { model in | |
self.currentModel = model | |
} | |
} | |
} | |
class ReaktiveInterop { | |
static func toCombinePublisher<T>(reaktive: ObservableWrapper<T>) -> AnyPublisher<T, ReaktiveError> { | |
return AnyPublisher.create { (subscriber: Publishers.Create.Subscriber) in | |
let reaktiveDisposable = reaktive.subscribe( | |
isThreadLocal: false, | |
onSubscribe: nil, | |
onError: { e in | |
e.printStackTrace() | |
subscriber.send(completion: .failure(ReaktiveError(throwable: e))) | |
}, | |
onComplete: { | |
subscriber.send(completion: .finished) | |
}, | |
onNext: { value in | |
subscriber.send(value) | |
} | |
) | |
return AnyCancellable { | |
reaktiveDisposable.dispose() | |
} | |
} | |
} | |
public struct ReaktiveError: Error { | |
let throwable: KotlinThrowable | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment