Zorro02
Zorro02

Reputation: 31

UITableView updating wrong cell

I have a Tableview with multiple cells of the same class. On changing the switch within the cell, a specific file should be downloaded.

Actual behaviour: The correct switch is changing its state and it is also stored even when scrolling away, so this is not the problem. The countryDesignator is correct when starting the download, it is also the correct file when finished, but when the file is stored, it uses the wrong countryDesignator and the progressBar is also wrong. In this particular case, hitting Canada will lead to a download of file [..]ca_asp.aip and the switch in the Canada cell changed, but the progressBar of the cell Austria is moving and the stored file is called at_asp_aip.

example

Code:

var countrySettings : [countryOptions] = [countryOptions(name: "Austria", isEnabled: false, progress: 0.0, filename: "at"),
                                      countryOptions(name: "Australia", isEnabled: false, progress: 0.0, filename: "au"),
                                      countryOptions(name: "Brazil", isEnabled: false, progress: 0.0, filename: "br"),
                                      countryOptions(name: "Canada", isEnabled: false, progress: 0.0, filename: "ca"), ...]

var downloadCount : Int = 0

class CountrySettings: UIViewController, UITableViewDataSource, UITableViewDelegate {

  @IBOutlet weak var countryTableView: UITableView!

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {        
    var cell = tableView.dequeueReusableCell(withIdentifier: "Country") as! countryCell  
    cell.countryName.text = countrySettings[indexPath.row].name
    cell.countrySwitch.isOn = countrySettings[indexPath.row].isEnabled
    cell.countryDesignator = countrySettings[indexPath.row].filename        
    return cell
}

class countryCell: UITableViewCell, URLSessionDownloadDelegate,UIDocumentInteractionControllerDelegate {
  @IBOutlet weak var countrySwitch: UISwitch!
  @IBOutlet weak var countryName: UILabel!
  var countryDesignator : String = "ad"

  @IBAction func didChangeSwitchValue(_ sender: UISwitch) {
   guard let indexPath = self.indexPath else { return }
        downloadCount = 0
        print(countryDesignator)   // prints correct Designator
        startDownloading()
  }

  func startDownloading () {   
    guard let indexPath = self.indexPath else { return }
    countrySettings[indexPath.row].isEnabled = countrySwitch.isOn
    DispatchQueue.main.async {
      print(self.countryDesignator)  // prints correct Designator
      downloadCount += 1 
      if downloadCount == 1 {
        let url = URL(string: "https://www.blablabla.com/" + self.countryDesignator + "_asp.aip")!
        self.downloadTask = self.defaultSession.downloadTask(with: url)
        self.downloadTask.resume()
      }
    }
  }


// MARK:- URLSessionDownloadDelegate
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
    print(countryDesignator)    //  prints WRONG!
    print("File download succesfully") 
    let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
    let documentDirectoryPath:String = path[0]
    let fileManager = FileManager()      
    var destinationURLForFile = URL(fileURLWithPath: documentDirectoryPath.appendingFormat("/" + countryDesignator + "_asp.aip"))

    if downloadCount == 2 {
        destinationURLForFile = URL(fileURLWithPath: documentDirectoryPath.appendingFormat("/" + countryDesignator + "_wpt.aip"))
    }   
    if fileManager.fileExists(atPath: destinationURLForFile.path){
        showFileWithPath(path: destinationURLForFile.path, completePath: destinationURLForFile)
        print(destinationURLForFile.path)
    }
    else{
        do {
            try fileManager.moveItem(at: location, to: destinationURLForFile)
            // load data into database
            showFileWithPath(path: destinationURLForFile.path, completePath: destinationURLForFile)
        }catch{
            print("An error occurred while moving file to destination url")
        }
    }
}

func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {

    DispatchQueue.main.async {
      print(self.countryDesignator) // prints WRONG
      guard let indexPath = self.indexPath else { return }
      self.progress.setProgress(Float(totalBytesWritten)/Float(totalBytesExpectedToWrite), animated: true)
      countrySettings[indexPath.row].progress = self.progress.progress
    }
}


func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
    downloadTask = nil
    progress.setProgress(0.0, animated: true)
    if (error != nil) {
        print("didCompleteWithError \(error?.localizedDescription ?? "no value")")
    }
    else {
        print("The task finished successfully")
        print(downloadCount)
        if downloadCount == 1 {
            startDownloading()
        }
    }
}

override func awakeFromNib() {
    super.awakeFromNib()    
    let backgroundSessionConfiguration = URLSessionConfiguration.background(withIdentifier: "backgroundSession" + countryDesignator)
    defaultSession = Foundation.URLSession(configuration: backgroundSessionConfiguration, delegate: self, delegateQueue: OperationQueue.main)    
    progress.setProgress(0.0, animated: false)   // This is working correctly
  }
}

Upvotes: 1

Views: 334

Answers (1)

Codus
Codus

Reputation: 1473

This is a common problem. Because UITableViewCell is reuseable, so you save the cell but the cell will presenting different datasource when scrolling.

You should not save the cell but record what you are downloading depends on datasource.

    func startDownloading () {
        guard let indexPath = self.indexPath else { return }
        countrySettings[indexPath.row].isEnabled = countrySwitch.isOn
        countrySettings[indexPath.row].isDownloading = true
        DispatchQueue.main.async {
            print(self.countryDesignator)  // prints correct Designator
            downloadCount += 1
            if downloadCount == 1 {
                let url = URL(string: "https://www.blablabla.com/" + self.countryDesignator + "_asp.aip")!
                countrySettings[indexPath.row].downloadTask = self.defaultSession.downloadTask(with: url)
                countrySettings[indexPath.row].downloadTask.resume()
            }
        }
    }

And always change the countryDesignator to which cell presenting this downloading datasource.

Upvotes: 1

Related Questions