Background
I am engaged on a UITableView
that masses with preloaded information, then fetches extra objects asynchronously. Once I spam pull-to-refresh, every pull triggers a brand new async request, leading to a number of callbacks and duplicate objects.
I would like to make sure that:
Solely the newest loadItems()
name’s async outcomes are processed.
Earlier async requests are ignored or canceled, avoiding duplicate objects.
Essential
When calling loadItems()
, it may doubtlessly create a brand new occasion with the identical information every time. Nevertheless, as a result of every occasion is technically distinctive, merely checking if the merchandise already exists within the objects
array earlier than including it will not work.
To deal with this, I would like a strategy to cancel any earlier pending requests every time pull-to-refresh is triggered, guaranteeing solely the newest request is processed.
Additional
I attempted checking if the merchandise already exists within the objects
array earlier than including it, however this method fails as a result of loadItems()
can return a brand new occasion every time, even when the info is an identical. On this instance, it is only a easy String sort, however within the precise situation, the info sort is extra advanced.
Code
import UIKit
closing class AsyncProvider {
func loadItems(completion: @escaping ([String]) -> Void) {
DispatchQueue.most important.asyncAfter(deadline: .now() + 5.0) {
completion(["Async Item"])
}
}
}
closing class ViewController: UIViewController, UITableViewDataSource {
non-public let tableView = UITableView()
non-public var objects: [String] = ["Item A", "Item B", "Item C"] // Preliminary objects
non-public var hasReloaded = false
non-public let asyncProvider = AsyncProvider()
non-public let refreshControl = UIRefreshControl() // Pull-to-refresh management
override func viewDidLoad() {
tremendous.viewDidLoad()
// Fundamental desk view setup
tableView.dataSource = self
view.addSubview(tableView)
tableView.body = view.bounds
// Arrange pull-to-refresh management
refreshControl.addTarget(self, motion: #selector(refreshTableView), for: .valueChanged)
tableView.refreshControl = refreshControl
// Preliminary information load
loadItems()
}
// Pull-to-refresh motion
@objc non-public func refreshTableView() {
loadItems()
refreshControl.endRefreshing() // Finish the refresh animation
}
// Load extra async objects
non-public func loadItems() {
asyncProvider.loadItems { [weak self] newItems in
guard let self = self else { return }
if self.hasReloaded {
self.objects.insert(contentsOf: newItems, at: 0)
self.tableView.performBatchUpdates({
self.tableView.insertSections(IndexSet(integer: 0), with: .computerized)
}, completion: nil)
} else {
self.objects.append(contentsOf: newItems)
}
}
self.hasReloaded = true
tableView.reloadData()
}
func numberOfSections(in tableView: UITableView) -> Int {
return objects.depend
}
func tableView(_ tableView: UITableView, numberOfRowsInSection part: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(type: .default, reuseIdentifier: "cell")
cell.textLabel?.textual content = objects[indexPath.section]
return cell
}
}