Skip to content

Instantly share code, notes, and snippets.

@StewartLynch
Last active May 9, 2021 15:45
Show Gist options
  • Save StewartLynch/12778dbc541a31fda8921e9988ca2606 to your computer and use it in GitHub Desktop.
Save StewartLynch/12778dbc541a31fda8921e9988ca2606 to your computer and use it in GitHub Desktop.
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
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