Ryan H
Ryan H

Reputation: 153

Unable to get defined throw responses in async throws function

I am in the process of starting to some work with Swift Concurrency in URLSessions for based API interaction in Swift and am having an issue customizing any error types to the calling function if anything fails.

Based on some research, I have a simple function that makes a get request using URL Session;

    typealias NetworkResponse = (data: Data, response: URLResponse)

    func getData<D: Decodable>(from endpoint: Route) async throws -> D {
        let request = try createRequest(from: endpoint)
        let response: NetworkResponse = try await session.data(for: request)
        guard let urlResponse = response.response as? HTTPURLResponse else {
            throw APIError.invalidServerResponse
        }
        let statusCode = urlResponse.statusCode
        guard (200...299).contains(statusCode) else {
            throw APIError.resourceUnavailable(statusCode)
        }
        return try decoder.decode(D.self, from: response.data)
    }

If the request is successful, the data is decoded and returned as expected but the issue is if any of the throws are, well, thrown, the return always says "The operation couldn’t be completed. (GenericNetworkingLayer.APIError error.)" instead of the error response.

I have an error enum created;

enum APIError: Error {
    case invalidPath
    case decodingError
    case invalidServerResponse
    case resourceUnavailable(Int)  //Want to return statuscode here
}

For testing, I'm just making the network call in a VC ViewDidLoad();

        Task {
            do {
                let response: [PostsModel] = try await APIManager.shared.getData(from: .getCommentFromPost(postId: 1))
            } catch {
                print(error.localizedDescription)
            }
        }

I seem to be missing something do have the throw data return what I want. I ultimately want to get at least the statusCode on the throw if the error is resourceUnavailable but I can't even just get the correct error returned. I have come across similar code in my research but nothing about this not working so I wa hoping someone could tell me what the issue might be?

Here is my localized error extension;

extension APIError: LocalizedError {
    var localizedDescription: String {
        switch self {
        case .invalidPath:
            return "Invalid Path"
        case .decodingError:
            return "There was an error decoding the response"
        case .invalidServerResponse:
            return "Network request error."
        case .resourceUnavailable:
            return "Server or resource not available"
        }
    }
}

Upvotes: -1

Views: 34

Answers (1)

Ryan H
Ryan H

Reputation: 153

Thanks to the post reference by @CraigSiemens, I was able to get this to work by modifying my error extension as per the post within the referenced post by @Abuzeid.

extension APIError: LocalizedError {
    
    public var description: String {
        switch self {
            case .invalidPath:
                return "Invalid Path"
            case .decodingError:
                return "There was an error decoding the response"
            case .invalidServerResponse:
                return "Network request error."
            case .resourceUnavailable(let error):
                return "Server or resource not available. Status Code: \(error)"
            }
        }
    
    public var errorDescription: String? {
        return description
    }
}

I then updated my print statement to this;

print(error.localizedDescription.description)

And now I get an output I want like below;

Server or resource not available. Status Code:404

Upvotes: 0

Related Questions