I just started to learn Combine and therefore I can't figure out how to make a complex request to the API.
It is necessary to create an application where the user can enter the name of the company's GitHub account in the input field and get a list of open repositories and their branches.
There are two API methods:
struct for this method
struct Repository: Decodable {
let name: String
let language: String?
enum Seeds {
public static let empty = Repository(name: "", language: "")
}
}
struct for this method
struct Branch: Decodable {
let name: String
}
As a result, I need to get an array of such structures.
struct BranchSectionModel {
var name: Repository
var branchs: [Branch]
}
For this I have two functions:
func loadRepositorys(orgName: String) -> AnyPublisher<[Repository], Never> {
guard let url = URL(string: "https://api.github.com/orgs/\(orgName)/repos" ) else {
return Just([])
.eraseToAnyPublisher()
}
return URLSession.shared.dataTaskPublisher(for: url)
.map { $0.data }
.decode(type: [Repository].self, decoder: JSONDecoder())
.replaceError(with: [])
.receive(on: RunLoop.main)
.eraseToAnyPublisher()
}
and
func loadBranchs(orgName: String, repoName: String) -> AnyPublisher<[Branch], Never> {
guard let url = URL(string: "https://api.github.com/repos/\(orgName)/\(repoName)/branches") else {
return Just([])
.eraseToAnyPublisher()
}
return URLSession.shared.dataTaskPublisher(for: url)
.map { $0.data }
.decode(type: [Branch].self, decoder: JSONDecoder())
.replaceError(with: [])
.receive(on: RunLoop.main)
.eraseToAnyPublisher()
}
Both of these functions work separately, but I don't know how to end up with an [BranchSectionModel] . I guess to use flatMap and sink, but don't understant how.
I do not understand how to combine these two requests in one thread.
When you're looking to convert one publisher into another, .map
and .switchToLatest
. In this case, since you're also looking to turn one publisher into many (and then back down into one), MergeMany
will also be a useful tool:
loadRepositorys(orgName: orgName)
.map { repos in
Publishers.MergeMany(repos.map { repo in
loadBranchs(orgName: orgName, repoName: repo.name)
.map { branches in
BranchSectionModel(name: repo, branchs: branches)
}
})
.collect(repos.count)
}
.switchToLatest()
.sink { result in
print("---")
print(result)
}
.store(in: &cancellables)
Although I'm a big fan of Combine, I don't think it's particularly well suited to this task, compared with async/await, which will probably be a little less confusing and look cleaner. As a learning exercise, it's a great one, but if you were to tackle this problem in the real world, async/await would likely be my go-to.