0

I have a combine construct to combine calls to the functions that return AnyPublishers. [credVendor.credentials() and credVendor.accessToken()]. The issue here is that the call to the publisher does not return back sometimes and the application freezes and the scheduler for timeout does not work.

But when I use a DispatchQueue.main instead of DispatchQueue.global() on the scheduler this behavior never happens. I would like to understand this weird behavior. I pretty new to iOS and still learning. I would really appreciate if someone can point me in the right direction.

Code for the combine.

 anyCancellable = credVendor.credentials()
        .combineLatest(credVendor.accessToken())
        .eraseToAnyPublisher()
        .catch { error -> AnyPublisher<(Credentials, AccessToken), Error> in
            return Fail(outputType: (Credentials, AccessToken).self, failure: error).eraseToAnyPublisher()
        }
        .sink(
            receiveCompletion: { completion in
                switch completion {
                case .failure:
                    let error = Error(message: "\(self) Could not find credentials.", type: .credentialsError)
                    completionHandler?(false, error)
                default: ()
                }
            },
            receiveValue: { [weak self] credentials, accessToken in
                guard let self = self else {
                   return
                }

                self.display(request: request, timer: timer, token: accessToken, credentials: credentials) { result in
                    switch result {
                    case .success(let maybeSplash):
                        -- success-> Do the work
                    case .failure(let error):
                        ---
                        completionHandler?(false, error)
                        return
                    }
                }
              }
        )

Code for the publishers.

 func credentials() -> AnyPublisher<Credentials, Error> {
    return authentication.getSessionId().compactMap { $0 }
        .combineLatest(authentication.getDirectedID().compactMap { $0 })
        .first()
        .setFailureType(to: CredentialsFetchError.self)
        .timeout(.seconds(10), scheduler: DispatchQueue.global(), customError: { .timeout })
        .tryMap { (sessionId, directedId) -> Credentials in
            // use the sessionId and directedId to populate credentials object
            return Credentials(....some attributes)
        }
        .mapError { $0 as Error }
        .eraseToAnyPublisher()
}



func accessToken() -> AnyPublisher<AccessToken, Error> {
    return authentication.accessToken.compactMap { $0 }
        .first()
        .setFailureType(to: CredentialsFetchError.self)
        .timeout(.seconds(10), scheduler: DispatchQueue.global(), customError: { .timeout })
        .mapError { $0 as Error }
        .eraseToAnyPublisher()
}
Sabya
  • 197
  • 4
  • 17
  • `DispatchQueue.global()`. is a concurrent queue while you can only use serial queues as schedulers. If you use a concurrent queue you sometimes receive completion before receiving value. You can use `RunLoop.current` instead. – LuLuGaGa Mar 22 '22 at 12:11
  • Yes @LuLuGaGa you're right about it. Quick question - when a scheduler is used in the timeout operator, does it mean my publisher is using that same scheduler? This is because sometimes even there is no timeout the receive completion is called before the receive value. – Sabya Mar 22 '22 at 19:06
  • The docs say it is *the scheduler on which to deliver events*, which makes sense since once you switch to concurrent queue things will get weird. – LuLuGaGa Mar 23 '22 at 09:21

0 Answers0