Created
October 31, 2023 11:23
-
-
Save profh/d6543b9bf35914555b1c1e821f4c9e7d to your computer and use it in GitHub Desktop.
Combine Demo
This file contains 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
// 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)") | |
} | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment