Swift Timer.scheduledTimer () not working

I have two kinds in my quick application. I am performing segue as below.

ViewController.swift -----------------> GameViewController.swift

When loading a GameViewController, an array of values ​​is also passed to GameViewController.swift from ViewController.swift

The timer must be initialized in GameViewController.swift

I tried to initialize the timer and call the method through it, but it does not work.

Submissions are my pieces of code.

ViewController.swift

func signIn(difficultyLvl:String){ let username = usernameTxt.text let password = passwordTxt.text let url = URL(string: "http://192.168.1.106/speed/scoreBoardController.php?username="+username!+"&password="+password!+"&action=SIGNIN") let task = URLSession.shared.dataTask(with: url!) {(data, response, error) in let isPassed = String(data: data!, encoding:.utf8)?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) var gameViewControllerParams = [Int: [String: String]]() gameViewControllerParams[0] = ["userId" : isPassed!] gameViewControllerParams[1] = ["difficultyLvl" : difficultyLvl] if(isPassed != "null"){ self.performSegue(withIdentifier: "gotoGame", sender: gameViewControllerParams) } } task.resume() } 

GameViewController.swift

 class GameViewController: UIViewController { var gameViewControllerParams = [Int: [String: String]]() override func viewDidLoad() { super.viewDidLoad() let _ = Timer.scheduledTimer(timeInterval: 1.0, target:self, selector: #selector(self.setCalculationLs), userInfo:nil,repeats: true) } func setCalculationLs(){ print("Timing") } } 
+5
source share
1 answer

Timers do not work in background queues (without much effort associated with creating start loops or manually scheduling in an existing run loop). However, you should never initiate a UI update from anything other than the main queue.

So, since you call performSegue from closing the completion of the URLSession (which runs in the background), it actually starts viewDidLoad from the background queue. Thus, an attempt to schedule a timer does not work. To get around this, you need to manually send the performSegue code to the main queue:

 let task = URLSession.shared.dataTask(with: url!) { data, response, error in ... if isPassed != "null" { DispatchQueue.main.async { self.performSegue(withIdentifier: "gotoGame", sender: ...) } } } 

If you never know if any code is running in the main queue or not, see the documentation . Or you can use the sending precondition:

 dispatchPrecondition(condition: .onQueue(.main)) 

Thus, it (in debug builds) will stop the application if you accidentally called the code from the background queue.


Unrelated to solve the current problem, but as an aside, in order to avoid a strong reference cycle between the timer and the view controller, you generally want to keep the timer reference so that you can invalidate it when the view disappears (for example, create a timer in viewDidAppear and delete it in viewDidDisappear ). Otherwise, you can save the GameViewController after it is fired, for example:

 class GameViewController: UIViewController { var timer: Timer? override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) timer = Timer.scheduledTimer(timeInterval: 1.0, target:self, selector: #selector(setCalculationLs), userInfo: nil, repeats: true) } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) timer?.invalidate() } func setCalculationLs() { print("Tick") } } 

Or in iOS 10, you can use the block option with a weak reference to self and invalidate in deinit :

 class GameViewController: UIViewController { var timer: Timer? override func viewDidLoad() { super.viewDidLoad() timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] timer in self?.setCalculationLs() } } deinit { timer?.invalidate() } func setCalculationLs() { print("Tick") } } 
+10
source

All Articles