Search code examples
swiftalamofire

API Fetch returns nil


The fetch doesn't throw any error, it just returns nil, I don't know why, this is the api URL:

https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd&include_market_cap=true&include_24hr_vol=true&include_24hr_change=true&include_last_updated_at=true

Note: There is an infinite amount of tokens, not only Bitcoin, so I can't hardcode it.

it looks like:

{
    "bitcoin": {
        "usd": 51110,
        "usd_market_cap": 964544028181.903,
        "usd_24h_vol": 19080677597.123028,
        "usd_24h_change": 2.163373654971005,
        "last_updated_at": 1640575480
    },
    thereCanBeMoreWhichIcan'tPredict: {
        ...
    }
}

This is the model I created:

struct TokenSimplePriceCoingeckoUsdModel: Codable, Hashable {
    let usd, usd_market_cap, usd_24h_vol, usd_24h_change: Double?
    let last_updated_at: Int?
}

and this is how I'm fetching it with Alamofire:

func afRequest(url: URL) async throws -> Data {
    try await withUnsafeThrowingContinuation { continuation in
        // If another error try lowering the request error: (statusCode: 200..<300)
        // timeoutInterval is in seconds
        AF.request(url, method: .get, requestModifier: { $0.timeoutInterval = .infinity }).validate(statusCode: 200..<600).responseData { response in
            
            if let data = response.data {
                continuation.resume(returning: data)
                return
            }
            
            if case let .failure(error) = response.result {
                print("🛑 Error on afRequest(): \(error)")
                continuation.resume(throwing: error)
                return
            }
        }
    }
}

func getDataFromAPI(url: String) async -> Any {
    do {
        let url = URL(string: url)!
        let undecodedData = try await afRequest(url: url)
        let finalData = try JSONDecoder().decode(TokenSimplePriceCoingeckoUsdModel.self, from: undecodedData)
        
        return finalData
    } catch(let error) {
        print("🛑 Error on getDataFromAPI(): ", error)
    }
    return ""
}

When I print the return of that I get:

TokenSimplePriceCoingeckoUsdModel(usd: nil, usd_market_cap: nil, usd_24h_vol: nil, usd_24h_change: nil, last_updated_at: nil)

EDIT: If I do:

print(String(data: undecodedData, encoding: .utf8)!) under the line let undecodedData ... it prints the data, so it's confirmed the problem is while decoding it with my Model, what am I doing wrong?


Solution

  • I have never used alamofire directly to make a network request, but seems the error is on how you decode the response, assuming undecodedData is already containing the data you want, your current response model does not decode the "bitcoin" key, hence the struct is nil, you can change your struct to something like

    struct BitcoinStats: Codable, Hashable {
        let bitcoin: TokenSimplePriceCoingeckoUsdModel
    }
    
    struct TokenSimplePriceCoingeckoUsdModel: Codable, Hashable {
        let usd, usd_market_cap, usd_24h_vol, usd_24h_change: Double?
        let last_updated_at: Int?
    }
    

    or you might want to try to decode the value of the "bitcoin" key directly, so you can keep your current struct as is

    UPDATE : Because the JSON has other keys than "bitcoin", this is my proposed solution for decoding the data dynamically without the needs to hardcode the keys: pastecode.io/s/tkim2jf0