Swift - Make async calls in collection view - Resetting data via prepareForReuse() method doesn't work

29 Views Asked by At

I have this method to put data into my dataSource array which is used for my collection view

private func fetchInitialData() {
    
    for index in self.HITSSOURCE_CHALLENGE_COPY_COUNT..<self.challengeHitsInteractor.numberOfHits() {
        
        if let jsonChallengeObject: Hit<JSON> = try? self.challengeHitsInteractor.hit(atIndex: index),
            
            let challengeObject: Challenge = try? Challenge(json: jsonChallengeObject.object) {
            let challengeProfileItem = ChallengeProfileItem()
            challengeProfileItem.challengeObject = challengeObject
            self.HITSSOURCE_CHALLENGE_COPY_COUNT = self.challengeHitsInteractor.numberOfHits()
            self.dataSource.append(challengeProfileItem)
            
        }
        
    }

    for index in self.HITSSOURCE_TOURNAMENT_COPY_COUNT..<self.tournamentHitsInteractor.numberOfHits() {
       
        if let jsonTournamentObject: Hit<JSON> = try? self.tournamentHitsInteractor.hit(atIndex: index),
            
            let tournamentObject: Tournament = try? Tournament(json: jsonTournamentObject.object) {
            let tournamentProfileItem = TournamentProfileItem()
            tournamentProfileItem.tournamentObject = tournamentObject
            self.HITSSOURCE_TOURNAMENT_COPY_COUNT = self.tournamentHitsInteractor.numberOfHits()
            self.dataSource.append(tournamentProfileItem)
            
        }
    }
    
    self.eventCollectionView?.reloadData()
}^

Now, in the cellForItemAt delegate method, I am iterating through this dataSource array

 func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    
    self.hideActivityView()
    
    let myChallengesCell = collectionView.dequeueReusableCell(withReuseIdentifier: MyEventsCollectionViewCell.reuseIdentifier, for: indexPath) as! MyEventsCollectionViewCell
    
    myChallengesCell.delegate = self

    if (self.challengeHitsInteractor.numberOfHits() + self.tournamentHitsInteractor.numberOfHits() == 0) {
        
        return myChallengesCell
        
    }
    
    if self.displayedIndices.contains(indexPath.row) {
       
        return myChallengesCell
        
    }
    
    else {
        
        let abstractProfileItem = self.dataSource[indexPath.row]
        self.displayedIndices.insert(indexPath.row)
        
        myChallengesCell.indexPath = indexPath
        
        switch abstractProfileItem.challengeType {
           
            case .singleChallenge:
                
                var challengeProfileItem = abstractProfileItem as! ChallengeProfileItem
                challengeProfileItem = myChallengesCell.setupCellWithChallengeProfileItem(challengeObject: challengeProfileItem.challengeObject) as! ChallengeProfileItem
                myChallengesCell.setEventTitle(abstractEvent: challengeProfileItem.challengeObject)
                self.dataSource[indexPath.row] = challengeProfileItem

..

in my MyEventsCollectionViewCell cell, I setup some properties synchronously, however, some other data need to be fetched from the database on the fly

 public func setupCellWithChallengeProfileItem(challengeObject:Challenge) -> AbstractProfileItem {
    
    self.challengeProfileItem = ChallengeProfileItem()
    self.challengeProfileItem?.delegate = self
    
    self.setEventTitle(abstractEvent: challengeObject)
    self.checkIfToAddOrRemovePlayIcon(abstractEvent: challengeObject)
    
    self.challengeProfileItem.challengeObject = challengeObject

    challengeObject.eventType = .singleChallenge
    
    DispatchQueue.main.async {
        
        self.challengeProfileItem.populateChallengeObject(challengeObject: challengeObject)
        
    }

    return self.challengeProfileItem
    
}

The ChallengeProfileItem looks like this

public func populateChallengeObject(challengeObject:Challenge)  {
    
    self.userDataAccessService.checkIfUserIsBlocked(completionHandler: { (userExists, isSourceUserBlocked, isTargetUserBlocked, userId) in
        
        if (!userExists || isSourceUserBlocked || isTargetUserBlocked) {
            
            return
            
        }
        
        else {
            
            self.challengeParticipationDataAccessService.fetchParticipationByUserIdChallengeId(completionHandler: { (participationObject) in
                   
                self.ratingDataAccessService.fetchRatingByEventIdAndParticipantId(completionHandler: { (numberOfRating) in
                       
                    self.numberOfRating = numberOfRating
                                        
                    self.delegate?.didFetchRating(eventType: .singleChallenge, eventId: challengeObject.id, rating: numberOfRating)
                                          
                }, participantId: participationObject.userId, eventId: participationObject.challengeId)
                                           
            }, userId: challengeObject.organizerId!, challengeId: challengeObject.id!)
                
                
            self.challengeResourceService.identifyRankingOfParticipant(completionHandler: { (ranking, numberOfParticipants) in
                   
                self.ranking = ranking
                self.numberOfParticipants = numberOfParticipants
                                  
                self.delegate?.didFetchRankingAndNumberOfParticipants(eventType: .singleChallenge, eventId: challengeObject.id, ranking: ranking, numberOfParticipants: numberOfParticipants)
                
            }, challengeId: challengeObject.id!)
            
            
            self.challengeDataAccessService.fetchDownloadURLOfChallengeImage(challengeId: challengeObject.id!) { (contentURL) in
                
                self.delegate?.didFetchContentURL(eventType: .singleChallenge, eventId: challengeObject.id, contentURL: contentURL!)
            }
            
        }

   }, sourcePersonId: Auth.auth().currentUser?.uid, targetPersonId: challengeObject.organizerId!)
}

Once the data is fetched, I go back to the view with my collection view by using delegates. On the view where my collection view is located, I'll try to update the specific cell by using an index.

extension MyEventsView:MyEventsCollectionViewCellDelegate {

 func didFetchContentURL(eventType: EventType, eventId: String, contentURL: URL) {
  
    if let index = self.findIndexForEventItem(withID: eventId) {
       
        let indexPath = IndexPath(row: index, section: 0)

       
        DispatchQueue.main.async {
          
            if let cell = self.eventCollectionView?.cellForItem(at: indexPath) as? MyEventsCollectionViewCell {
                
                self.dataSource[indexPath.row].contentURL = contentURL
                
                let challengeProfileItem = self.dataSource[indexPath.row] as! ChallengeProfileItem
                print(challengeProfileItem.challengeObject.title)
                print(cell.challengeTitleLabel?.text)
                
                cell.challengeImageView!.sd_setImage(with: contentURL, placeholderImage: UIImage(named: "placeholder.png"))
                
            }
        }
    }
}

It looks like, there is a mismatch of data. The displayed image doesn't match the challenge title attribute. I know cells are designed to be re-used but when I tried to reset the data within the prepareForReuse() method, the challenge title was displayed at all.

I am struggling with this issue unfortunately..

0

There are 0 best solutions below