Swift - Exit Outer Function from Closure

Swift - Exit outer function from closure

Swift does not have non-local returns from closures. In other words, there is no direct way to return out of multiple levels. This only works with inlined functions in Kotlin, but Swift does not have this distinction.

There are other collection methods which stop once an element is found, for example index(where:).

func lookForLetter(letter: String, letters: [String]) {
guard let _ = letters.index(where: { (l) in
if l == letter {
print("Found");
return true
}
print(l)
return false
}) else {
print("Completed")
return
}
}

Can swift exit out of the root closure?

No there is not. Closures run in self-contained environments. For all you know, by the time that closure is executed, the thread on which test() was called is no longer executing the test() method.

Swift closure return from function

You'll need to use some other mechanism. Perhaps return a bool to say you should return straight away.

func synchronized(_ object: AnyObject, block: () -> Bool) -> Bool 
{
objc_sync_enter(object)
defer { objc_sync_exit(object) }
return block()
}

public func stopRunning() {
guard synchronized( self, block: {
if status != .finished {
return false//<--Need to return from the function here, not just closure
}
return true
})
else { return }

...
...
}

Use break into closures [Swift]

Desipte it's very important to know count of images , you can use this

func getImages(_ i:Int) {

let downloadURL = URL(string: "serverURL")
Database.downloadImage(withURL: downloadURL!) { (image) in

if let img = image {

self.imagesArray.append(img)
self.collectionView?.reloadData()
getImages(i+1)

}
else {
print("image is nil final call")
}
}
}
}

Also may be the download is dependent on i as not to make an infinite loop

Is there a way to break from an array's reduce function in Swift?

As others have suggested, you can use contains for this purpose:

var flags = [false, false, true, false, false, true, false]
contains(flags, true) //--> true

Another option is to use find to search for the first instance of what you're looking for, in this case true:

var flags = [false, false, true, false, false, true, false]    
find(flags, true) // --> 2, returns nil if not found
let containsTrue = (find(flags, true) != nil)

Edit: Newer versions of Swift expose these functions on the related collection protocols instead of global functions.

var flags = [false, false, true, false, false, true, false]
flags.contains(where: { $0 == true })
flags.contains(true) // if checking for a specific element
let index = flags.firstIndex(where: ${ $0 == true }) // --> 2

Swift Closures in for loop

You can use a DispatchGroup for this. It works like a counted semaphore. You can increase the count by calling enter and decrease the count by calling leave. You can schedule a closure to be executed when the count reaches 0 using notify:

let dispatchGroup = DispatchGroup()
var noOfTimes = 10
for i in 0..<noOfTimes {
dispatchGroup.enter() // Enter the dispatch group
someVariable.someClosure {
result in
// Process result
...
dispatchGroup.leave() // Exit dispatch group
}
}
dispatchGroup.notify(queue: DispatchQueue.main, execute: {
print("All Done")
})

If the Swift 'guard' statement must exit scope, what is the definition of scope?

It is totally possible to do what you envision, it just happens to not be what that particular code does. return always exits a method, not the local scope. To do what you wish, you can use a label, and break:

func testGuardControlFlow () {

let x = 2
let y = 2

func embededFunc () {

breakLabel:
if y == 2 {

guard x == 1 else {
print("oops, number is not 1")
break breakLabel
}

print ("from in embededFunc")

}

print ("I still want this to print even if x != 1")
}

embededFunc()
print("Great, return still allows this to be printed.")

}

testGuardControlFlow()

To add on to vadian's answer:

guard forces you to exit the scope using a control transfer statement. There are 4 available to you:

  • return and throw both exit the function/method
  • continue can be used within loops (while/for/repeat-while)
  • break can be used in loops (while/for/repeat-while) to exit the immediate scope. Specifying a label to break to will allow you to exit multiple scopes at once (e.g. breaking out of nested loop structure). When using a label, break can also be used in if scopes.

Additionally, you may exit the scope by calling a function that returns Never, such as fatalError.

How do I access variables that are inside closures in Swift?

var dict: NSDictionary! // Declared in the main thread

The closure is then completed asynchronously so the main thread doesn't wait for it, so

println(dict)

is called before the closure has actually finished. If you want to complete another function using dict then you will need to call that function from within the closure, you can move it into the main thread if you like though, you would do this if you are going to be affecting UI.

var dict: NSDictionary!
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {(response, data, error) in
var jsonError: NSError?
let json = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: &jsonError) as? NSDictionary
dict = json
//dispatch_async(dispatch_get_main_queue()) { //uncomment for main thread
self.myFunction(dict!)
//} //uncomment for main thread
})

func myFunction(dictionary: NSDictionary) {
println(dictionary)
}

Do capture lists of inner closures need to redeclare `self` as `weak` or `unowned`?

The [weak self] in anotherFunctionWithTrailingClosure is not needed.

You can empirically test this:

class Experiment {
func someFunctionWithTrailingClosure(closure: @escaping () -> Void) {
print("starting", #function)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
closure()
print("finishing", #function)
}
}

func anotherFunctionWithTrailingClosure(closure: @escaping () -> Void) {
print("starting", #function)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
closure()
print("finishing", #function)
}
}

func doSomething() {
print(#function)
}

func testCompletionHandlers() {
someFunctionWithTrailingClosure { [weak self] in
self?.anotherFunctionWithTrailingClosure { // [weak self] in
self?.doSomething()
}
}
}

// go ahead and add `deinit`, so I can see when this is deallocated

deinit {
print("deinit")
}
}

And then:

func performExperiment() {
DispatchQueue.global().async {
let obj = Experiment()

obj.testCompletionHandlers()

// sleep long enough for `anotherFunctionWithTrailingClosure` to start, but not yet call its completion handler

Thread.sleep(forTimeInterval: 1.5)
}
}

If you do this, you will see that doSomething is never called and that deinit is called before anotherFunctionWithTrailingClosure calls its closure.

That having been said, I might still be inclined to use the [weak self] syntax on anotherFunctionWithTrailingClosure to make my intent explicit.



Related Topics



Leave a reply



Submit