Swift Closures in For Loop

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")
})

Swift - Using a closure inside a repeat loop

Since the process is asynchronous the loop will run until hit the first test = false , but i think you need this recursive way until find an available id

func checkID() {

id = generateId()
ref.child("\(databaseReferenceName)").observeSingleEvent(.value) { (snapshot) in
let test = snapshot.hasChild("\(id)")

if test {
print("id exists")
checkID() // check another one
}
else {
print("this is the one \(id)")
}
}

}

another thing is that it should be observeSingleEvent instead of observe

For loop with closure

You say:

Suppose you have an array, and you want to iterate over each element in the array and call a function ... which accepts that element as a parameter.

The basic GCD pattern to know when a series of asynchronous tasks are done is the dispatch group:

let group = DispatchGroup()

for item in array {
group.enter()

someAsynchronousMethod { result in
// do something with `result`

group.leave()
}
}

group.notify(queue: .main) {
// what to do when everything is done
}

// note, don't use the results here, because the above all runs asynchronously;
// return your results in the above `notify` block (e.g. perhaps an escaping closure).

If you wanted to constrain this to, say, a max concurrency of 4, you could use the non-zero semaphore pattern (but make sure you don't do this from the main thread), e.g.

let group = DispatchGroup()
let semaphore = DispatchSemaphore(value: 4)

DispatchQueue.global().async {
for item in array {
group.enter()
semaphore.wait()

someAsynchronousMethod { result in
// do something with `result`

semaphore.signal()
group.leave()
}
}

group.notify(queue: .main) {
// what to do when everything is done
}
}

An equivalent way to achieve the above is with a custom asynchronous Operation subclass (using the base AsynchronousOperation class defined here or here), e.g.

class BarOperation: AsynchronousOperation {
private var item: Bar
private var completion: ((Baz) -> Void)?

init(item: Bar, completion: @escaping (Baz) -> Void) {
self.item = item
self.completion = completion
}

override func main() {
asynchronousProcess(bar) { baz in
self.completion?(baz)
self.completion = nil
self.finish()
}
}

func asynchronousProcess(_ bar: Bar, completion: @escaping (Baz) -> Void) { ... }
}

Then you can do things like:

let queue = OperationQueue()
queue.maxConcurrentOperationCount = 4

let completionOperation = BlockOperation {
// do something with all the results you gathered
}

for item in array {
let operation = BarOperation(item: item) { baz in
// do something with result
}
operation.addDependency(completionOperation)
queue.addOperation(operation)
}

OperationQueue.main.addOperation(completion)

And with both the non-zero semaphore approach and this operation queue approach, you can set the degree of concurrency to whatever you want (e.g. 1 = serial).

But there are other patterns, too. E.g. Combine offers ways to achieve this, too https://stackoverflow.com/a/66628970/1271826. Or with the new async/await introduced in iOS 15, macOS 12, you can take advantage of the new cooperative thread pools to constrain the degree of concurrency.

There are tons of different patterns.

How to use closures in a for loop in swift?

It is a bug:

var data : [(Int) -> ()] = []

for (var i = 0; i < 5; i++) {
data.append { (j:Int) in
println (j + i)
}
}

9> data[0](10)
15 // should be 10

[Edit: Not actually a bug; this is the way some languages are, Swift too. My described 'work around' is how you handle this (not just a 'work around')]

The variable i IS NOT modified in the body of the closure, it thus MUST BE 'copied' (see 'Capturing Values') when the closure is created. Instead, apparently, the compiler sees that i is modfiable, albeit outside of the closure, and incorrectly concludes that a 'reference' is needed.

To work around, move reference to the incorrectly captured i, outside of the closure:

Welcome to Swift version 1.2. Type :help for assistance.
1> var data : [(Int) -> ()] = []
2.
3. for (var i = 0; i < 5; i++) {
4. let r = i
5. data.append { (j:Int) in
6. println (j + r)
7. }
8. }
data: [Int -> ()] = 5 values {
[0] = ($__lldb_expr2`partial apply forwarder for reabstraction thunk helper from @callee_owned (@unowned Swift.Int) -> (@unowned ()) to @callee_owned (@in Swift.Int) -> (@out ()) at repl1.swift)
...
[4] = ($__lldb_expr2`partial apply forwarder for reabstraction thunk helper from @callee_owned (@unowned Swift.Int) -> (@unowned ()) to @callee_owned (@in Swift.Int) -> (@out ()) at repl1.swift)
}
9> data[0](10)
10
10> data[4](10)
14

How is this working? Do closures run implicit loops?

expenses.items.firstIndex(where: { $0.id == expenses.businessItems[offset].id })

Is shorthand for this version which uses an explicit argument name

expenses.items.firstIndex(where: { expense in
expense.id == expenses.businessItems[offset].id
})

which is a closure (AKA an anonymous function, or a block). To make it really clear, that's shorthand for

func expenseMatchesCurrentBusinessItem(_ expense: Expense) -> Bool {
return expense.id == expenses.businessItems[offset].id
}

expenses.items.firstIndex(where: expenseMatchesCurrentBusinessItem)

Basically, it's a boolean function for checking if a given expense matches the one specified by the offset into the businessItems array. firstIndex(where:) accepts this function as its parameter and internally loops through its array calling that function with each element. If the provided function returns true, firstIndex(where:) returns the index for that element. Note that locally declared functions and closures have access to the variables declared in the same scope, which is how the function can access expenses and offset.

Once you get used to functional programming, that full example looks way too verbose. In general expenseMatchesCurrentBusinessItem is just "the function that tests if this is the thing we want" and its parameter expense is just "the thing in the array" so rather than name either of them, we use the unnamed anonymous closure syntax and the automatic argument name $0

Closure Completion in For Loop Swift

I recommend to use tableView.reloadRowsAtIndexPaths for each geocoding callback.

But if you must use reloadData(), easiest solution is to count your geocoding jobs preventing reentrance.

Example:

var loading =   false

@IBAction func
Do( sender: AnyObject ) {

if !loading {
loading = true
var w = 0
for i in 0 ..< 10 {
dispatch_async( dispatch_get_main_queue() ) {
println( i )
if ++w == 10 {
self.loading = false
println( "It's time to call reloaddata!" )
}
}
}
}
}

EDIT:

How about attaching something like job ids and put those in a list?

var jobIDs: [ Int ] =   []

@IBAction func
Do( sender: AnyObject ) {

if jobIDs.count == 0 {
for theJobID in 0 ..< 10 {
jobIDs.append( theJobID )
dispatch_async( dispatch_get_main_queue() ) {
println( "Doing job:\(theJobID)" )
self.jobIDs.removeAtIndex( find( self.jobIDs, theJobID )! )
if self.jobIDs.count == 0 {
println( "It's time to call reloaddata!" )
}
}
}
}
}

You may use 'aClient' as job ID.

JavaScript closure inside loops – simple practical example

Well, the problem is that the variable i, within each of your anonymous functions, is bound to the same variable outside of the function.

ES6 solution: let

ECMAScript 6 (ES6) introduces new let and const keywords that are scoped differently than var-based variables. For example, in a loop with a let-based index, each iteration through the loop will have a new variable i with loop scope, so your code would work as you expect. There are many resources, but I'd recommend 2ality's block-scoping post as a great source of information.

for (let i = 0; i < 3; i++) {
funcs[i] = function() {
console.log("My value: " + i);
};
}

Beware, though, that IE9-IE11 and Edge prior to Edge 14 support let but get the above wrong (they don't create a new i each time, so all the functions above would log 3 like they would if we used var). Edge 14 finally gets it right.



ES5.1 solution: forEach

With the relatively widespread availability of the Array.prototype.forEach function (in 2015), it's worth noting that in those situations involving iteration primarily over an array of values, .forEach() provides a clean, natural way to get a distinct closure for every iteration. That is, assuming you've got some sort of array containing values (DOM references, objects, whatever), and the problem arises of setting up callbacks specific to each element, you can do this:

var someArray = [ /* whatever */ ];
// ...
someArray.forEach(function(arrayElement) {
// ... code code code for this one element
someAsynchronousFunction(arrayElement, function() {
arrayElement.doSomething();
});
});

The idea is that each invocation of the callback function used with the .forEach loop will be its own closure. The parameter passed in to that handler is the array element specific to that particular step of the iteration. If it's used in an asynchronous callback, it won't collide with any of the other callbacks established at other steps of the iteration.

If you happen to be working in jQuery, the $.each() function gives you a similar capability.



Classic solution: Closures

What you want to do is bind the variable within each function to a separate, unchanging value outside of the function:

var funcs = [];

function createfunc(i) {
return function() {
console.log("My value: " + i);
};
}

for (var i = 0; i < 3; i++) {
funcs[i] = createfunc(i);
}

for (var j = 0; j < 3; j++) {
// and now let's run each one to see
funcs[j]();
}

How can I use Closures in Closures, without creating an infinite loop?

There is no problem calling a function inside itself - it's called a recursion, and so long as you have a stop condition, this isn't going be infinite.

In your case, somewhere you're making the determination that you need to perform observeCurrentUserToDisk - presumably, if you didn't, then the function would be done. That is your stop condition.

Furthermore, since fetchCurrentUser is async and you're going to call it and need to act on its results, add completion (onSuccess and onError) handlers to it as well.

Here's a general idea:

func fetchCurrentUser(onSuccess: @escaping (RealmUser) -> Void, 
onError: @escaping (String) -> Void) {

observeCurrentUserFromDisk(
onSuccess: onSuccess,
onError: {
observeCurrentUserToDisk(
onSuccess: {
// start recursive call, and in the next iteration this shouldn't
// end in onError of observeCurrentUserFromDisk.
// Though, if it's a possibility, you might want to limit
// the number of tries before failing
fetchCurrentUser(onSuccess: onSuccess)
},
onError: onError)
}
}

You can use fetchCurrentUser's completion handler to do what you needed:

fetchCurrentUser(
onSuccess: { user in
self.emailLabel.text = user.email

if let profileUrl = URL(string: user.profileImageUrl!) {
self.profileImageView.sd_setImage(with: profileUrl)
}
},
onError: {
// handle the error case
})


Related Topics



Leave a reply



Submit