Home > Software design >  How to get SubscribeOn flatMap operator for iOS 13?
How to get SubscribeOn flatMap operator for iOS 13?

Time:10-03

This code does not compile on iOS 13 because flatMap is available only in iOS 14. How to extend SubscribeOn to have flatMap operator?

func f1(_ value: Int) -> Future<Int, Error> {
    Future { promise in
        print("\(Thread.current): f1() value = \(value)")
        promise(.success(value   1))
    }
}

func f2(_ value: Int) -> Future<Int, Error> {
    Future { promise in
        print("\(Thread.current): f2() value = \(value)")
        promise(.success(value   1))
    }
}

let a1 = Just(0)
    .subscribe(on: DispatchQueue.global())
    .flatMap(f1)
    .flatMap(f2)
    .receive(on: DispatchQueue.main)
    .sink { _ in
        print("\(Thread.current): completed")
    } receiveValue: { value in
        print("\(Thread.current): sink() value = \(value)")
    }

UPDATE Here is how my solution looks like now:

extension Publishers.SubscribeOn {
    func myFlatMap<P>(_ transform: @escaping (Self.Output) -> P) -> Publishers.FlatMap<P, Publishers.SetFailureType<Self, P.Failure>> where P: Publisher {
        Publishers.FlatMap<P, Publishers.SetFailureType<Self, P.Failure>>(
            upstream: Publishers.SetFailureType<Self, P.Failure>(upstream: self),
            maxPublishers: .unlimited,
            transform: transform
        )
    }
}

CodePudding user response:

There is a flatMap operator available in iOS 13, but there are more flatMap operators available in iOS 14.

Specifically, the only flatMap operator available in iOS 13 requires that the upstream publisher (in your example, Just<Int>) and the inner publisher (in your example, Future<Int, Error>) have the same Failure type. But Just<Int>.Failure is Never, while Future<Int, Error>.Failure is Error.

In iOS 14, there are three extra overloads of flatMap for special treatment of three cases:

Your example code falls into the first special case (upstream Failure is Never), so it compiles for iOS 14 but not for iOS 13.

You can work around the problem by explicitly transforming your Just with a setFailureType operator:

let a1 = Just(0)
    .setFailureType(to: Error.self)
    .subscribe(on: DispatchQueue.global())
    .flatMap(f1)
    .flatMap(f2)
    .receive(on: DispatchQueue.main)
    .sink { _ in
        print("\(Thread.current): completed")
    } receiveValue: { value in
        print("\(Thread.current): sink() value = \(value)")
    }

Alternatively, you can use a Result.Publisher to start out with the necessary Failure type of Error. Swift should even be able to deduce it for you:

let a1 = Result.success(0).publisher
    .subscribe(on: DispatchQueue.global())
    .flatMap(f1)
    .flatMap(f2)
    .receive(on: DispatchQueue.main)
    .sink { _ in
        print("\(Thread.current): completed")
    } receiveValue: { value in
        print("\(Thread.current): sink() value = \(value)")
    }
  • Related