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

Corinne Krych corinnekrych at gmail.com
Thu Feb 25 16:11:09 UTC 2016


@summers That would go even deeper in FRP.
We should list it as:
S4: More reactive way
I don't have code snippet for it but i'll do some.
btw all the other code snippets comes have an "trial" implementation [1].
pro:
Rx way fits for asynch data stream
con:
A bit far from the other Java/JS/C# existing syntax

For the initial Swift SDK, I'd stick to S1 to be in sync with other
language SDK.
But I see where you're coming from Summers and I like the discussion. Let
me come back to you with some Swifty code snippets.
++
Corinne

On 25 February 2016 at 15:43, Summers Pittman <supittma at redhat.com> wrote:

>
>
> On Thu, Feb 25, 2016 at 5:55 AM, Corinne Krych <corinnekrych at gmail.com>
> wrote:
>
>> 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.
>>
>>
> Might I suggest using events instead?
>
> Sorry for the Java but I'm thinking something like this :
>
> public class SplashScreen extends AppCompatActivity {
>
>     FHClient client = new FHClient();
>
>     @Override
>     protected void onStart() {
>         super.onStart();
>         fhClient.registerEventHandler(this)
>         if (!fhClient.isInit()) {
>             fhClient.init();
>         }
>     }
>
>     public void onInit(InitSuccessful event) {
>         //Client is initted
>     }
>     public void onInitError(final InitFailed event) {
>         //some kind of init failure.
>     }
> }
>
>
> I'm borrowing patterns that Google Play uses in Android proper, and I
> would like to see the Android SDK also adopt an event driven model (perhaps
> using an event bus mechanism)
>
>
>> _______________________________________________
>> feedhenry-dev mailing list
>> feedhenry-dev at redhat.com
>> https://www.redhat.com/mailman/listinfo/feedhenry-dev
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://listman.redhat.com/archives/feedhenry-dev/attachments/20160225/46133aeb/attachment.htm>


More information about the feedhenry-dev mailing list