Skip to content

Instantly share code, notes, and snippets.

@rnapier
Last active February 3, 2018 23:20
Show Gist options
  • Save rnapier/b1f13be8d018bf4d145b to your computer and use it in GitHub Desktop.
Save rnapier/b1f13be8d018bf4d145b to your computer and use it in GitHub Desktop.
// Previous async code based on Result. A "Search" wraps an async operation and offers features like cancel().
// An earlier experiment includes self.pages = result ?? [], but that's cheating.
// If you didn't care about error handling, what's the point of Result? Added displayError() to test reality.
// Of course maybe "self.pages = result ?? { displayError(err); return [] }()" would work,
// but that gets a little obtuse and scales poorly.
func search(text: String, completionHandler: (Result<[Page]>) -> Void) -> Search { ... }
self.currentSearch = self.searcher.search(searchString, completionHandler: { result in
switch result {
case .Success(let pages):
self.pages = pages
case .Failure(let err):
displayError(err)
self.pages = []
}
self.tableView.reloadData()
}
/////
// New idea based on throws. Note use of "() throws -> T" as a replacement for Either
// The closure is a little funny, but it's actually shorter and less "noisy" than the Result approach
// when you take real error handling into account (calling displayError)
func search(text: String, completionHandler: (() throws -> [Page]) -> Void) -> Search { ... }
self.currentSearch = search(searchString, completionHandler: { result in
do {
try self.pages = result()
} catch {
displayError(err)
self.pages = []
}
self.tableView.reloadData()
}
/////////
// Here's the Result-way that I handled processing that completion handler:
struct Operation<ResultType> {
let task: NSURLSessionDataTask
init(url: NSURL, queue: NSOperationQueue, parser: NSData -> Result<ResultType>, completionHandler: Result<ResultType> -> Void) {
let handler = operationHandler(queue: queue, parser: parser, completionHandler: completionHandler)
self.task = NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: handler)
self.task.resume()
}
func cancel() {
self.task.cancel()
}
}
private func operationHandler<T>(#queue: NSOperationQueue, #parser: NSData -> Result<T>, #completionHandler: Result<T> -> Void)
(data: NSData?, _: NSURLResponse?, error: NSError?) {
switch (data, error) {
case (_, .Some(let error)) where error.isCancelled():
break // Ignore cancellation
case (_, .Some(let error)):
queue.addOperationWithBlock({completionHandler(.Failure(error))})
case (.Some(let data), _):
queue.addOperationWithBlock({completionHandler(parser(data))})
default:
fatalError("Did not receive an error or data.")
}
}
/////////
// And here's the throw-wrapped way:
struct Operation<ResultType> {
let task: NSURLSessionDataTask?
init(url: NSURL, queue: NSOperationQueue, parser: (NSData) throws -> ResultType, completionHandler: (() throws -> ResultType) -> Void) {
let handler = operationHandler(queue: queue, parser: parser, completionHandler: completionHandler)
self.task = NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: handler)
self.task?.resume()
}
func cancel() {
self.task?.cancel()
}
}
private func operationHandler<T>(queue queue: NSOperationQueue, parser: (NSData) throws -> T, completionHandler: (() throws -> T) -> Void)
(data: NSData?, _: NSURLResponse?, error: NSError?)
{
switch (data, error) {
case (_, .Some(let error)) where error.isCancelled():
break // Ignore cancellation
case (_, .Some(let error)):
queue.addOperationWithBlock {completionHandler({ throw error })}
case (.Some(let data), _):
queue.addOperationWithBlock {completionHandler({ try parser(data) })}
default:
fatalError("Did not receive an error or data.")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment