[feedhenry-dev] Let's talk about Swift: async completion closure

Corinne Krych corinnekrych at gmail.com
Thu Feb 25 10:55:56 UTC 2016


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.

*S0: Swift consuming ObjC sdk*

*Motivation:*
Swift app consuming our ObjC fh-io-ask will use the automatic conversion.
Default syntax helloworls-ios template [1].

*API looks like:*

> public class func initWithSuccess(sucornil: ((FHResponse!) -> Void)!,
> andFailure failornil: ((FHResponse!) -> Void)!)
> 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)!)


Note the extensive usage of implicit umwrap (the ! mark). As ObjC does not
have Optional.
Let's see how we can improve it.

*S1: More iOS standard lib: one completion block/closure*

*Motivation:*
One completion close to looks like iOS standard lib. For example, if you
look at [NSURLSession API documentation](
https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLSession_class/#//apple_ref/occ/instm/NSURLSession/dataTaskWithRequest:completionHandler:),
you can see the usage of `completionHandler`.

*API looks like:*

> public class func `init`(completionHandler: (Response, NSError?) -> Void )
> -> Void
> public class func performCloudRequest(path: String,  method: String,
> headers: [String:String]?, args: [String: String]?, config: Config =
> Config(), completionHandler: (Response, NSError?) -> Void) -> Void


Note the back tick around init as init is a reserved keyword in Swift.

*API usage:*

> FH.setup(config, completionHandler: { (resp: Response, err: NSError?) ->
> Void  in
>    if (err != nil) {
>      FH.performCloudRequest("/hello",  method: "POST", headers: nil, args:
> nil, config: config, completionHandler: {(resp: Response, err: NSError?) ->
> Void  in
>         // Do something
>       })
>    } else {
>      // Error in FH.init
>    }
> })


*Pro:*
Easy to use, close to ObjC API
*Con:*
Error code used, no usage os error handling

*S2: More Swift 2: Using Error handling for async, closure wrapper*

*Motivation:*
Make use of try/catch instead of NSError to looks more Swift2.
Refer to this excellent article:
http://alisoftware.github.io/swift/async/error/2016/02/06/async-errors/#hacking-it
or more on wrapper closure on
http://appventure.me/2015/06/19/swift-try-catch-asynchronous-closures/

*API looks like:*

> public class func `init`(completionHandler: (() throws -> Response) ->
> Void) -> Void
> public class func performCloudRequest(path: String,  method: String,
> headers: [String:String]?, args: [String: String]?, config: Config =
> Config(), completionHandler: (() throws -> Response) -> Void) -> Void


*API usage:*

> FH.setup(config, completionHandler: { (inner: () throws -> Response) ->
> Void in
>     do {
>         let result = try inner()
>         FH.performCloudRequest("/hello",  method: "POST", headers: nil,
> args: nil, config: config, completionHandler: { (inner: () throws ->
> Response) -> Void in
>             do {
>                 let result = try inner()
>             } catch _ {}
>         })
>     } catch _ {}
> })


*Pro:*
You do/catch your async method call.

*Con:*
* Not very elegant syntax
* Wrapper closure feels awkward.

*S3: More trendy: going "Monad"*

*Motivation:*
Going one step further, we create an enum container Result wich hold
"future" values

*API loos like:*

> public class func `init`(completionHandler: Result<Response> -> Void) ->
> Void
> public class func performCloudRequest(path: String,  method: String,
> headers: [String:String]?, args: [String: String]?, config: Config =
> Config(), completionHandler: Result<Response> -> Void) -> Void


*API usage:*

> FH.setup(config, completionHandler: { (result: Result<Response>) -> Void in
>     do {
>         let response = try result.resolve()
>         FH.performCloudRequest("/hello",  method: "POST", headers: nil,
> args: nil, config: config, completionHandler: { (result: Result<Response>)
> -> Void in
>             do {
>                 let result = try result.resolve()
>             } catch _ {/*ERROR */}
>         })
>     } catch _ {/*ERROR */}
> })


*Pro:*
Modern syntax close to promise/future

*Con:*
A bit far from the other Java/JS/C# syntax

*Conclusion*

After trying S2, I have to say it's quite awkward, I'd rather stick to S1.
S3 is really nice but does not fit with other languages API.
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.

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.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://listman.redhat.com/archives/feedhenry-dev/attachments/20160225/38ae69e3/attachment.htm>


More information about the feedhenry-dev mailing list