Escaping closure captures 'inout' parameter - swift

I have a function like below, but when I perform it , it's show "Escaping closure captures 'inout' parameter 'cani'"
and i missing anything?
func duration(out:Int,h2:Int,rep:Int,cani:inout Bool){
var io = 0
var b = 0
cani = false
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { // "Escaping closure captures 'inout' parameter 'cani'" //
timer in
io += 1
b += 1
if b <= out{
text = "come in"
}else if b <= out + h2 && b > out{
text = "Hold on"
}
if io == (out + h2 ) * rep{
textcange = "End"
timer.invalidate()
cani = true
}
}
}
it's show "Escaping closure captures 'inout' parameter 'cani'"

When you enter your function, the cani value is duplicated,
when you exit the function, the duplicated value, potentially modified, is written back. That's what inout does.
Your function is asynchronous, so it exits immediately and cani is not modified.
When your timer closure is called, first you don't even know if the caller is still living, but you can be sure the cani copy that was passed does not exist anymore since the function has already exit a while ago.
The compiler detects this non-sense and tells you to do differently.
You may consider adding a cani property in the object that calls the timer, and changes its value in the timer closure or better, since it is asynchronous, call a closure do do what you want to do when the timer issues.
func duration(out:Int,h2:Int,rep:Int,cani:inout Bool){
var io = 0
var b = 0
var test: String
Timer.scheduledTimer(withTimeInterval: 1.0,
repeats: true,
completion: (String)->Void) {
timer in
io += 1
b += 1
if b <= out {
text = "Come in"
} else if b <= out + h2 {
text = "Hold on"
}
if io == (out + h2 ) * rep {
text = "End"
timer.invalidate()
completion()
}
}
}
It's better to put simple and understandable code in StackOverflow questions. And if possible, buildable. ;)

Related

Calling function inside closure, Swift

private func showOrder() {
disableButtons()
var count = 0
_ = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { time in
self.popColor(color: self.colorOrder[count])
count += 1
if count == self.colorOrder.count {
time.invalidate()
self.depopAllButtons()
self.enableButtons()
}
}
}
In this function I am looping through an Array of colors. The popColor() method is used to add shadows and such to a button the screen.
The issue that I am having is that the popColor() function seems to execute after the closure has executed, so it is always one behind where it should be. The error this causes is on the last iteration of the loop the last color doesn't execute its popColor().
Is it possible to call a function inside of a closure as I am doing here with popColor()?
I have write your code with small changes:
var count = 0
_ = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { time in
debugPrint("set color")
count += 1
if count == 3 {
time.invalidate()
debugPrint("disable set color")
}
}
And the console printed:
"set color"
"set color"
"set color"
"disable set color"
Your timer is working correctly. Look for a problem elsewhere.

Having trouble with a while loop in Swift 5

Essentially, I'm making an app about a virtual dog to help people take care of their dogs. One of the screens gives you fifteen seconds to pet the dog five times. Whenever I try to load the screen, the app freezes. The code is inside of viewDidLoad()
while timesPetted < 5 {
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1), execute: {
timer += 1
if timer == 15 {
self.failureLabel.isHidden = false
self.reset.isHidden = false
self.timesPetted = 0
}
})
}
When I delete the while loop, the screen loads normally and runs perfectly, but (obviously) there isn't a time limit. If I move the while loop out of viewDidLoad(), I get an error saying that Xcode "Expected declaration".
Either use a timer that is set to expire in 15 seconds
let timer = Timer.scheduledTimer(withTimeInterval: 15.0, repeats: false) { timer in
self.failureLabel.isHidden = false
self.reset.isHidden = false
self.timesPetted = 0
}
Or if you want to use DispatchQueue then use it only once
DispatchQueue.main.asyncAfter(deadline: .now() + 15) {
self.failureLabel.isHidden = false
self.reset.isHidden = false
self.timesPetted = 0
}
it appears the while loop is running infinitely due to the value of timesPetted. As the value of timesPetted is not changing at all once it enters the while loop.
To solve your issue you can make changes to your code as below :-
You must be updating your timesPetted value some where in the code.
lets say timesPetted is changed in function called "Petting", so when this function is called have check, which limits the user to pet till 5 times only and another check for 15 seconds. As shown below.
func petting() {
if reset.isHidden && timesPetted <= 5{ // here the reset.isHidden will become false in DispatchQueue (present in viewDidLoad) once the 15 seconds have been passed.
timesPetted += 1
}
}
And also make sure to add this line of code in your viewDidLoad.
DispatchQueue.main.asyncAfter(deadline: .now() + 15) {
self.failureLabel.isHidden = false
self.reset.isHidden = false
self.timesPetted = 0
}

calling function from an array with time delay

Let's say I have this array of functions:
lazy var funcArray = [firstFunc, secondFunc, thirdFunc, ....n+Func, ...Inifinit number of Func........]
How would you call them one after the other with a 2.5 second delay?
I have tried without success many things including this while loop:
while n < funcArray.count {
funcArray[n]()
DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) {
n = n +1
}
}
i write small code for better understanding.
initialize count and funcTimer variable and created static function array .
var count:Int = 0
var funcTimer = Timer()
let funcArray:Array = [somefunc(),somefunc(),somefunc()]
After that add these line in appropriate place
funcTimer = Timer.scheduledTimer(timeInterval: 2.5, target: self, selector: (#selector(ViewController.scheduleArrayFunctions)), userInfo: nil, repeats: true)
#objc func scheduleArrayFunctions(funcarray:[ String])
{
if count < self.funcArray.count {
//this if is interesting checking both static function when it check it call the somefunc method
if self.funcArray[count] == ViewController.somefunc(){
print("equel")
}
self.count += 1
}
else
{
funcTimer.invalidate()
}
}
func somefunc()
{
print("hello world")
}
Hope so this will work for you.
I think this will work. Follow these steps.
Declare a variable count as zero.
Schedule a timer with time interval 2.5 and repeat to true.
Now call the function from the array with index as count inside the timer callback block.
Check if the count is less than array.count.
Increment the count.
Otherwise stop the timer.
I did a function to function loop
probably bad coding but...it works
n = 0
self.funcArray[n]()
n = n + 1
timerAction()
func timerAction() {
let when = DispatchTime.now() + 2.5
DispatchQueue.main.asyncAfter(deadline: when) {
self.funcArray[self.n]()
self.n = self.n + 1
if self.n < self.funcArray.count {
self.timerAction2()
}
}
}
func timerAction2() {
let when = DispatchTime.now() + 2.5
DispatchQueue.main.asyncAfter(deadline: when) {
self.funcArray[self.n]()
}
if self.n < self.funcArray.count {
self.timerAction()
}
}

Using dispatchQueue in loops

I am attempting to loop something with a delay (just as a proof of concept) this is for something else. But to simplify it, here is an example.
so in this example, I have a string with "text" and I want to loop the addition of another string lets say 10 times. The only thing is that I want there to be a delay in each iteration. here is my code thus far.
// global variable
var myString = "text"
// an action inside a button
let delayInSeconds = 1.0
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delayInSeconds) {
for _ in 1...10 {
self.myString += "another text"
}
}
labelOne.text = myString
}
I should add that the result is that all 10 "another text" are added immediately without any delay.
thank you
In your example, you append your string ten times in the same work unit. Try dispatching work once per loop:
for delay in 1...10 {
let delayInSeconds = Double(delay)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + delayInSeconds) {
self.myString += "another text"
labelOne.text = myString
}
}
This loop won't work well for arbitrarily large values, though. It also doesn't provide the kind of precise timing we might want for user interface changes. For that, we can use Timer. Here’s the same problem reworked with Timer:
// Add to the class body…
var i = 1
// Lazy initialization so the timer isn’t initialized until we call it
lazy var timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) {timer in
guard self.i <= 20 else {
timer.invalidate()
return
}
self.label.text?.append(" another text")
self.i += 1
}
// Add to the button action…
timer.fire()
If I understand correctly, you want to show the text one by one with delay for each “text”.
You can use recursion here.
1.Move the dispatch code into a method.
2. Remove for loop.
3. Do your necessary actions inside dispatch it.
4.call that same method again inside dispatch.
Do remember to use a counter variable that increments each time method is called. Use it to break the recursion.

For loop based on exponential increases [duplicate]

...or how can I use the index inside the for loop condition
Hey people
Since we're left with no c style for loops in swift 3 I can't seem to find a way to express a bit more complex for loops so maybe you can help me out.
If I were to write this
for(int i=5; num/i > 0; i*=5)
in swift 3 how would I do that?
The closes I came by was:
for i in stride(from: 5, through: num, by: 5) where num/i > 0
but this will of course iterate 5 chunks at a time instead if i being: 5, 25, 125 etc.
Any ideas?
Thanks
Using a helper function (originally defined at Converting a C-style for loop that uses division for the step to Swift 3)
public func sequence<T>(first: T, while condition: #escaping (T)-> Bool, next: #escaping (T) -> T) -> UnfoldSequence<T, T> {
let nextState = { (state: inout T) -> T? in
// Return `nil` if condition is no longer satisfied:
guard condition(state) else { return nil }
// Update current value _after_ returning from this call:
defer { state = next(state) }
// Return current value:
return state
}
return sequence(state: first, next: nextState)
}
you can write the loop as
let num = 1000
for i in sequence(first: 5, while: { num/$0 > 0 }, next: { $0 * 5 }) {
print(i)
}
A simpler solution would be a while-loop:
var i = 5
while num/i > 0 {
print(i)
i *= 5
}
but the advantage of the first solution is that the scope of the loop variable is limited to the loop body, and that the loop variable is a constant.
Swift 3.1 will provide a prefix(while:) method for sequences,
and then the helper function is no longer necessary:
let num = 1000
for i in sequence(first: 5, next: { $0 * 5 }).prefix(while: { num/$0 > 0 }) {
print(i)
}
All of above solutions are "equivalent" to the given C loop.
However, they all can crash if num is close to Int.max
and $0 * 5 overflows. If that is an issue then you have to check
if $0 * 5 fits in the integer range before doing the multiplication.
Actually that makes the loop simpler – at least if we assume that
num >= 5 so that the loop is executed at least once:
for i in sequence(first: 5, next: { $0 <= num/5 ? $0 * 5 : nil }) {
print(i)
}
For completeness: an alternative to the while loop approach is using an AnyIterator:
let num = 1000
var i = 5
for i in AnyIterator<Int>({
return i <= num ? { defer { i *= 5 }; return i }() : nil
}) {
// note that we choose to shadow the external i variable name,
// such that any access to i within this loop will only refer
// to the loop local immutable variable i.
print(i)
// e.g. i += 1 not legal, i refers to a constant here!
} /* 5
25
125
625 */
This method suffers from the same drawback as the while loop in that the loop "external" i variable persists outside and after the scope of the loop block. This external i variable is not, however, the i variable that is accessible within the loop body, as we let the loop body variable i shadow the external one, limiting access to i within the body to the immutable, temporary (loop scope local) one.