I have two views in my swift app. I am performing a segue as below.
ViewController.swift -----------------> GameViewController.swift
When loading the GameViewController an value array also passed to GameViewController.swift from ViewController.swift
A timer should be initialized in GameViewController.swift
I tried to initialize a timer and call a method through it, but it doesn't work.
Followings are my code snippets.
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")
}
}
Timers don't work on background queues (without some sleight of hand involving creating run loops or manually scheduling it on an existing run loop). But you should never initiate any UI update from anything other than the main queue, anyway.
So, since you're calling
performSegue
from aURLSession
completion closure (which runs on a background queue), it's actually runningviewDidLoad
from the background queue, too. Thus the attempt to schedule the timer is failing. To get around this, you have to manually dispatch theperformSegue
code to the main queue:If you're ever unsure whether some code is running on the main queue or not, refer to the documentation. Or you can use a dispatch precondition:
That way it will (in debug builds) stop the app if you've accidentally invoked the code from a background queue.
Unrelated to your current problem, but as an aside, to avoid a strong reference cycle between the timer and the view controller, you generally want to keep a reference to the timer so that you can
invalidate
it when the view disappears (e.g. create timer inviewDidAppear
and remove it inviewDidDisappear
). Otherwise you can end up retaining theGameViewController
after it was dismissed, e.g.:Or in iOS 10 or later, you can use the block-based variant with
weak
reference toself
, andinvalidate
indeinit
: