Reputation: 13
I'm trying to fetch some data using YouTube JSON API, JSON looks like this:
{
"kind": "youtube#channelListResponse",
"etag": "AVEF8yG4pQMKfzZjMyy1rfVtFXA",
"pageInfo": {
"resultsPerPage": 1
},
"items": [
{
"kind": "youtube#channel",
"etag": "LpEHq7VFVl1gDbt97Gf4ObyFn0U",
"id": "UCBJycsmduvYEL83R_U4JriQ",
"statistics": {
"viewCount": "2084223845",
"commentCount": "0",
"subscriberCount": "12100000",
"hiddenSubscriberCount": false,
"videoCount": "1272"
}
}
]
}
And here is my code:
import Foundation
struct StatResponse: Decodable {
let items: Items
}
struct Items: Decodable {
var statistics: Stats
}
struct Stats: Decodable {
let subscriberCount: Double
}
enum APIError: Error {
case wrongURL
case decodingError
case noData
}
extension URL {
static func url() -> URL? {
guard let url = URL(string: "https://www.googleapis.com/youtube/v3/channels?part=statistics&id=UCBJycsmduvYEL83R_U4JriQ&key=API_KEY") else {
return nil
}
return url
}
}
class StatService {
func getStats(completion: @escaping (Result<Items?,APIError>) -> Void) {
guard let url = URL.url() else {
return completion(.failure(.wrongURL))
}
URLSession.shared.dataTask(with: url) {
data, response, error in
guard let data = data, error == nil else {
return completion(.failure(.noData))
}
let statResponse = try? JSONDecoder().decode(StatResponse.self, from: data)
if let statResponse = statResponse {
completion(.success(statResponse.items))
} else {
return completion(.failure(.decodingError))
}
}.resume()
}
}
class StatsViewModel: ObservableObject {
@Published private var items: Items?
var subCount: Double {
guard let subscriberCount = items?.statistics.subscriberCount else {
return 0
}
return subscriberCount
}
func fetchStats() {
StatService().getStats {
result in switch result {
case .success(let Items):
DispatchQueue.main.async {
self.items = Items
}
case .failure(_ ):
print("fail")
}
}
}
}
I have no idea what is going wrong, it gives 0 as the result. I tried various things like self.items?.statistics = Items.statistics
instead of self.items = Items
because I'm trying to get the data I'm trying to fetch is under statistics but it also didn't work and got an error saying "Value of optional type 'Stats?' must be unwrapped to a value of type 'Stats'" and I don't know what to do, I can fetch non-nested objects pretty easily in Swift but I can't nested ones. I'm kind of a beginner to both Swift and programming in general so if that's a stupid question sorry for taking your time.
Upvotes: 0
Views: 68
Reputation: 5203
You have an error on your model classes, the items
property is an array, and you are trying to decode it as an object, change StatResponse
to:
struct StatResponse: Decodable {
let items: [Items]
}
Also change the subscriberCount
to String on the Stats
struct:
struct Stats: Decodable {
let subscriberCount: String
}
Then you will have to change the way you are accessing the data, because now you're accessing an array. You can use the first
property of an array which will return the first element of and array like this:
if let item = statResponse.items.first {
completion(.success(item))
} else {
return completion(.failure(.decodingError))
}
Upvotes: 2