calling function from an array with time delay - swift

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

Related

Swift Timer that will not run again until a certain period of time

This is proving to be a challenge, I want a timer that triggers a pulse for a 10th of a second, then effectively blocks it from running again until at least 1 second has past.
This is the code for the pulse , but how can I block it from triggering again until 1 second has passed - could I put a timer within a timer?
Any help would be greatly appreciated.
func pulseOn(on: Bool) {
var pulseCount = 4
Timer.scheduledTimer(withTimeInterval: 0.025, repeats: true)
{ timer in
print("PulseCount: \(pulseCount)")
pulseCount = pulseCount - 1
if pulseCount < 1 {
timer.invalidate()
}
}
}
When measuring time between two events, use Date and its timeIntervalSince* functions instead of a timer.
Use a property lastPulse to record the time of the last pulse and have a Bool property pulseActive that keeps track if a pulse in in progress. Only run your function if 1 second has passed and a pulse is not active.
// time of last pulse
// seed lastPulse with distantPast so first pulse will always succeed
var lastPulse = Date.distantPast
// are we in the middle of running a pulse?
var pulseActive = false
func pulseOn(on: Bool) {
guard !pulseActive && abs(lastPulse.timeIntervalSinceNow) >= 1 else { return }
pulseActive = true
// record the time of this pulse
lastPulse = Date()
var pulseCount = 4
Timer.scheduledTimer(withTimeInterval: 0.025, repeats: true)
{ timer in
print("PulseCount: \(pulseCount)")
pulseCount = pulseCount - 1
if pulseCount < 1 {
timer.invalidate()
pulseActive = false
// If you want the time from last pulse end to be 1 second
// then set lastPulse here instead
//lastPulse = Date()
}
}
}
Notes:
Your on: Bool is unused. I have left it there because I wanted to highlight just the new code.
It wasn't clear if you wanted the 1 second interval to be from pulse start to pulse start or pulse end to pulse start. If you want the pulse starts to be 1 second apart at least, then use the code as written. If you want the next pulse to start at least one second after the previous pulse ended, then set lastPulse = Date() when the timer is invalidated.
May something like this. add a bool;
var runPulse: Bool = false
orginal timer function:
func pulseOn(on: Bool) {
var pulseCount = 4
Timer.scheduledTimer(withTimeInterval: 0.025, repeats: true)
{ timer in
print("PulseCount: \(pulseCount)")
secondCheck() //new funtion
pulseCount = pulseCount - 1
if pulseCount < 1 {
timer.invalidate()
}
}
}
New function:
func pulseOn(on: Bool) {
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false)
{ timer in
self.runPulse = true
}
}
Then wherever you trigger the pulseOn func you can add an if statement.
if runPulse {
pulseOn()
}

How to create a function which measures time performance of algorithms?

If I want to evaluate time performance of a few algos using Date() or Dispatch() how can I create a function that does this?
For example this binary search algo. How can I pass it as a closure parameter and have the closure do all of the time performance measuring using any of the Swift time keeping methods below? Please answer with an example of a closure. Thanks.
let startingPoint = Date()
let startingPoint = Dispatch().now
func binarySearchForValue(searchValue: Int, array: [Int]) -> Bool {
var leftIndex = 0
var rightIndex = array.count - 1
while leftIndex <= rightIndex {
let middleIndex = (leftIndex + rightIndex) / 2
let middleValue = array[middleIndex]
if middleValue == searchValue {
return true
}
if searchValue < middleValue {
rightIndex = middleIndex - 1
}
if searchValue > middleValue {
leftIndex = middleIndex + 1
}
}
return false
}
Since you may want to measure different functions, it probably makes sense to capture the arguments for the function in the closure instead of including their types in the signature. But I did use a generic type for the return value. I hope that this is what you're after:
func measure<R>(_ label: String, operation: () -> R) -> R {
let start = DispatchTime.now()
let result = operation()
let end = DispatchTime.now()
let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds
let timeInterval = Double(nanoTime) / 1_000_000_000
print(String(format: "%#: %.9f s", label, timeInterval))
return result
}
Here's how you use it:
let result = measure("search") { binarySearchForValue(searchValue: 3, array: [1, 3, 8]) }
print(result) // that's the result of the function that was measured, not the measurement
measure("some function") { functionWithoutReturnValue() }
If the function has no return value, R will be (), so that should work too. Just don't assign the result to anything (see the example above). If you want to do something with the measurement other than printing it to the console, you can do that, too. But you didn't specify that in your question, so I went with print.
Not sure what exactly you are after here and this solution below will only fit one specific function signature to test
func testBench(search: Int, array: [Int], testCase test: (Int, [Int]) -> Bool) {
let start = DispatchTime.now()
test(search, array)
let end = DispatchTime.now()
print("\(start) - \(end)")
}
called like this
testBench(search: 3, array: [6,7,5,3]) {binarySearchForValue(searchValue: $0, array: $1)}
You should use XCTest to measure the performance ... It gives you proper stats for your method in terms of performance
i.e.
func testMyCodesPerformance() {
measureBlock {
someClass.doSomethingFancy()
}
}
You can do lot more using XCTest measureBlock for performance testing

Can't find a way to implement a wait function in my Swift code

I can't find a way to implement a wait function, I'm using swiftforwindows and no examples online have been able to solve it so far. It's Swift 4.2
The class is basically an array that when a function is called each index on the array gets a constant value deducted. the tick function is what is being called. I'm new to Swift.
class resProj {
var list = [1,1,1,1]
var projReq = [100,200,300,50]
var completed = false
func tick(){
for count in 0..<projReq.count{
if projReq[count] <= list[count]{
projReq[count] = 0
}
else if projReq[count] > list[count]{
projReq[count] -= list[count]
}
}
print(projReq)
}
init(
mathsP mathsIn: Int,
scienceP sciecnceIn: Int,
enginerP enginerIn: Int,
businessP businessIn: Int) {
self.list [0] = mathsIn
self.list [1] = sciecnceIn
self.list [2] = enginerIn
self.list [3] = businessIn
}
}
var spaceElev = resProj(
mathsP: 10,
scienceP: 20,
enginerP: 30,
businessP: 5)
var x = false
while x == false{
//wait function here pls//
print("tick", terminator:"?")
let y = readLine()
if y == "y"{
spaceElev.tick()
}
else{
print("gotta put y")
}
var templist = spaceElev.projReq
var templistcount = 0
templistcount = templist.count
for loop in 0..<templistcount{
if templist[loop] == 0{
templistcount -= 1
}
}
if templistcount == 0 {
x = true
print("project completed")
}
}
}
Where it says //wait function here pls// I would like to make the program wait for 1 second.
There are a lot of way to do this but most common way is create a completion function. For example:
func doSth(_ someParameter: String, _ completion: ()->()) {
print(someParameter)
// After your code is finish call completion
completion()
}
And when you call (there is two way to call):
doSth("Done") {
print("You can be sure that this block will work after your func finish")
}
or you can simply create another func and send it as a parameter.
You can also use DispatchQueue:
DispatchQueue.main.asyncAfter(deadline: .now()+1) {
// put your func here...
}
You can simple use the UNIX-Functin func sleep(_: UInt32) -> UInt32.
In your case use sleep(1) to wait one second.
You could use Grand Central Dispatch or perform.
GCD solution:
let delayInSeconds = 1
DispatchQueue.main.asyncAfter(deadline: .now() + delayInSeconds) {
print("tick", terminator:"?")
}
If you want to learn more about Grand Central Dispatch (GCD) I suggest you read through this:
Grand Central Dispatch - Wikipedia
Grand Central Dispatch Tutorial - Ray Wenderlich
Perform solution:
Create a function like this:
#objc func delayedFunc() {
//write the code here that you want to execute with a one second delay
}
Then call this where you want the delayed function to execute:
let delayInSeconds = 1
perform(#selector(delayedFunc), with: nil, afterDelay: delayInSeconds)
You can use the RunLoop class:
func wait(for interval: TimeInterval) {
RunLoop.current.run(until: Date() + interval)
}

Swift for iOS - 2 for loops run at the same time?

I have two objects where I need to update their UI at the same time. I have a for loop for one, and after that another for loop. Each iteration in the for loop I have a short delay so that for elements in the object I am making a UI change... one after the other - not seemingly all at once.
func update(value: Int){
var delay: Double = 0.05
// first loop
for i in 0...value {
delayWithSeconds(delay) {
//do something with object 1
}
delay = delay + 0.05
}
var delay2: Double = 0.05
// second loop
for i in 0...value {
delayWithSeconds(delay2) {
//do something with object 2
}
delay2 = delay2 + 0.05
}
}
// Utility
func delayWithSeconds(_ seconds: Double, completion: #escaping () -> ()) {
DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
completion()
}
}
I have tried wrapping each for loop with DispatchQueue.main.async and it didn't make a difference. In short - I would like to run both for loops at the same time (or perceived as such). These are on the UI thread.
I tried this and it seemed to work out quite well. It does exactly what I want it to do (at least visually they seem to run at the same time).
let concurrentQueue = DispatchQueue(label: "net.ericd.hello", attributes: .concurrent)
concurrentQueue.async {
//my loop with delay here for object 1.
}
concurrentQueue.async {
//my separate loop with delay here for object 2.
}
We can use it when we want execute different arrays at the same time:
using this Generic Function
zip(_:_:)
Here i took 2 array:
var arrOfInt = ["1","2","3"]
var arrOfIntString = ["one","two","three"]
for (intNum, intString) in zip(arrOfInt, arrOfIntString) {
print("Int:\(intNum), String:\(intString)")
}

Int in swift closure not incrementing

I have a closure in the code below that executes over an array of custom SKShapeNodes (blockNode). The problem I'm having is that completedActionsCount is always 1, no matter how many how many times the closure is executed. Is completedActionsCount just copied at the start of the closure?? Is there something I'm missing here?
for action in blockActionSets[index]
{
var blockActionSet = blockActionSets[index]
var completedActionsCount = 0
let blockNode = getBlockNodeForBlock(action.block)
if blockNode
{
blockNode!.updateWithAction(action, completion:
{ blockAction in
completedActionsCount++
println(completedActionsCount)
println(blockActionSet.count)
if (completedActionsCount == blockActionSet.count)
{
index = index + 1
self.executeBlockActionSetAtIndexRecursive(index, blockActionSets: blockActionSets, completion: completion)
}
})
}
else
{
completedActionsCount++
}
}
The following code is how the block is executed:
func updateWithAction(blockAction : BlockAction, completion : (BlockAction) -> Void)
{
if blockAction.blockActionType == BlockActionType.ChangeColor
{
var startColor = CIColor(CGColor: self.fillColor.CGColor)
var endColor = CIColor(CGColor: UI.getUIColorForBlockType(blockAction.endBlockType).CGColor)
var startRed = startColor.red()
var startGreen = startColor.green()
var startBlue = startColor.blue()
var endRed = endColor.red()
var endGreen = endColor.green()
var endBlue = endColor.blue()
var action = SKAction.customActionWithDuration(CHANGE_COLOR_ANIMATION_DURATION, actionBlock: {(node : SKNode!, elapsedTime : CGFloat)->Void in
var blockNode = node as? BlockNode
if blockNode
{
var ratio : CGFloat = min(1.0, elapsedTime / CGFloat(self.CHANGE_COLOR_ANIMATION_DURATION))
blockNode!.fillColor = UIColor(red: startRed + ratio * (endRed - startRed),
green: startGreen + ratio * (endGreen - startGreen),
blue: startBlue + ratio * (endBlue - startBlue),
alpha: 1.0)
}
if elapsedTime >= CGFloat(self.CHANGE_COLOR_ANIMATION_DURATION)
{
completion(blockAction)
}
})
self.runAction(action)
}
}
I believe the issue is that you are defining the completedActionsCount variable inside the loop, so each time through the loop a new Int value is defined and the value is reset to 0
does this work for you?
var completedActionsCount = 0
for action in blockActionSets[index] {
var blockActionSet = blockActionSets[index]
…
}
re-reading the question… I'm not sure if you intended for each blockActionSet to have it's own counter. But, to answer one of your sub-questions, I'm pretty sure that even though Int is a value type, it is not being copied into the closure, it's available via the outer scope, and you should be able to modify it.
Is updateWithAction:completion: run in the current thread? if it's asynchronous, that may be a factor.
response to update: Generally any API that has "withDuration" in the name will be async to avoid blocking the main thread.
I'm not sure exactly what is happening, but if the counter is being copied because it's a value type I don't know if switching to the main thread will actually fix your issue, but it's worth a try:
blockNode!.updateWithAction(action, completion:
{ blockAction in
dispatch_async(dispatch_get_main_queue()) {
completedActionsCount++
…
}
}
})
I have somewhat low hopes for this approach though since I suspect the value is being copied and copying it again is certainly not going to give you back the original reference. You may be able to wrap the reference to the counter in a reference type (like a variable array or dictionary):
var completedActionsCounts : [Int:Int] = [:]
for action in blockActionSets[index] {
var blockActionSet = blockActionSets[index]
completedActionsCounts[index] = 0
…
blockNode!.updateWithAction(action, completion:
{ blockAction in
dispatch_async(dispatch_get_main_queue()) {
completedActionsCounts[index] += 1
…
}
}
})
}