barcis89
(Bartlomiej Woronin)
1
Hello!!
I spent 3 days to think about one thing I have to give up... I'm too stupid....
Guys HELP!!!
My scenario:
I have my custom publisher which I want to start to listening (it should be running all the time through the entire life of the application) when whole store is initialized, then I want to send action to reducer when I got receiveValue callback in the subsription.
So what I do is putting subscription in my init Environemnt body:
syncCancellable = Effect(dataPublisher)
.map(HomeScreenAction.fetchedCGMData)
.sink(receiveCompletion: {
print("receiveCompletion")
print ($0)
},
receiveValue: {
print("receiveValue")
print ($0)
// how do something like this
//send.action(HomeScreenAction.fetechedData(data))
})
So my question is how to in receiceValue blok run an action with parameter, so then
redcuer could catch this events permanently and run some other method after receiving values from this place?
Thank you in advance for any help!!!
barcis89
(Bartlomiej Woronin)
2
So maybe putting all subscription (and listening) to environment objects is a bad practice? But still I didn't find a better place for where should I put the code which is a publisher which should be run when the application starts.
Many thanks for any help!!
mbrandonw
(Brandon Williams)
3
What you can do is put the publisher in your environment and then use something like a didFinishLaunching action to subscribing to it:
struct HomeState { ... }
enum HomeAction {
case didFinishLaunching
case dataResponse(Result<SomeData, SomeError>)
// other actions
}
struct HomeEnvironment {
var data: Effect<SomeData, SomeError>
// other dependencies
}
let homeReducer = Reducer<HomeState, HomeAction, HomeEnvironment> { state, action, environment in
switch action {
case .didFinishLaunching:
return environment.data
.catchToEffect()
.map(HomeAction.dataResponse)
case let .dataResponse(.success(data)):
// do something with data
case let .dataResponse(.failure(error)):
// do something with error
}
}
Hope it helps.
barcis89
(Bartlomiej Woronin)
4
Thank you so much @mbrandonw for your answer!!!
currently, I'm sure that the environment is a good place for it!!!
So I tried something that you suggested:
public struct HomeScreenEnvironment {
fileprivate var positionEffect: Effect<PositionData, Never>
init (positionPublisher: CurrentValueSubject<PositionData, Never>) {
positionEffect = Effect(positionPublisher)
...
}
public let homeScreenReducer = Reducer<HomeScreenState, HomeScreenAction, HomeScreenEnvironment> {
state, action, environment in
switch action {
case .didFinishLaunching:
return environment.positionEffect
.receive(on: DispatchQueue.global())
.flatMap { CurrentValueSubject<Int, Never>($0.cellNumber) }
.removeDuplicates()
.debounce(for: .seconds(1), scheduler: DispatchQueue.global())
.catchToEffect()
.map(HomeScreenAction.receivedPositionValue)
.cancellable(id: UUID())
but still, unfortunately this event only runs once :/ Maybe do you have some suggestion what can happen?
I'm quite sure that positionPublisher is publishing the values...
Thank you very much for any suggestion!!!
mbrandonw
(Brandon Williams)
5
From what I see here it should work, as long as positionPublisher is definitely emitting some values. You should confirm that.
There is a bit of clean up you could do in that publisher chain though. You could replace .flatMap { CurrentValueSubject(...) } with just .map { $0.cellNumber }, or even .map(\.cellNumber). And you can remove the .cancellable since it doesn't seem you are using it.
barcis89
(Bartlomiej Woronin)
6
Many thanks for your help @mbrandonw!! You helped me a lot!! And thank you for your tips about cleaning the code. This is really awesome!