Last active
May 9, 2021 15:45
-
-
Save StewartLynch/12778dbc541a31fda8921e9988ca2606 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
// Here is the codable model that I am trying to decode the feed into | |
struct Feed: Codable { | |
let items: [Items] | |
let pageInfo: PageInfo | |
let nextPageToken: String? | |
} | |
struct PageInfo: Codable { | |
let totalResults: Int | |
let resultsPerPage: Int | |
} | |
struct Items: Codable, Identifiable { | |
struct Snippet: Codable { | |
struct Thumbnails: Codable { | |
struct Default: Codable { | |
let url: URL | |
} | |
let `default`: Default? | |
} | |
struct ResourceId: Codable { | |
let videoId: String | |
} | |
let publishedAt: Date | |
let title: String | |
let description: String | |
let thumbnails: Thumbnails | |
let resourceId: ResourceId? | |
let channelId: String? | |
} | |
let id: String | |
let snippet: Snippet | |
} | |
// Here is a sample endpoint. I have specified maxResults here as 20, because I know that there are 50items in this playlist and so there will be pagination involved meaning that for the first 4 requests, the Feed object's nextPageToken will not be nil | |
let endpoint = "https://www.googleapis.com/youtube/v3/playlistItems?part=snippet%2CcontentDetails&maxResults=20&playlistId=PLBn01m5Vbs4C8jeAmLZxk9kZ59lYtxnHW&key=AIzaSyAfF93xIFQEVYXjci2P3VYdhIWNFG9n7cw" | |
// This is what I currently use for my APIServce to make a request to get the JSON | |
enum APIService { | |
struct ErrorType: Identifiable { | |
let id = UUID() | |
let error: APIError | |
} | |
public enum APIError: Error { | |
case error(_ errorString: String) | |
} | |
public static func getJSON<T: Decodable>(urlString: String, | |
dateDecodingStategy: JSONDecoder.DateDecodingStrategy = .deferredToDate, | |
keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .useDefaultKeys, | |
completion: @escaping (Result<T,APIError>) -> Void) { | |
guard let url = URL(string: urlString) else { | |
completion(.failure(.error("Invalid URL"))) | |
return | |
} | |
let request = URLRequest(url: url) | |
URLSession.shared.dataTask(with: request) { (data, response, error) in | |
if let error = error { | |
completion(.failure(.error("URLError: \(error.localizedDescription)"))) | |
return | |
} | |
guard let data = data else { | |
completion(.failure(.error("Error: Data is corrupt."))) | |
return | |
} | |
let decoder = JSONDecoder() | |
decoder.dateDecodingStrategy = dateDecodingStategy | |
decoder.keyDecodingStrategy = keyDecodingStrategy | |
guard let decodedData = try? decoder.decode(T.self, from: data) else { | |
completion(.failure(.error("Error: decoding data."))) | |
return | |
} | |
completion(.success(decodedData)) | |
}.resume() | |
} | |
} | |
// So, when when I want to retrieve the data, I do this. | |
var allItems = [Items]() | |
// Here is a function that I use to retrieve the items and populate the allItems array | |
func getAllItems(nextPageToken:String? = nil, completion: @escaping (Feed) -> Void) { | |
var urlString = endpoint | |
if let nextPageToken = nextPageToken { | |
urlString = endpoint + "&pageToken=\(nextPageToken)" | |
} | |
APIService.getJSON(urlString: urlString,dateDecodingStategy: .iso8601) { (result: Result<Feed, APIService.APIError>) in | |
switch result { | |
case .success(let feed): | |
completion(feed) | |
case .failure(let error): | |
print(error.localizedDescription) | |
} | |
} | |
} | |
// Example processing of retrieved items | |
func printAllTitles(completion: @escaping ([Items]) -> Void) { | |
var foundItems = [Items]() | |
getAllItems { feed in | |
foundItems += feed.items | |
let nextPageToken = feed.nextPageToken | |
if nextPageToken != nil { | |
// Here is where I am stuck. I need to keep calling getAllItems until nextPageToken is no longer nil so that I can append all of the items to the allItems array and then execute the completion | |
// I am forcing completion here so that you can see that I get the first 20 in the set | |
// NOTE ****** If I don't complete here, I am in an infinite loop and my daily API quota exceeds and I can't work on it again until the next day | |
completion(foundItems) | |
} else { | |
completion(foundItems) | |
} | |
} | |
} | |
printAllTitles { items in | |
allItems = items | |
for item in allItems { | |
print(item.snippet.title) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment