Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save kittinunf/64e9194ca0fd25209a5884df16abb8cb to your computer and use it in GitHub Desktop.
Save kittinunf/64e9194ca0fd25209a5884df16abb8cb to your computer and use it in GitHub Desktop.
//a somewhat contrived example of how reactive programming can be useful
//some real concerns that are omitted, but can be easily accomplished using rxswift are
//error handling, displaying loading indicators, etc
// A result object that comes from the network.
// The contents are irrelevant for this example.
struct Result {
let text: String
let someOtherThing: String
}
// An object that can get data from the network
struct Fetcher {
/**
Performs a network query, and returns 0 or more results as an array.
- Parameter query: The query to complete. Comes from the UI
- Returns: An observable with 0 or more results
*/
static func performQuery(query: String) -> Observable<[Result]> {
// Go to the network
// Get data
// Transform into Result objects
// If the returned Observable is disposed, it will cancel the network request.
Observable.create { observer in
//standard building of a NSURLSessionDataTask
let task = myURLSession.dataTaskWithURL(url) { data, response, error in
guard let error == nil else {
//inform the observer there was an error
observer.onError(error)
return
}
let results: [Result] = //make results from the data
//inform the observer there were results
observer.onNext(results)
//inform the observer this observable will not send any more results
observer.onComplete()
}
//start the data task
task.resume()
//!!!! we return a Disposable instance that executes the given block when the observer is disposed of
//this is what gives us the "cancel inflight request" behavior
return AnoymousDisposable {
task.cancel()
}
}
}
}
/**
Some UI View Controller that has a UITextField on it
*/
class ViewController: UIViewController: UITableViewDataSource, UITableViewDelegate {
weak var tableView: UITableView!
weak var textField: UITextField!
weak var button: UIButton!
//in your post, you described dispose bags as a "parallel memory mangement system"
//i liken them to more of an "resource clean up system"
//much like closing a file handle, or unsubscribing form NSNotifications
let disposeBag = DisposeBag()
//an array of strings we display in our table view
var results = [String]()
override func viewDidLoad() {
// Get the text field, throttled, filtered, distinct
let o: Observable<String> =
textField.rx_text
//only send me the text field's value every 3/10th of a second
.throttle(0.3)
//only send me the text field's value when it is different than before
.distinctUntilChanged()
//filter out queries less than 3 characters
.filter { query in return query.characters.count > 3 }
// Get button taps *with the latest value of the text field*
let p: Observable<String> = button.rx_tap.withLatestFrom(o)
// Merge the two and then carry on like our prior example.
// Merge requires observables with the same elemnt type.
//
// In short, the last value from o gets put into the
// observable stream for p; if *either* happens we should
// signal the observable.
let q: Observable<String> = Observable.of(o, p).merge()
//map expects that you return a type that can be converted to an observable
//such as an array which you see below in the other map
//since we are returning a function that returns an observable
//we need to flatten it so that we don't end up with a returned type of Observable<Observable<[Result]>>
//the "latest" uses the latest value emitted by the text field and disposes the other in-flight observables (network requests)
q.flatMapLatest { query in
Fetcher.performQuery(query)
//convert the Result struct into an array of strings
}.map { results in
// Extract the text out of this object; standard Swift.
results.map { result in return result.text }
}
//at this point we have an Observable<[String]> `o` that we can "subscribe" to
//subscribing to o will emit a new array of strings for every completed network request,
//which we use as the data in a table view
//there are better ways to display data in a tableview using rxcocoa, but for this simple example it should suffice
//any errors that may have happened will bubble up to our observable (ie, the network request failed),
//which we can subscribe to as well with a subscribeError(onError:((ErrorType) -> Void)). this has been omitted for clarity
o.subscribeNext { [weak self] results in
self?.results = results
self?.tableView.reloadData()
}
//table view code omitted for clarity
}
@kittinunf
Copy link
Author

Make .swift so it reads much better with syntax highlighting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment