Usually subscription is not done in SceneDelegate, but in View Model. Also, if you move currentDate from ReaderView as @State to ReaderViewModel as @Published, you can watch the change of stories in Live View.
You don’t need import SwiftUI in View Model.
You don’t need init in ReaderView.
So can use Live View:
You don’t need subscription in SceneDelegate.
1 Like
One thing I’ve noticed about this solution is that once you use flatMap
in ReaderViewModelNew
to combine the api.stories()
publisher and the $filter
publisher, you lose the ability to receive the .success
completion handler from the api.stories()
publisher (i.e., you only ever receive errors in the sink
).
Is there any way around this other than doing something like this?
let stories = api.stories().share()
$currentDate
.setFailureType(to: API.Error.self)
.flatMap { _ in
Publishers.CombineLatest(stories, self.$filter.setFailureType(to: API.Error.self))
}
...
stories
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { print($0) }, receiveValue: { print($0) }).store(in: &subscriptions)
You are wrong.
Operator setFailureType (to: API.Error.self)
is just a way to turn an infallible publisher into a fallible one. That’s it. (Chapter 16: Error Handling)
All @Published
property has a failure type of Never
and I take it from example.
If you want to control error of $filter
you have to create another Publisher
from simple $filter, probably just here in ReaderViewModelNew
.
For example:
private var validFilter: AnyPublisher<[String], Error> {
$filter
.tryMap { (filter) ->[String] in
guard check (filter) else {
throw API.Error.invalidResponse}
return filter
}
.eraseToAnyPublisher()
}
let stories = api.stories().share()
$currentDate
.setFailureType(to: API.Error.self)
.flatMap { _ in
Publishers.CombineLatest(stories, self.validFilter)
}
...
stories
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { print($0) }, receiveValue: { print($0) }).store(in: &subscriptions)
To update List of stories on screen, when new story is coming.
Very need solution, I would go for it.
A maybe desired side effect when updating the filter keywords in ReaderView :
.sheet(isPresented: $presentingSettingsSheet,onDismiss:{
** self.model.filter = self.settings.keywords.map{**
** $0.value}**
}, content: {
SettingsView(dismissAction:{
self.presentingSettingsSheet = false
})
.environmentObject(self.settings)
})
is when you persist the keywords as in the sample code from the book, after restart the app the filter will not applied on startup.
@afrank Thank you for sharing your solution - much appreciated!