Search code examples
iosswiftswiftuicombinerefresh-token

refresh token using combine networking ios


this question is straight forward :

My code :

        return urlSession.dataTaskPublisher(for: urlRequest)
        .tryMap { (data: Data, response: URLResponse) -> Data in
            //TODO: hide loader
            GRP.hideLoader()
            if let httpURLResponse = response as? HTTPURLResponse {
                if !(200...299 ~= httpURLResponse.statusCode) {
                    var error = NetworkingError(errorCode: httpURLResponse.statusCode)
                    if let json = try? JSONSerialization.jsonObject(with: data, options: []) {
                        error.jsonPayload = json
                    }
                    throw error
                }
            }
            
            if withErrorMessage, let errorCheckModel = try? JSONDecoder().decode(ErrorModel.self, from: data)
            {
                if let statusIsSuccess = errorCheckModel.success, let errorMessage = errorCheckModel.message, !errorMessage.isEmpty
                {
                    if(!statusIsSuccess)
                    {
                        print(urlString)
                        GRP.showToast(failure: true, message: errorMessage)
                    }
                }
            }

            
            return data
        }.mapError { error -> NetworkingError in
            return NetworkingError(error: error)
        }
        .decode(type: T.self, decoder: decoder)
        .receive(on: RunLoop.main)
        .eraseToAnyPublisher()
}

i made this task buikder but i am stuck, i want to know how can i implement refresh token i. Thank you.


Solution

  • The question is kind of confusing as written. Do you mean I have a request that returns an AnyPublisher<SomeDecodable, NetworkError> and if it fails for a specific reason then I want to make another call (to refresh) and then retry the request? If so it looks something like this:

      let authenticatedRequest = URLSession.shared.dataTaskPublisher(for: urlRequest)
      return authenticatedRequest
        .map { (data, response) -> AnyPublisher<(Data, URLResponse), Error> in
          isUnanthenticated(response)
            ? refetchToken.map { _ in
                authenticatedRequest
              }
              .switchToLatest()
              .eraseToAnyPublisher()
            : Just((data, response)).eraseToAnyPublisher()
          }
        .switchToLatest()
        .decode(T.self, from: decoder)
        .mapError { error -> NetworkingError in
           return NetworkingError(error: error)
        }
        .eraseToAnyPublisher()
      }
        
    
    1. We make the authenticated request
    2. We map the request and if it failed then we make a reauthrequest and retry. Otherwise we just return out input.
    3. Either way we now have a Publisher of Publishers and we don't want that so we call switch to latest to flatten it and we continue.