// h/t to @JohnSundell (https://www.swiftbysundell.com/) import UIKit import Combine import PlaygroundSupport // This playground will execute indefinetly in order to give our // async operations enough time to execute. PlaygroundPage.current.needsIndefiniteExecution = true // --- Creating a publisher for performing a network request --- // Note: set a retry automatically (3x) if request fails let url = URL(string: "https://api.github.com/repos/67443-Mobile-Apps/RepoBrowser2022")! // for class exercise, change the url to: // let url = URL(string: "https://api.github.com/search/repositories?q=language:swift&sort=stars&order=desc")! let publisher = URLSession.shared .dataTaskPublisher(for: url) .retry(3) // --- Bringing back our old friend, Repository --- struct Repository: Codable, Identifiable { let id: Int let name: String let htmlURL: String let itemDescription: String? enum CodingKeys: String, CodingKey { case id case name case htmlURL = "html_url" case itemDescription = "description" } } // --- Subscribing to our publisher using sink() --- let cancellableSubscriber = publisher.sink( receiveCompletion: { completion in switch completion { case .failure(let error): print(error) case .finished: print("Success") } }, receiveValue: { value in let decoder = JSONDecoder() do { // Since each value passed into our closure will be a tuple // containing the downloaded data, as well as the network // response itself, we're accessing the 'data' property here: let repo = try decoder.decode(Repository.self, from: value.data) print(repo) } catch { print(error) } } ) // --- Constructing a reactive chain of operators --- let repoPublisher = publisher .map(\.data) .decode( type: Repository.self, decoder: JSONDecoder() ) .receive(on: DispatchQueue.main) // --- Updating our UI based on our reactive chain --- // Two labels that we want to render our data using: let nameLabel = UILabel() let urlLabel = UILabel() let errorLabel = UILabel() let cancellableRepoSubscriber = repoPublisher.sink( receiveCompletion: { completion in switch completion { case .failure(let error): // Rendering a description of the error that was encountered: errorLabel.text = error.localizedDescription print("ErrorMsg: \(error.localizedDescription)") case .finished: break } }, receiveValue: { repo in // Rendering the downloaded repository's name and url: nameLabel.text = repo.name urlLabel.text = repo.htmlURL print("Name: \(repo.name)") print("URL: \(repo.htmlURL)") } )