Skip to content

Instantly share code, notes, and snippets.

@profh
Created October 31, 2023 11:23
Show Gist options
  • Save profh/d6543b9bf35914555b1c1e821f4c9e7d to your computer and use it in GitHub Desktop.
Save profh/d6543b9bf35914555b1c1e821f4c9e7d to your computer and use it in GitHub Desktop.
Combine Demo
// 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