Skip to content

Instantly share code, notes, and snippets.

@StewartLynch
Last active May 9, 2021 15:45

Revisions

  1. StewartLynch revised this gist May 9, 2021. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions Pagination Problem
    Original file line number Diff line number Diff line change
    @@ -110,6 +110,7 @@ func printAllTitles(completion: @escaping ([Items]) -> Void) {
    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)
  2. StewartLynch created this gist May 9, 2021.
    125 changes: 125 additions & 0 deletions Pagination Problem
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,125 @@
    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)
    }
    }