Reputation: 11
There is a problem with a snapshot, the situation is: I have a collection with 3 sections made using compositional layout, there is also 3 different fetch services so the goal is to fill every section with 3 different pictures albums but when I try to do it pictures downloading into first or second section or into second and third, I can't understand how to fix it
First I've tried not to use a DispatchGroup - doesn't work at all and downloading only to the first section, tried to make 3 different snapshots for each section - doesn't work too
var snapshot = NSDiffableDataSourceSnapshot<SectionLayoutKind, Int>()
snapshot.appendSections([.one, .two, .three])
let group = DispatchGroup()
group.enter()
firstService.fetchFirst { [weak self] result in
defer {
group.leave()
}
switch result {
case .success(let fetchedFirstPhotos):
self?.firstPhotos = fetchedFirstPhotos
snapshot.appendItems(Array(0..<fetchedFirstPhotos.count), toSection: .one)
print(fetchedFirstPhotos)
case .failure(let error):
print(error)
}
}
group.enter()
secondService.fetchSecond { [weak self] result in
defer {
group.leave()
}
switch result {
case .success(let fetchedSecondPhotos):
self?.secondPhotos = fetchedSecondPhotos
snapshot.appendItems(Array(0..<fetchedSecondPhotos.count), toSection: .two)
print(fetchedSecondPhotos)
case .failure(let error):
print(error)
}
}
group.enter()
thirdService.fetchThird { [weak self] result in
defer {
group.leave()
}
switch result {
case .success(let fetchedThirdPhotos):
self?.thirdPhotos = fetchedThirdPhotos
snapshot.appendItems(Array(0..<fetchedThirdPhotos.count), toSection: .three)
print(fetchedThirdPhotos)
case .failure(let error):
print(error)
}
}
group.notify(queue: .main) {
self.dataSource.apply(snapshot, animatingDifferences: false)
}
Upvotes: 0
Views: 59
Reputation: 119242
The snapshot should be recreated and applied each time your data updates, regardless of the source.
Have a separate function that performs this:
func updateSnapshot() {
var snapshot = NSDiffableDataSourceSnapshot<SectionLayoutKind, Int>()
snapshot.appendSections([.one, .two, .three])
if firstPhotos.isEmpty == false {
snapshot.appendItems(Array(0..<firstPhotos.count), toSection: .one)
}
if secondPhotos.isEmpty == false {
snapshot.appendItems(Array(0..<secondPhotos.count), toSection: .two)
}
if thirdPhotos.isEmpty == false {
snapshot.appendItems(Array(0..<thirdPhotos.count), toSection: .three)
}
dataSource.apply(snapshot, animatingDifferences: false)
}
Fetching your data can then finish in any order / time you like and the UI will update progressively:
firstService.fetchFirst { [weak self] result in
switch result {
case .success(let fetchedFirstPhotos):
self?.firstPhotos = fetchedFirstPhotos
self?.updateSnapshot()
case .failure(let error):
print(error)
}
}
secondService.fetchSecond { [weak self] result in
switch result {
case .success(let fetchedSecondPhotos):
self?.secondPhotos = fetchedSecondPhotos
self?.updateSnapshot()
case .failure(let error):
print(error)
}
}
thirdService.fetchThird { [weak self] result in
switch result {
case .success(let fetchedThirdPhotos):
self?.thirdPhotos = fetchedThirdPhotos
self?.updateSnapshot()
case .failure(let error):
print(error)
}
}
Upvotes: 0
Reputation: 34
This is happening because the group is immediately notifying on main queue.
You need to understand how this API works. It matches the enter count and the leave count, when the count matches it proceeds to notify.
In your case the services respond faster and the block gets called, hence it only have 1 enter count and it matches with 1 leave count, so it calls the notify.
What I suggest is that you call enter 3 times before making the calls to service.
let group = DispatchGroup()
group.enter()
group.enter()
group.enter()
firstService.fetchFirst { .... }
secondService.fetchSecond { ... }
thirdService.fetchThird { ... }
group.notify(queue: .main) { [weak self] in
self?.dataSource.apply(snapshot, animatingDifferences: false)
}
Upvotes: 0