Combine publisher to flow mapping
This commit is contained in:
parent
b0693cf381
commit
6701e51b45
5 changed files with 89 additions and 32 deletions
|
@ -16,6 +16,7 @@ import kotlinx.coroutines.flow.flowOf
|
|||
import kotlinx.coroutines.flow.*
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koin.core.inject
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
|
||||
class PeopleInSpaceRepository() : KoinComponent {
|
||||
|
@ -82,21 +83,6 @@ class PeopleInSpaceRepository() : KoinComponent {
|
|||
}
|
||||
|
||||
|
||||
fun startObservingISSPosition(success: (IssPosition) -> Unit) {
|
||||
logger.d { "startObservingISSPosition" }
|
||||
issPositionJob = coroutineScope.launch {
|
||||
pollISSPosition().collect {
|
||||
success(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun stopObservingISSPosition() {
|
||||
logger.d { "stopObservingISSPosition, peopleJob = $issPositionJob" }
|
||||
issPositionJob?.cancel()
|
||||
}
|
||||
|
||||
|
||||
fun pollISSPosition(): Flow<IssPosition> = flow {
|
||||
while (true) {
|
||||
val position = peopleInSpaceApi.fetchISSPosition().iss_position
|
||||
|
@ -106,8 +92,32 @@ class PeopleInSpaceRepository() : KoinComponent {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
val iosScope: CoroutineScope = object : CoroutineScope {
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = SupervisorJob() + Dispatchers.Main
|
||||
}
|
||||
|
||||
fun iosPollISSPosition() = KotlinNativeFlowWrapper<IssPosition>(pollISSPosition())
|
||||
|
||||
|
||||
companion object {
|
||||
private const val POLL_INTERVAL = 10000L
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class KotlinNativeFlowWrapper<T>(private val flow: Flow<T>) {
|
||||
fun subscribe(
|
||||
scope: CoroutineScope,
|
||||
onEach: (item: T) -> Unit,
|
||||
onComplete: () -> Unit,
|
||||
onThrow: (error: Throwable) -> Unit
|
||||
) = flow
|
||||
.onEach { onEach(it) }
|
||||
.catch { onThrow(it) }
|
||||
.onCompletion { onComplete() }
|
||||
.launchIn(scope)
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
1ABFB8C423AFF5CE003D807E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1ABFB8C323AFF5CE003D807E /* Assets.xcassets */; };
|
||||
1ABFB8C723AFF5CE003D807E /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1ABFB8C623AFF5CE003D807E /* Preview Assets.xcassets */; };
|
||||
1ABFB8CA23AFF5CE003D807E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1ABFB8C823AFF5CE003D807E /* LaunchScreen.storyboard */; };
|
||||
1AC2439025A1D57700F17D2F /* IssPositionPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AC2438F25A1D57700F17D2F /* IssPositionPublisher.swift */; };
|
||||
1E1255057DE614781855FC02 /* libPods-PeopleInSpaceSwiftUI.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 28681F6577A67864E2C2D9B4 /* libPods-PeopleInSpaceSwiftUI.a */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
|
@ -33,6 +34,7 @@
|
|||
1ABFB8C623AFF5CE003D807E /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||
1ABFB8C923AFF5CE003D807E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
1ABFB8CB23AFF5CE003D807E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
1AC2438F25A1D57700F17D2F /* IssPositionPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssPositionPublisher.swift; sourceTree = "<group>"; };
|
||||
28681F6577A67864E2C2D9B4 /* libPods-PeopleInSpaceSwiftUI.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-PeopleInSpaceSwiftUI.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
56CD7101BBBC60F561BFB049 /* Pods-PeopleInSpaceSwiftUI.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PeopleInSpaceSwiftUI.release.xcconfig"; path = "Target Support Files/Pods-PeopleInSpaceSwiftUI/Pods-PeopleInSpaceSwiftUI.release.xcconfig"; sourceTree = "<group>"; };
|
||||
E9A29AFE5FFFB564C509840A /* Pods-PeopleInSpaceSwiftUI.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PeopleInSpaceSwiftUI.debug.xcconfig"; path = "Target Support Files/Pods-PeopleInSpaceSwiftUI/Pods-PeopleInSpaceSwiftUI.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
|
@ -99,6 +101,7 @@
|
|||
1ABFB8C523AFF5CE003D807E /* Preview Content */,
|
||||
1ABD44F923B00008008387E3 /* ViewModel.swift */,
|
||||
1AA82EEC25052E1E00193051 /* ImageView.swift */,
|
||||
1AC2438F25A1D57700F17D2F /* IssPositionPublisher.swift */,
|
||||
);
|
||||
path = PeopleInSpaceSwiftUI;
|
||||
sourceTree = "<group>";
|
||||
|
@ -219,6 +222,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1AA82EED25052E1E00193051 /* ImageView.swift in Sources */,
|
||||
1AC2439025A1D57700F17D2F /* IssPositionPublisher.swift in Sources */,
|
||||
1ABFB8BE23AFF5CC003D807E /* AppDelegate.swift in Sources */,
|
||||
1ABFB8C023AFF5CC003D807E /* SceneDelegate.swift in Sources */,
|
||||
1ABD44FA23B00008008387E3 /* ViewModel.swift in Sources */,
|
||||
|
|
|
@ -8,25 +8,24 @@ struct ContentView: View {
|
|||
var body: some View {
|
||||
NavigationView {
|
||||
VStack {
|
||||
let issPosition = String(format: "ISS Position = (%f, %f)", peopleInSpaceViewModel.issPosition.latitude, peopleInSpaceViewModel.issPosition.longitude )
|
||||
let issPosition = peopleInSpaceViewModel.issPosition
|
||||
let issPositionString = String(format: "ISS Position = (%f, %f)", issPosition.latitude, issPosition.longitude )
|
||||
HStack {
|
||||
Text(issPosition)
|
||||
Text(issPositionString)
|
||||
}
|
||||
.padding(EdgeInsets(top: 18, leading: 16, bottom: 0, trailing: 16))
|
||||
|
||||
|
||||
List(peopleInSpaceViewModel.people, id: \.name) { person in
|
||||
NavigationLink(destination: PersonDetailsView(peopleInSpaceViewModel: self.peopleInSpaceViewModel, person: person)) {
|
||||
NavigationLink(destination: PersonDetailsView(peopleInSpaceViewModel: peopleInSpaceViewModel, person: person)) {
|
||||
PersonView(peopleInSpaceViewModel: self.peopleInSpaceViewModel, person: person)
|
||||
}
|
||||
}
|
||||
.navigationBarTitle(Text("People In Space"))
|
||||
.onAppear {
|
||||
self.peopleInSpaceViewModel.startObservingPeopleUpdates()
|
||||
self.peopleInSpaceViewModel.startObservingISSPosition()
|
||||
}.onDisappear {
|
||||
self.peopleInSpaceViewModel.stopObservingPeopleUpdates()
|
||||
self.peopleInSpaceViewModel.stopObservingISSPosition()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +58,7 @@ struct PersonDetailsView: View {
|
|||
Text(person.name).font(.title)
|
||||
|
||||
ImageView(withURL: peopleInSpaceViewModel.getPersonImage(personName: person.name), width: 240, height: 240)
|
||||
|
||||
|
||||
Text(peopleInSpaceViewModel.getPersonBio(personName: person.name)).font(.body)
|
||||
Spacer()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
import Combine
|
||||
import common
|
||||
|
||||
|
||||
public struct IssPositionPublisher: Publisher {
|
||||
public typealias Output = IssPosition
|
||||
public typealias Failure = Never
|
||||
|
||||
private let repository: PeopleInSpaceRepository
|
||||
public init(repository: PeopleInSpaceRepository) {
|
||||
self.repository = repository
|
||||
}
|
||||
|
||||
public func receive<S: Subscriber>(subscriber: S) where S.Input == IssPosition, S.Failure == Failure {
|
||||
let subscription = IssPositionSubscription(repository: repository, subscriber: subscriber)
|
||||
subscriber.receive(subscription: subscription)
|
||||
}
|
||||
|
||||
final class IssPositionSubscription<S: Subscriber>: Subscription where S.Input == IssPosition, S.Failure == Failure {
|
||||
private var subscriber: S?
|
||||
private var job: Kotlinx_coroutines_coreJob? = nil
|
||||
|
||||
private let repository: PeopleInSpaceRepository
|
||||
|
||||
init(repository: PeopleInSpaceRepository, subscriber: S) {
|
||||
self.repository = repository
|
||||
self.subscriber = subscriber
|
||||
|
||||
job = repository.iosPollISSPosition().subscribe(
|
||||
scope: repository.iosScope,
|
||||
onEach: { position in
|
||||
subscriber.receive(position!)
|
||||
},
|
||||
onComplete: { debugPrint("onComplete") },
|
||||
onThrow: { error in debugPrint(error) }
|
||||
)
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
subscriber = nil
|
||||
job?.cancel(cause: nil)
|
||||
}
|
||||
|
||||
func request(_ demand: Subscribers.Demand) {}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import Foundation
|
||||
import Combine
|
||||
import common
|
||||
|
||||
|
||||
|
@ -6,9 +7,14 @@ class PeopleInSpaceViewModel: ObservableObject {
|
|||
@Published var people = [Assignment]()
|
||||
@Published var issPosition = IssPosition(latitude: 0.0, longitude: 0.0)
|
||||
|
||||
private var subscription: AnyCancellable?
|
||||
|
||||
private let repository: PeopleInSpaceRepository
|
||||
init(repository: PeopleInSpaceRepository) {
|
||||
self.repository = repository
|
||||
|
||||
subscription = IssPositionPublisher(repository: repository)
|
||||
.assign(to: \.issPosition, on: self)
|
||||
}
|
||||
|
||||
func startObservingPeopleUpdates() {
|
||||
|
@ -21,17 +27,6 @@ class PeopleInSpaceViewModel: ObservableObject {
|
|||
repository.stopObservingPeopleUpdates()
|
||||
}
|
||||
|
||||
func startObservingISSPosition() {
|
||||
repository.startObservingISSPosition(success: { data in
|
||||
self.issPosition = data
|
||||
})
|
||||
}
|
||||
|
||||
func stopObservingISSPosition() {
|
||||
repository.stopObservingISSPosition()
|
||||
}
|
||||
|
||||
|
||||
func getPersonBio(personName: String) -> String {
|
||||
return repository.getPersonBio(personName: personName)
|
||||
}
|
||||
|
@ -40,5 +35,8 @@ class PeopleInSpaceViewModel: ObservableObject {
|
|||
return repository.getPersonImage(personName: personName)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue