I've got this network call for fetching images.
func load() {
guard let url = URL(string: urlString)
else { return }
subscription = URLSession.shared.dataTaskPublisher(for: url)
.map({ UIImage(data: $0.data) })
.replaceError(with: nil)
.receive(on: RunLoop.main)
.sink(receiveValue: { [weak self] in self?.image = $0 })
}
This is triggered by the text field being filled. After each letter is typed, I would like my publisher to wait with the execution for let's say 2 seconds, unless a user typed another letter. If that happens, I'd like the timer to reset to 2 seconds again.
Is there any on the fly cancel operator if the new request has been sent in the meanwhile?
Thanks for all the help.
CodePudding user response:
This is a pretty standard thing to do with a reactive library and easily accomplished. The switchToLatest
operator is the one that does the magic.
@available(iOS 14.0, *)
final class ViewController: UIViewController {
var textField: UITextField!
var image: UIImage?
var cancelBag = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
textField.textPublisher // this is from the `CombineCocoa` library
.debounce(for: 2, scheduler: RunLoop.main)
.compactMap { makeURL(from: $0) }
.map {
URLSession.shared.dataTaskPublisher(for: $0)
.catch { _ in Empty() } // what do you want to do with loading errors?
}
.switchToLatest()
.map { UIImage(data: $0.data) }
.receive(on: RunLoop.main)
.sink(receiveValue: { [weak self] in self?.image = $0 })
.store(in: &cancelBag)
}
}
func makeURL(from: String?) -> URL? {
// build and return the correct URL for the image request
fatalError()
}