Maximovic
Maximovic

Reputation: 11

Fetching services through NSDiffableDataSourceSnapshot

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

Answers (2)

jrturton
jrturton

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

Krishnapalsinh Gohil
Krishnapalsinh Gohil

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

Related Questions