Skip to content

Instantly share code, notes, and snippets.

@brentsimmons
Created April 8, 2016 17:35
Show Gist options
  • Select an option

  • Save brentsimmons/387c5ec75aa1a5373d929fd9f1ae5f43 to your computer and use it in GitHub Desktop.

Select an option

Save brentsimmons/387c5ec75aa1a5373d929fd9f1ae5f43 to your computer and use it in GitHub Desktop.

Revisions

  1. brentsimmons created this gist Apr 8, 2016.
    139 changes: 139 additions & 0 deletions gist.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,139 @@
    // This is the traditional version of the RxSwift-based gist posted here:
    // https://gist.github.com/cliss/51cb740b14f3cd56ba1d11f2a9a6ba02
    // This won’t compile and it surely has errors.
    // (The same may be true as the original.)
    // Some things are obviously omitted.
    // It's meant as illustrative rather than as actual running code.
    //
    // The problem being solved:
    // There is a text field. When you type in it, all changes are coalesced
    // for 0.3 seconds, and then an HTTP call is made, and a table is updated
    // with results.
    // Also: there's a Refresh button, and tapping the Refresh button
    // runs that same HTTP call and updates the table with results.
    // Whenever there's an existing HTTP call, and a new one has been triggered,
    // the existing HTTP call should be canceled.

    struct Result {
    let text: String
    let someOtherThing: String
    }

    typealias FetcherCallback = (result: Result?, error: NSError?) -> Void

    class Fetcher {

    let task: NSURLSessionDataTask

    init(query: String, callback: FetcherCallback) {

    //Assume the url was created based on the query. And that myURLSession came from somewhere.
    self.task = myURLSession.dataTaskWithURL(url) { data, response, error in {

    if let error = error {
    callback(nil, error)
    return
    }
    let results: [Result] = something //make results from the data; obvious omission for the sake of argument
    callback(results, nil)
    }
    }

    self.task.resume()
    }

    func cancel() {
    task.cancel()
    }
    }

    class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    private weak var tableView: UITableView!
    private weak var textField: UITextField!
    private var fetchTimer: NSTimer?
    private var fetcher: Fetcher?

    private var results = [String]() {
    didSet {
    tableView.reloadData()
    }
    }

    private var currentText = "" {
    didSet {
    if currentText.count < 4 {
    invalidateFetchTimer()
    }
    else {
    resetFetchTimer()
    }
    }
    }

    deinit {

    invalidateFetchTimer()
    cancelExistingFetch()
    }

    override func viewDidLoad() {

    textField.delegate = self
    }

    func textDidChange(sender: AnyObject) {

    if currentText != textField.stringValue {
    currentText = textField.stringValue
    }
    }

    private func invalidateFetchTimer() {

    if let timer = fetchTimer where fetchTimer.isValid {
    timer.invalidate()
    }
    fetchTimer = nil
    }

    private func resetFetchTimer() {

    invalidateFetchTimer()
    fetchTimer = NSTimer(timeInterval: 0.3, target: self, selector: #selector(fetchTimerDidFire(_:)), userInfo: nil, repeats: false)
    }

    dynamic func fetchTimerDidFire(sender: AnyObject) {

    runFetch()
    }

    private func cancelExistingFetch() {

    if let existingFetcher = fetcher {
    existingFetcher.cancel()
    }
    fetcher = nil
    }

    private func runFetch() {

    invalidateFetchTimer()
    cancelExistingFetch()

    fetcher = Fetcher(text: currentText) { result, error in
    if let error = error {
    //Handle it.
    }
    else {
    self.results = result.map { oneResult in return oneResult.text }
    }
    }
    }

    @IBAction func refresh(sender: AnyObject) {

    runFetch()
    }
    }