Subscribing for the changes in the environment init body

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!!!

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!!

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.

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!!!

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.

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!

Terms of Service

Privacy Policy

Cookie Policy