Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Combine sink stops receiving values after first error

Im moving my project to Combine from RxSwift I have a logic where I want publisher to emit event every time I click button. Acrually clicking button executed pushMe.send()

pushMe
            .print("Debug")
            .flatMap { (res) -> AnyPublisher<Bool, Error> in
                return Future<Bool, Error>.init { closure in
                    closure(.failure(Errors.validationFail))
                }.eraseToAnyPublisher()
            }
            .sink(receiveCompletion: { completion in
                print("Completion received")
            }, receiveValue: { value in
                print("Value = \(value)")
            })
            .store(in: &subscriptions)

The console result

Debug: receive value: (true)
Completion received
Debug: receive value: (true)
Debug: receive value: (true)

I do not understand why sink receive error only on first event. The rest clicks are ignored.

like image 450
Sergey Brazhnik Avatar asked Oct 20 '20 12:10

Sergey Brazhnik


1 Answers

What does flatMap do -

  1. Subscribes to the given publisher (let's say XPublisher).
  2. Sends the Errors and Output values (not finished event/ completion) emitted by XPublisher to the down stream.

So If you handle errors inside the flat map , (which means the publisher inside the flatMap does not emit errors), then flatMap Never sends an error to the down stream.

pushMe
    .print("Debug")
    .flatMap { (res) -> AnyPublisher<Bool, Never> in //<= here
        return Future<Bool, Error>.init { closure in
            closure(.failure(Errors.validationFail))
        }
        .replaceError(with: false) //<= here
        .eraseToAnyPublisher()
    }
    .sink(receiveCompletion: { completion in
        print("Completion received")
    }, receiveValue: { value in
        print("Value = \(value)")
    })
    .store(in: &subscriptions)

Otherwise you can handle error outside the fatMap. Problem here is that, once an error out whole the subscription / cancellable cancelled. ( in the below example error has replace with a false value)

pushMe
    .print("Debug")
    .flatMap { (res) -> AnyPublisher<Bool, Error> in 
        return Future<Bool, Error>.init { closure in
            closure(.failure(Errors.validationFail))
        }
        .eraseToAnyPublisher()
    }
    .replaceError(with: false) //<= here
    .sink(receiveCompletion: { completion in
        print("Completion received")
    }, receiveValue: { value in
        print("Value = \(value)")
    })
    .store(in: &subscriptions)

What is happening in the above code.

  1. FlatMap error outs.
  2. replace the error with false (One false value will receive Because of this)
  3. subscription cancelled because of the error out in the stream.
like image 82
Yodagama Avatar answered Sep 27 '22 20:09

Yodagama