> On Jun 6, 2016, at 3:48 PM, michael.petern...@gmx.at wrote:
> 
> Yes, ok...
> 
> Well, I cannot easily make everything synchronous. I just can change it in a 
> way that makes a `@required` attribute "optional" ;)
> 
> import Foundation
> 
> enum Result<T> {
>    case success(T)
>    case error(ErrorType)
> }
> 
> enum MyError: ErrorType {
>    case unknownError
>    case corruptData
>    case badStatusCode(Int)
> }
> 
> struct SomeThing {
>    init?(data: NSData) {
>        ...
>    }
> }
> 
> func getSomethingFromTheNetwork(url: NSURL, completionHandler: 
> (Result<SomeThing>) -> ()) { 
>    func toSomeThing(data: NSData?, response: NSURLResponse?, error: NSError?) 
> -> Result<SomeThing> {
>        if let error = error {
>            return .error(error)
>        }
> 
>        if let httpResponse = response as? NSHTTPURLResponse {
>            let statusCode = httpResponse.statusCode
> 
>            if statusCode < 200 || statusCode >= 300 {
>                return .error(MyError.badStatusCode(statusCode))
>            }
>        }
> 
>        guard let data = data else {
>            return .error(MyError.unknownError)
>        }
> 
>        guard let something = SomeThing(data: data) else {
>            return .error(MyError.corruptData)
>        }
> 
>        return .success(something)
>    }
>    let task = NSURLSession.sharedSession().dataTaskWithURL(url) { data, 
> response, error in
>        completionHandler(toSomething(data, response, error))
>    }
> 
>    task.resume()
> }

I’ve written code like that; however, it falls down as soon as you have to nest 
two or more asynchronous APIs. Consider the following, and assume we don’t have 
control of SomeThing’s API (or, maybe, SomeThing relies on some other API we 
don’t control that has to be asynchronous):

import Foundation

enum Result<T> {
   case success(T)
   case error(ErrorType)
}

enum MyError: ErrorType {
   case unknownError
   case corruptData
   case badStatusCode(Int)
}

struct SomeThing {
   make(data: NSData, completionHandler: (SomeThing?) -> ())
}

func getSomethingFromTheNetwork(url: NSURL, completionHandler: 
(Result<SomeThing>) -> ()) {     
        let task = NSURLSession.sharedSession().dataTaskWithURL(url) { data, 
response, error in
                if let error = error {
                        completionHandler(.error(error))
                        return
                }

                if let httpResponse = response as? NSHTTPURLResponse {
                        let statusCode = httpResponse.statusCode
                        
                        if statusCode < 200 || statusCode >= 300 {
                                
completionHandler(.error(MyError.badStatusCode(statusCode)))
                                return
                        }
                }
        
                guard let data = data else {
                        completionHandler(.error(MyError.unknownError))
                        return
                }

                SomeThing.make(data) { something in
                        if let something = something {
                                completionHandler(.success(something))
                        } else {
                                completionHandler(.error(MyError.corruptData))
                        }
                }
        }
        
        task.resume()
}

Yeah, you can wrap it in yet another function, but if things get complex 
enough, and you forget to do that somewhere, it’s bad news.

> With a semaphore, I can also make it synchronous. Not sure if this is a good 
> idea though... If the API is already asynchronous, it's probably better to 
> use it that way.

It’s not a good idea. As I mentioned before, Apple recommends against blocking 
on dispatch queues. It’s not considered good practice. That includes the main 
queue, by the way;  both Cocoa-Dev and Stack Overflow are filled with questions 
from people who tried using dispatch_sync or dispatch_async to the main queue 
to run -[NSOpenPanel runModal], and then got confused when nothing in the panel 
displayed properly. Using the async methods on NSOpenPanel and returning via a 
completion handler would have caused this to work properly.

Charles

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to