Home > Back-end >  Handle no internet connection errors with Combine and URLSession
Handle no internet connection errors with Combine and URLSession

Time:10-02

I am trying to wrap all errors into one class, with Combine and URLSession. However, I can't map the "No internet connection" errors.

This is my code:

func request<T>(type: T.Type, urlRequest: URLRequest) -> AnyPublisher<Resource<Response<T>>, Error> where T : GenericDecodable {
    return URLSession.shared.dataTaskPublisher(for: urlRequest)
        .tryMap({ try NetworkController.handleURLResponse(output: $0, url: urlRequest) })
        .receive(on: DispatchQueue.main)
        .eraseToAnyPublisher()
}

static func handleURLResponse<T>(output: URLSession.DataTaskPublisher.Output, url: URLRequest) throws -> Resource<Response<T>> {
    guard let response = output.response as? HTTPURLResponse else { throw NetworkingError.badURLResponse(url: url) }
    
    var finalResponse: Response<T>
    do {
        finalResponse = try JSONDecoder().decode(Response<T>.self, from: output.data)
    } catch {
        return Resource.networkError(data: nil)
    }
    
    switch response.statusCode {
        case 200..<300:
            return Resource.success(data: finalResponse)
        case 422, 400:
            return Resource.validationError(data: finalResponse)
        default:
            return Resource.networkError(data: nil)
    }
}

As you can see, if the server sends malformed JSON or something like that, my code works without any problem. But if I disable the internet connection on my phone, I mean if there is no response from the server, my tryMap method is not calling. I only see some text of logs. Like these:

), NSLocalizedDescription=Could not connect to the server., NSErrorFailingURLStringKey=http://api-panel-gw.productplus.conf/api/customers/login?, NSErrorFailingURLKey=http://api-panel-gw.productplus.conf/api/customers/login?, _kCFStreamErrorDomainKey=1}

If this happens, I want to send Resource.networkError(data: nil) to the subscriber.

I tried to put .mapError above the .tryMap, but no luck. I am getting the following error:

enter image description here

How can I achieve that?

CodePudding user response:

The mapError in your sample code is not currently returning anything (it's returning type Void or ()). It currently just prints something. The publisher it creates is expected to return some kind of Error. The compiler is complaining because the return type of your block does not match what it expects. You should return some kind of Error from the mapError block.

enum MyNetworkErrors : Error {
    case networkUnavailable(String)
}


func doDownload() {
    URLSession.shared.dataTaskPublisher(for: URL(string: "https://somedomain.com/path")!)
        .mapError {
            (networkError : URLError) -> Error  in

            /* Note that all the return paths in this block return some kind of Error */

            if nil != networkError.networkUnavailableReason {
                return MyNetworkErrors.networkUnavailable(networkError.localizedDescription)
            }

            return networkError
        }
    /* Other modifiers here */
}
  • Related