Multiple timers in a loop - arrays

I have an array of characters, eg.
["A","E","I","O","U","Y"]
I should loop this array and, for every character, start a timer. That is: if the current letter is A, A must be printed and last 2 seconds. After A has been printed, we need to print "E" and it should last 3 seconds, "I" one second, "O" four seconds, etc.
I think I should create a new timer for every character, this way:
Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector:
#selector(printCharacter), userInfo: nil, repeats: false)
but I can't do it inside the loop.
I hoped there was a way to build an array of timers and then fire them sequentially, but this is not possible. Any idea?

You can achieve this using recursion and DispatchQueue only. If you are not familiar with recursion a simple way to understand it's by googling it
A sample code if print duration isn't linear would look like following :
var currentIndex = 0
let charactersArray = ["A","B","C","D"]
let durations : [TimeInterval] = [1.0,2.0,3.0,4.0]
func printCharacter(){
guard currentIndex < charactersArray.count else { return }
print(charactersArray[currentIndex])
DispatchQueue.main.asyncAfter(deadline: .now() + durations[currentIndex], execute: {
currentIndex = currentIndex + 1
printCharacter()
})
}
printCharacter()
where durations array represent time shown for each character

Related

SwiftUI Large Array (over 30K elements) taking forever to iterate over

I'm so confused as to what's going on but basically, this is my code. Maybe I just am stupid or do not enough above swift or something but I feel like this should take less than a second, but it takes a very long time to iterate through (i added the CFAbsoluteTimeGetCurrent) because I wanted to see how long each assignment took and they take around 0.0008949041366577148 seconds each, but over the span of 30K that adds up obviously. This is just test code but what I'm really trying to do is implement Agglomerative Clustering using a single linkage in Swift, at first I thought that my algorithm was written poorly but then I just tried to iterate over an array like this and it still took a long time. Does anyone know what's up?
I'm also aware that printing out statements in the console takes time but even after removing these statements the onAppear closure still took a while to finish.
Also sorry this is my first time ever posting on Stack Overflow so if please let me know if I should write my posts a certain way in the future.
#State private var mat: [Double?] = Array(repeating: nil, count: 30000)
var body: some View {
Text("HELLo")
.onAppear() {
for i in 0..<matrix.count {
let start = CFAbsoluteTimeGetCurrent()
mat[i] = 0
let diff = CFAbsoluteTimeGetCurrent() - start
print("HAC_SINGLELINK.init TIME: \(diff), ROW \(i) of \(matrix.count)")
}
}
}
I believe the time is caused by the number of modifications you do to your #State variable, which cause a lot of overhead.
With your initial code, on my machine, it took ~16 seconds. With my modified code, which does all of the modifications on a temporary non-state variable and then assigns to #State once, it takes 0.004 seconds:
.onAppear() {
let start = CFAbsoluteTimeGetCurrent()
var temp = matrix
for i in 0..<temp.count {
temp[i] = 0
}
let diff = CFAbsoluteTimeGetCurrent() - start
matrix = temp
print("HAC_SINGLELINK.init TIME: \(diff) \(matrix.count)")
}

Executing morse code with flashlight from array

I have an array of characters "." and "-" which is a translated text to morse code. They are of course in a extacly specified order because of the morse code. Now i want to convert this array to long and short flashes from flashlight in iPhone, after button is tapped. I tried of course looping through this array with 'if' statement, with scheduledTimers just like below, but obviously that doesnt worked out, loop was executing immediately and flashlight was launching only once.
let dash: Character = "-"
let dot: Character = "."
for i in translatedArray {
if i == dash{
//two sec delay at the beggining because of the pause beetween each flash which is equal to one dot
timer1 = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(toggleFlashlightOn), userInfo: nil, repeats: false)
timer2 = Timer.scheduledTimer(timeInterval: 6, target: self, selector: #selector(toggleFlashlightOff), userInfo: nil, repeats: false)
print("long flash activated")
}else if i == dot{
timer1 = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(toggleFlashlightOn), userInfo: nil, repeats: false)
timer2 = Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(toggleFlashlightOff), userInfo: nil, repeats: false)
print("short flash activated")
} else { return }
}
I also tried to delay separate 'if' iteration with delay() function, but execution of this loop with morse code should have irregular time interval beetween each iteration depending on previous character aka previous lenght of flash, and delay() have only static value of seconds, so this didn't worked either.
Finally i found this thread here. And except updating it to swift4, and changing array of morse code characters to array of time intervals, which i did succesfully this would be a perfect solution for my problem... but it doesn't work. It turns on flashlight in only one mode which is ON, and refresh endlesly printing "ON" regularly with first time interval value in the array, for example every 2 seconds or so. So it never turns off the flashlight.
let shortFlash: Double = 2
let longFlash: Double = 4
let pause: Double = 2
var sequenceOfFlashes: [Double] = []
//this works ok, prints correct values in correct order from sequenceOfFlashes array
func setupMorseFlashesSequence(){
let dot: Character = "•"
let line: Character = "—"
for i in translatedArray {
switch i {
case dot:
sequenceOfFlashes.append(shortFlash)
sequenceOfFlashes.append(pause)
case line:
sequenceOfFlashes.append(longFlash)
sequenceOfFlashes.append(pause)
default:
return
}
}
print(sequenceOfFlashes)
}
var index: Int = 0
weak var timer: Timer?
func scheduleTimer(){
timer = Timer.scheduledTimer(timeInterval: sequenceOfFlashes[index], target: self, selector: #selector(timerTick), userInfo: nil, repeats: false)
}
#objc func timerTick(){
if index == sequenceOfFlashes.count {
stop()
}
turnFlashlight(on: index % 2 == 0)
scheduleTimer()
}
func start(){
index = 0
turnFlashlight(on: true)
scheduleTimer()
}
func stop(){
timer?.invalidate()
turnFlashlight(on: false)
}
deinit{
timer?.invalidate()
}
how can i achieve executing this flashes one by one with different times?
You forgot to increment index in timerTick():
#objc func timerTick(){
index += 1
if index == sequenceOfFlashes.count {
stop()
}
turnFlashlight(on: index % 2 == 0)
scheduleTimer()
}

Removing data from array correctly

I am facing the problem that I fill up an array with data and when I want to remove it later, even though I call the removeAll() the array still is not empty.
Better see my code for a more clear view on the problem
Updated new code
#objc func textFieldDidChange() {
doSearch()
if let commentText = commentTextField.text , !commentText.isEmpty {
sendButton.setTitleColor(UIColor.blue, for: UIControlState.normal)
sendButton.isEnabled = true
return
}
sendButton.setTitleColor(UIColor.lightGray, for: UIControlState.normal)
sendButton.isEnabled = false
}
func doSearch() {
let caption = commentTextField.text
let words = caption?.components(separatedBy: CharacterSet.whitespacesAndNewlines)
self.usersSuggestion.removeAll()
for var word in words! {
self.usersSuggestion.removeAll()
if word.hasPrefix("#") {
word = word.trimmingCharacters(in: CharacterSet.punctuationCharacters)
self.isCellSelected = true
self.usersSuggestion.removeAll()
API.User.suggestUsers(withText: word, completion: { (user) in
print("closure", word)
self.usersSuggestion.append(user)
self.tableView.reloadData()
print("#", self.usersSuggestion.count)
})
self.usersSuggestion.removeAll()
tableView.reloadData()
} else {
self.isCellSelected = false
self.usersSuggestion.removeAll()
tableView.reloadData()
}
self.usersSuggestion.removeAll()
tableView.reloadData()
}
}
I am facing the problem here that the array, even though I call multiple times the removeAll() method, never gets back to 0. When I have 2 user, link one, the next time I write an # I get the 2 users+ the linked user (so him twice). I can do it infinitely, having like 100 userSuggestions with just 2 existing users.
photos
You are printing the count immediately after inserting an element in the array. This will alway give a count of 1.
// count was zero
self.hashTags.insert(hashTag, at: 0) // adding one
print("# = ", self.hashTags.count) // count is now 1
Given that the API.user... function uses a completion handler, I would assume that this happens in a separate thread or at least asynchronously so you could event get into situations where the UI shows empty and suddenly shows one.
You may want to structure your code differently to better convey the separation between requesting data and displaying the (asynchronously obtained) results. Embedded completion handlers tend be misleading and give the impression that execution will happen in their visually organized sequence.

Create a function to iterate and cycle through an array in Swift

I'm trying to create a function in Xcode that I can call every time I hit a button to iterate through an array in sequence which then updates the value of the button title.
I can't seem to crack the challenge. I've tried various iterations of while loops and if statements but everytime I run it I end straight up at the last value in the array. Here's the code I've got at the moment, I tried to add a break clause to stop the function from automatically iterating through the whole array but it's now throwing up an error message saying that the code after the return statement will never be executed:
So, I've created an instance of a button within my viewController as follows:
#IBAction func repCount() {
repCountButton.setTitle("\(repCounter.repCount())", forState: UIControlState.Normal)
I'm hoping that this will then update the title of the button with what I return from the repCount function that is called every time the button is pressed.
I've set up the function in a separate Swift file called repCounter and my code for the repCount function is as follows:
var repArray = [1,2,3,4,5]
var repArrayIndex: Int = 0
func repCount () -> String {
if repArrayIndex < repArray.count {
while repArrayIndex < repArray.count {
return "\(repArray[repArrayIndex])"
break
}
repArrayIndex++
} else {
return "\(repArray[0])"
}
}
What I'd like this to do is to cycle through the array every time it is called and once it's got to the end of the array to start cycling from the beginning of the array again.
Thanks in advance for any help!
I'm not at a computer where I can pull up XCode to test it, but I think the version below will do what you want. It isn't the most elegant code, but it is very straightforward. You have to do all the index juggling before the return statement since once the code hits a return, nothing following it will be executed.
I added some code to reset the index once it reaches the end of the array.
var repArray = [1,2,3,4,5]
var repArrayIndex: Int = 0
func repCount () -> String {
while repArrayIndex < repArray.count {
var curIndex = repArrayIndex
repArrayIndex = repArrayIndex + 1;
if repArrayIndex >= repArray.count {
repArrayIndex = 0
}
return "\(repArray[curIndex])"
}
return "\(repArray[0])"
}
Another option to getting this count, without iterating, is to do
// From Swift 1.2
func repCount () -> String {
return count(repArray)
}
// Before Swift 1.2
func repCount () -> String {
return countElements(repArray)
}
If you insist on iterating there are multiple options, see Iterating over an array, one which could be:
var count = 0
for rep in repArray {
count++
}
Or you could for the round trip iteration provided in the other answer, but why do it the hard way, when you don't need to? Or is there something you haven't told us?

Swift: test for expiration / invalidation of NSTimer seems to fail

Using Swift, I'm having difficulty testing for the expiration / invalidation of an NSTimer.
In particular, why does it appear that a test like while timer1 != nil { } or while timer1?.valid { } does not escape the null loop after it has been seemingly clearly invalidated and set to nil directly.
I have reviewed several of the questions relating to NSTimer already, and unless there is a version in Obj-C that I simply am not recognizing as the same issue due to my poor comprehension of ObjC properly, I don't believe this is covered (directly).
Please note, I'm not looking to directly "fix" the code, I'm looking for comprehension.
// ViewController.swift
// NSTimerTest
import UIKit
class ViewController: UIViewController {
var z:Int = 0
var timer1:NSTimer? = nil
#IBOutlet var lblMessage: UILabel
#IBOutlet var lbl2: UILabel
#IBAction func btnPress(sender: AnyObject) {
doBtnPress()
}
override func viewDidLoad() {
super.viewDidLoad()
z = 0
lblMessage.text = "Timer was called \(z) times."
lbl2.text = "waiting for countdown"
}
func doBtnPress(){
lblMessage.text = "doBtnPress!!"
timer1 = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "doTimedAction", userInfo: nil, repeats: true)
// All of the below infinitely loop
// while timer1?.valid { } // <=== never escapes, also depreciated in IOS 8.0 ???
// while timer1 { } // <==== never escapes
// while timer1 != nil { } // <==== never escapes
// while z >= 0 { } // <==== test counter instead, but no use
lbl2.text = "timer has been invalidated"
}
func doTimedAction(){
lblMessage.text = "Timer was called \(++z) times."
if z >= 10 {
timer1?.invalidate()
timer1 = nil
z = -1
}
}
}
As you can see, I have two labels -- one that is simply updated with the number of times the NSTimer has been invoked, and the other that hold be updated after I invalidate the timer.
With the testing lines commented out as noted above, things work as I expect. The label indicating the number of timer invocations updates each second (or so) and the immediate but factually incorrect display of the statement that "Timer has been invalidated" )
However, it my expectation that de-commenting any of the three lines that follow should allow the timer to run through 10 iterations, and then be invalidated, allowing the "Timer invalidated" message to display. Instead, however, I end up stuck infinitely in the empty loop, as if the condition(s) never come true. The UI does not update at all.
Instead of testing the timer, I also tried testing the counter z, but this does not work either, leading me to believe there is something more elemental at play that I am not understanding. For example, using while z >= 0 { println("z is \(z)")} in the location results in z is 0 -- the reason the loop is infinite. But again, if I comment out the line, z does in fact clearly increment, as the label DOES change, reflecting the cane in the counter z.
Again, I'm looking more to understand WHY things are failing (i.e., what I am failing to comprehend here). I understand, for example, I could make this "work" by having the doTimedAction() func directly update the label --- but that would not help me understand why my tests fail.
Whilst you are in the loop, you are blocking the timer from firing, as both the timer and loop are on the same thread.
Check out the documentation on Run Loops, and also this post on creating an NSTimer on a background thread.

Resources