Currently I use this method to get the current view controller:
func topMostContoller()-> UIViewController?{
if !Thread.current.isMainThread{
logError(message: "ACCESSING TOP MOST CONTROLLER OUTSIDE OF MAIN THREAD")
return nil
}
let topMostVC:UIViewController = UIApplication.shared.keyWindow!.rootViewController!
return topVCWithRootVC(topMostVC)
}
This method goes through the hierarchy of the view controllers starting at the rootViewController until it reaches the top.
func topVCWithRootVC(_ rootVC:UIViewController)->UIViewController?{
if rootVC is UITabBarController{
let tabBarController:UITabBarController = rootVC as! UITabBarController
if let selectVC = tabBarController.selectedViewController{
return topVCWithRootVC(selectVC)
}else{
return nil
}
}else if rootVC.presentedViewController != nil{
if let presentedViewController = rootVC.presentedViewController! as UIViewController!{
return topVCWithRootVC(presentedViewController)
}else{
return nil
}
} else {
return rootVC
}
}
This issue is in topMostController
since it uses UIApplication.shared.keyWindow
and UIApplication.shared.keyWindow.rootViewController
which should not be used in a background thread. And I get these warning:
runtime: UI API called from background thread: UIWindow.rootViewController must be used from main thread only
runtime: UI API called from background thread: UIApplication.keyWindow must be used from main thread only
So my question is. Is there a thread safe way to access the currently displayed view controller?
Will accessing from the main thread suit your needs?
func getTopThreadSafe(completion: @escaping(UIViewController?) -> Void) {
DispatchQueue.main.async {
let topMostVC: UIViewController = UIApplication.shared.keyWindow?.rootViewController?
completion(topMostVC)
}
}
this can get a little bit confusing, since it's an asynchronous method, but my gut tells me that this'd be the safest option with whatever you're up to :)