Modifying function-level variables from within a closure?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
11 messages Options
Reply | Threaded
Open this post in threaded view
|

Modifying function-level variables from within a closure?

Alex Hall
Hi all,
I've tried to look this up online, but can't find anything. I have a function that calls a framework function, and passes in two closures as part of the necessary arguments. The framework function wants both closures to return nothing. What I want to do is take the results passed into the first closure and set them equal to a variable in my function, so my function can return that variable. In the second closure (called if the framework fails), I want to set my variable to nil. Here's a representation of what I'm talking about:

func myFunction() -> [String]? {
        var resultsToReturn:[String]?
        let successHandler= {
                (results:[String]?) in
                resultsToReturn=results
        }

let errorHandler = {
                (error:NSError) in
print("\(error.localizedDescription)")
                resultsToReturn=nil
        }

myFrameworkFunction(success:successHandler, failure:failureHandler)
        return resultsToReturn
}

This way, I can store the results of success and return them, or return nil to indicate failure (yes, throwing would be better, but for now I'm mostly interested in saving those results). Right now, it's looking like resultsToReturn isn't being modified at all outside the closures, so I'm not sure how to save what the success closure is offering. I'm using Xcode 7 GM on 10.11 GM.

--
Have a great day,
Alex Hall
[hidden email]

--
You received this message because you are subscribed to the Google Groups "Swift Language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To post to this group, send email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/swift-language/D192B1EA-53B0-464F-B267-7B4EE2B569C9%40icloud.com.
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: Modifying function-level variables from within a closure?

Brent Royal-Gordon
Closures natively support modifying variables captured from the scope they're in; there's nothing special you need to do to make that work. However, many APIs that take a completion closure do it because they perform an operation asynchronously—that is, the operation isn't finished by the time myFrameworkFunction returns and the next line of code executes. Is that true of your function? If so, there's just no (easy) way to fill in resultsToReturn by the time that return statement executes, and you'll have to restructure your code so that the completion blocks continue the operation.

--
Brent Royal-Gordon
Sent from my iPhone

> On Sep 13, 2015, at 8:39 PM, Alex Hall <[hidden email]> wrote:
>
> Hi all,
> I've tried to look this up online, but can't find anything. I have a function that calls a framework function, and passes in two closures as part of the necessary arguments. The framework function wants both closures to return nothing. What I want to do is take the results passed into the first closure and set them equal to a variable in my function, so my function can return that variable. In the second closure (called if the framework fails), I want to set my variable to nil. Here's a representation of what I'm talking about:
>
> func myFunction() -> [String]? {
>    var resultsToReturn:[String]?
>    let successHandler= {
>        (results:[String]?) in
>        resultsToReturn=results
>    }
>
> let errorHandler = {
>        (error:NSError) in
> print("\(error.localizedDescription)")
>        resultsToReturn=nil
>    }
>
> myFrameworkFunction(success:successHandler, failure:failureHandler)
>    return resultsToReturn
> }
>
> This way, I can store the results of success and return them, or return nil to indicate failure (yes, throwing would be better, but for now I'm mostly interested in saving those results). Right now, it's looking like resultsToReturn isn't being modified at all outside the closures, so I'm not sure how to save what the success closure is offering. I'm using Xcode 7 GM on 10.11 GM.
>
> --
> Have a great day,
> Alex Hall
> [hidden email]
>
> --
> You received this message because you are subscribed to the Google Groups "Swift Language" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
> To post to this group, send email to [hidden email].
> To view this discussion on the web visit https://groups.google.com/d/msgid/swift-language/D192B1EA-53B0-464F-B267-7B4EE2B569C9%40icloud.com.
> For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "Swift Language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To post to this group, send email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/swift-language/A6116540-56AA-42AC-97AB-4070B1E301B1%40architechies.com.
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: Modifying function-level variables from within a closure?

Alex Hall

> On Sep 13, 2015, at 23:56, Brent Royal-Gordon <[hidden email]> wrote:
>
> Closures natively support modifying variables captured from the scope they're in; there's nothing special you need to do to make that work.
I hoped it was that easy. This seemed an odd thing for Swift to not handle.

> However, many APIs that take a completion closure do it because they perform an operation asynchronously—that is, the operation isn't finished by the time myFrameworkFunction returns and the next line of code executes. Is that true of your function?
Yes, that's exactly it. The framework is pulling data from the internet, and does so asynchronously.

> If so, there's just no (easy) way to fill in resultsToReturn by the time that return statement executes, and you'll have to restructure your code so that the completion blocks continue the operation.
Sounds like fun. Thanks for the answer, at least my original question had a simple solution. Were the function not asynchronous, then, my example would work as expected.

>
> --
> Brent Royal-Gordon
> Sent from my iPhone
>
>> On Sep 13, 2015, at 8:39 PM, Alex Hall <[hidden email]> wrote:
>>
>> Hi all,
>> I've tried to look this up online, but can't find anything. I have a function that calls a framework function, and passes in two closures as part of the necessary arguments. The framework function wants both closures to return nothing. What I want to do is take the results passed into the first closure and set them equal to a variable in my function, so my function can return that variable. In the second closure (called if the framework fails), I want to set my variable to nil. Here's a representation of what I'm talking about:
>>
>> func myFunction() -> [String]? {
>>   var resultsToReturn:[String]?
>>   let successHandler= {
>>       (results:[String]?) in
>>       resultsToReturn=results
>>   }
>>
>> let errorHandler = {
>>       (error:NSError) in
>> print("\(error.localizedDescription)")
>>       resultsToReturn=nil
>>   }
>>
>> myFrameworkFunction(success:successHandler, failure:failureHandler)
>>   return resultsToReturn
>> }
>>
>> This way, I can store the results of success and return them, or return nil to indicate failure (yes, throwing would be better, but for now I'm mostly interested in saving those results). Right now, it's looking like resultsToReturn isn't being modified at all outside the closures, so I'm not sure how to save what the success closure is offering. I'm using Xcode 7 GM on 10.11 GM.
>>
>> --
>> Have a great day,
>> Alex Hall
>> [hidden email]
>>
>> --
>> You received this message because you are subscribed to the Google Groups "Swift Language" group.
>> To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
>> To post to this group, send email to [hidden email].
>> To view this discussion on the web visit https://groups.google.com/d/msgid/swift-language/D192B1EA-53B0-464F-B267-7B4EE2B569C9%40icloud.com.
>> For more options, visit https://groups.google.com/d/optout.


--
Have a great day,
Alex Hall
[hidden email]

--
You received this message because you are subscribed to the Google Groups "Swift Language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To post to this group, send email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/swift-language/02D894DF-4940-49B3-BFA1-A42F74E66A6D%40icloud.com.
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: Modifying function-level variables from within a closure?

Brent Royal-Gordon
> Were the function not asynchronous, then, my example would work as expected.

Yup.

--
Brent Royal-Gordon
Architechies

--
You received this message because you are subscribed to the Google Groups "Swift Language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To post to this group, send email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/swift-language/7C641EFD-2F3A-46A4-8687-3383F4225D08%40architechies.com.
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: Modifying function-level variables from within a closure?

Jens Alfke
In reply to this post by Alex Hall

On Sep 13, 2015, at 9:15 PM, Alex Hall <[hidden email]> wrote:

Sounds like fun. Thanks for the answer, at least my original question had a simple solution. Were the function not asynchronous, then, my example would work as expected.

A little more detail: If the closure (or Obj-C block, same thing) outlives the function it was called from, it makes copies of the local variables from the enclosing scopes. So it can continue to use those variables, and even change them, but of course the changes no longer affect the scope it was called from (which doesn’t exist anymore.)

Asynchronous callbacks definitely make it harder to think about the flow of control. You have to imagine it skipping past the block and continuing out of the function back to its caller … and then at some later point returning to the block. I sometimes put a comment at the start of an asynchronous block to remind the reader about that, like “// This gets called later on after the grommet is finished detuning.”

—Jens

--
You received this message because you are subscribed to the Google Groups "Swift Language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To post to this group, send email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/swift-language/6901599B-8166-4B75-8E7B-C2CECB8E757E%40mooseyard.com.
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

async closures (was: Modifying function-level variables from within a closure?)

Alex Hall

On Sep 14, 2015, at 11:53, Jens Alfke <[hidden email]> wrote:


On Sep 13, 2015, at 9:15 PM, Alex Hall <[hidden email]> wrote:

Sounds like fun. Thanks for the answer, at least my original question had a simple solution. Were the function not asynchronous, then, my example would work as expected.

A little more detail: If the closure (or Obj-C block, same thing) outlives the function it was called from, it makes copies of the local variables from the enclosing scopes. So it can continue to use those variables, and even change them, but of course the changes no longer affect the scope it was called from (which doesn’t exist anymore.)

Asynchronous callbacks definitely make it harder to think about the flow of control. You have to imagine it skipping past the block and continuing out of the function back to its caller … and then at some later point returning to the block. I sometimes put a comment at the start of an asynchronous block to remind the reader about that, like “// This gets called later on after the grommet is finished detuning.”

Good idea, because the way I use the framework, it's not made obvious that the work happens asynchronously.

What's the best way to do this, then, in your opinion? I essentially need to get the results (as an array) back from the block, so I can give that array back to my app to have it processed. I've thought of NSNotifications, but I'm not sure how I'd set things up, and it seems like a bit of a cheat besides. I don't want my whole app to block while waiting for this to happen, but the specific part that gets and processes the array from the framework can block if it can run in the background. That is, I've considered running the whole update process--in m app--asynchronously and letting my Cocoa bindings take care of updating my table as my array gets added to. The core problem still remains, though: even in an async chunk of my own code, the framework is still running asynchronously as well. I'm reading about GCD and related topics, but any thoughts anyone has would be great.

—Jens


--
Have a great day,
Alex Hall
[hidden email]

--
You received this message because you are subscribed to the Google Groups "Swift Language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To post to this group, send email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/swift-language/20F50FF5-709E-4E11-8D15-DE50098477BC%40icloud.com.
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: async closures (was: Modifying function-level variables from within a closure?)

Jens Alfke

On Sep 14, 2015, at 10:33 AM, Alex Hall <[hidden email]> wrote:

What's the best way to do this, then, in your opinion? I essentially need to get the results (as an array) back from the block, so I can give that array back to my app to have it processed. I've thought of NSNotifications, but I'm not sure how I'd set things up, and it seems like a bit of a cheat besides.

Structure your API for asynchrony in the same way as the frameworks you’re using: Give your function a block parameter that it'll call later when it has the results. Then when the main thread of the app calls your function, it won’t block, and it can provide a callback that will do whatever it needs to update the UI.

—Jens

--
You received this message because you are subscribed to the Google Groups "Swift Language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To post to this group, send email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/swift-language/72FF00BC-71FB-4F4E-A478-EE71CCD7DEF5%40mooseyard.com.
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: async closures (was: Modifying function-level variables from within a closure?)

Alex Hall

On Sep 14, 2015, at 14:37, Jens Alfke <[hidden email]> wrote:


On Sep 14, 2015, at 10:33 AM, Alex Hall <[hidden email]> wrote:

What's the best way to do this, then, in your opinion? I essentially need to get the results (as an array) back from the block, so I can give that array back to my app to have it processed. I've thought of NSNotifications, but I'm not sure how I'd set things up, and it seems like a bit of a cheat besides.

Structure your API for asynchrony in the same way as the frameworks you’re using: Give your function a block parameter that it'll call later when it has the results. Then when the main thread of the app calls your function, it won’t block, and it can provide a callback that will do whatever it needs to update the UI

I'd considered that, but I'm still stuck on knowing when the async call is done. In other words, how would my function know when to execute the completion block I gave it, instead of running it right away?
.

—Jens


--
Have a great day,
Alex Hall
[hidden email]

--
You received this message because you are subscribed to the Google Groups "Swift Language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To post to this group, send email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/swift-language/8B2A907A-E871-47A9-9C35-F44472865E31%40icloud.com.
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: async closures (was: Modifying function-level variables from within a closure?)

Jens Alfke

On Sep 14, 2015, at 2:03 PM, Alex Hall <[hidden email]> wrote:

I'd considered that, but I'm still stuck on knowing when the async call is done. In other words, how would my function know when to execute the completion block I gave it, instead of running it right away?

Your function just calls it from its own closure that it passed to the async system routine.

—Jens

--
You received this message because you are subscribed to the Google Groups "Swift Language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To post to this group, send email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/swift-language/D48CBAF8-EF31-434A-8FC6-F0720F0F1090%40mooseyard.com.
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: async closures (was: Modifying function-level variables from within a closure?)

Brent Royal-Gordon
In reply to this post by Alex Hall
> how would my function know when to execute the completion block I gave it, instead of running it right away?

You know it’s done when the async call you’re making calls *your* completion closures.

   func frobSpludgerWithCompletion(completion: [String]? -> Void) {
       myFrameworkFunction(success: { strings in completion(strings) }, failure: { completion(nil) })
   }

If the signatures happen to be compatible, you could even pass the completion block the user provided to you in directly:

   func frobSpludgerWithCompletion(completion: [String]? -> Void) {
       myFrameworkFunction(success: completion, failure: { completion(nil) })
   }

--
Brent Royal-Gordon
Architechies

--
You received this message because you are subscribed to the Google Groups "Swift Language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To post to this group, send email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/swift-language/B2781970-2F2F-4496-842E-07B05B66932E%40architechies.com.
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: async closures (was: Modifying function-level variables from within a closure?)

Alex Hall
Thanks guys, it works perfectly!

> On Sep 14, 2015, at 20:15, Brent Royal-Gordon <[hidden email]> wrote:
>
>> how would my function know when to execute the completion block I gave it, instead of running it right away?
>
> You know it’s done when the async call you’re making calls *your* completion closures.
>
>   func frobSpludgerWithCompletion(completion: [String]? -> Void) {
>       myFrameworkFunction(success: { strings in completion(strings) }, failure: { completion(nil) })
>   }
>
> If the signatures happen to be compatible, you could even pass the completion block the user provided to you in directly:
>
>   func frobSpludgerWithCompletion(completion: [String]? -> Void) {
>       myFrameworkFunction(success: completion, failure: { completion(nil) })
>   }
>
> --
> Brent Royal-Gordon
> Architechies
>


--
Have a great day,
Alex Hall
[hidden email]

--
You received this message because you are subscribed to the Google Groups "Swift Language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To post to this group, send email to [hidden email].
To view this discussion on the web visit https://groups.google.com/d/msgid/swift-language/F1D0CB3B-8FF8-4D47-B04A-0589A4CEA297%40icloud.com.
For more options, visit https://groups.google.com/d/optout.