<div dir="ltr"><div>As we start writing the FH Swift SDK, I'd like to discuss the Swift syntax of our API. In this thread, I'd like to focus on how to express async completion. Let's see in detail the syntax of FH.init and FH.cloud.<br></div><div><br></div><div><font size="4"><b>S0: Swift consuming ObjC sdk</b></font></div><div><b><br></b></div><div><b>Motivation:</b></div><div>Swift app consuming our ObjC fh-io-ask will use the automatic conversion. Default syntax helloworls-ios template [1].</div><div><b><br></b></div><div><b>API looks like:</b></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">public class func initWithSuccess(sucornil: ((FHResponse!) -> Void)!, andFailure failornil: ((FHResponse!) -> Void)!)<br>public class func performCloudRequest(path: String!, withMethod requestMethod: String!, andHeaders headers: [NSObject : AnyObject]!, andArgs arguments: [NSObject : AnyObject]!, andSuccess sucornil: ((FHResponse!) -> Void)!, andFailure failornil: ((FHResponse!) -> Void)!)</blockquote><div> </div><div>Note the extensive usage of implicit umwrap (the ! mark). As ObjC does not have Optional. </div><div>Let's see how we can improve it.</div><div><br></div><div><font size="4"><b>S1: More iOS standard lib: one completion block/closure</b></font></div><div><b><br></b></div><div><b>Motivation:</b></div><div>One completion close to looks like iOS standard lib. For example, if you look at [NSURLSession API documentation](<a href="https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLSession_class/#//apple_ref/occ/instm/NSURLSession/dataTaskWithRequest:completionHandler:">https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLSession_class/#//apple_ref/occ/instm/NSURLSession/dataTaskWithRequest:completionHandler:</a>), you can see the usage of `completionHandler`. </div><div><b><br></b></div><div><b>API looks like:</b></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">public class func `init`(completionHandler: (Response, NSError?) -> Void ) -> Void<br>public class func performCloudRequest(path: String,  method: String, headers: [String:String]?, args: [String: String]?, config: Config = Config(), completionHandler: (Response, NSError?) -> Void) -> Void</blockquote><div><br></div><div>Note the back tick around init as init is a reserved keyword in Swift.</div><div><br></div><div><b>API usage:</b></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">FH.setup(config, completionHandler: { (resp: Response, err: NSError?) -> Void  in<br>   if (err != nil) {<br>     FH.performCloudRequest("/hello",  method: "POST", headers: nil, args: nil, config: config, completionHandler: {(resp: Response, err: NSError?) -> Void  in<br>        // Do something<br>      })<br>   } else {<br>     // Error in FH.init<br>   }<br>})</blockquote><div><br></div><div><b>Pro:</b></div><div>Easy to use, close to ObjC API</div><div><b>Con:</b></div><div>Error code used, no usage os error handling</div><div><font size="4"><b><br></b></font></div><div><font size="4"><b>S2: More Swift 2: Using Error handling for async, closure wrapper</b></font></div><div><br></div><div><b>Motivation:</b></div><div>Make use of try/catch instead of NSError to looks more Swift2.</div><div>Refer to this excellent article: <a href="http://alisoftware.github.io/swift/async/error/2016/02/06/async-errors/#hacking-it">http://alisoftware.github.io/swift/async/error/2016/02/06/async-errors/#hacking-it</a></div><div>or more on wrapper closure on <a href="http://appventure.me/2015/06/19/swift-try-catch-asynchronous-closures/">http://appventure.me/2015/06/19/swift-try-catch-asynchronous-closures/</a></div><div><br></div><div><b>API looks like:</b></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">public class func `init`(completionHandler: (() throws -> Response) -> Void) -> Void <br>public class func performCloudRequest(path: String,  method: String, headers: [String:String]?, args: [String: String]?, config: Config = Config(), completionHandler: (() throws -> Response) -> Void) -> Void</blockquote><div><br></div><div><b>API usage:</b></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">FH.setup(config, completionHandler: { (inner: () throws -> Response) -> Void in<br>    do {<br>        let result = try inner()<br>        FH.performCloudRequest("/hello",  method: "POST", headers: nil, args: nil, config: config, completionHandler: { (inner: () throws -> Response) -> Void in<br>            do {<br>                let result = try inner()<br>            } catch _ {}<br>        })        <br>    } catch _ {}<br>})</blockquote><div><br></div><div><b>Pro:</b></div><div>You do/catch your async method call. </div><div><br></div><div><b>Con:</b></div><div>* Not very elegant syntax</div><div>* Wrapper closure feels awkward.</div><div><br></div><div><font size="4"><b>S3: More trendy: going "Monad"</b></font> </div><div><b><br></b></div><div><b>Motivation:</b></div><div>Going one step further, we create an enum container Result wich hold "future" values</div><div><br></div><div><b>API loos like:</b></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">public class func `init`(completionHandler: Result<Response> -> Void) -> Void<br>public class func performCloudRequest(path: String,  method: String, headers: [String:String]?, args: [String: String]?, config: Config = Config(), completionHandler: Result<Response> -> Void) -> Void</blockquote><div><b><br></b></div><div><b>API usage:</b></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">FH.setup(config, completionHandler: { (result: Result<Response>) -> Void in<br>    do {<br>        let response = try result.resolve()<br>        FH.performCloudRequest("/hello",  method: "POST", headers: nil, args: nil, config: config, completionHandler: { (result: Result<Response>) -> Void in<br>            do {<br>                let result = try result.resolve()<br>            } catch _ {/*ERROR */}<br>        })<br>    } catch _ {/*ERROR */}<br>})</blockquote><div><br></div><div><b>Pro:</b></div><div>Modern syntax close to promise/future</div><div><br></div><div><b>Con:</b></div><div>A bit far from the other Java/JS/C# syntax</div><div><font size="4"><b><br></b></font></div><div><font size="4"><b>Conclusion</b></font></div><div><font size="4"><b><br></b></font></div><div>After trying S2, I have to say it's quite awkward, I'd rather stick to S1.</div><div>S3 is really nice but does not fit with other languages API.</div><div>On aside note, I'd also recommand that we should shorten method name ie: `performCloudRequest:method:headers:args:completionHandler:` will become `cloud:method:headers:args:completionHandler:` to stick closer to JavaScript syntax.</div><div><br></div><div>In my opinion, S1 is easy route making the ObjC/Swift transition easy. This is my point of view, but I'd love to hear from you.</div><div><br></div></div>