I just started a new Xcode ARKit project and ran it on a physical device but in the console I\'m getting this output:
Main Thread Checker: UI API called on a
(Of course you can disable checker, but sometimes it can be helpful. Check https://stackoverflow.com/a/45689250/7183675 for this.)
Error is inside Apple framework, so only way I found do work around it was subclassing UIApplication class and check application state on main thread this way:
1) Add main.swift file (this name is really important!) with these lines
import UIKit
UIApplicationMain(
CommandLine.argc,
CommandLine.unsafeArgv,
NSStringFromClass(MyApplicationClass.self),
NSStringFromClass(MyDelegateClass.self)
)
2) Remove @UIApplicationMain
from MyDelegateClass
if it was there or project wont compile because of multiple entry points
3) In MyApplicationClass.swift add this:
import UIKit
class MyApplicationClass: UIApplication {
let semaphore = DispatchSemaphore(value: 0)
override var applicationState: UIApplication.State {
if Thread.current == Thread.main {
return super.applicationState
}
var toReturn = UIApplication.State.inactive
DispatchQueue.main.async { [weak self] in
guard let self = self else {return}
toReturn = self.superAppState()
self.semaphore.signal()
}
semaphore.wait()
return toReturn
}
private func superAppState() -> UIApplication.State {
return super.applicationState
}
}
Now you are calling UIApplication.applicationState on main thread, so issue won't occur.
Well, there is one more issue. If CMMotionManager is initialized from main thread then if will block main thread and wait for UIApplication.State. That means deadlock. In this case you can't set your semaphore. Only way to return credible state avoiding deadlock would be implementing application state this way:
4)
import UIKit
class MyApplicationClass: UIApplication {
private static var configured = false
private var state = UIApplication.State.inactive
override var applicationState: UIApplication.State {
if !MyApplicationClass.configured {
MyApplicationClass.configured = true
NotificationCenter.default.addObserver(self, selector: #selector(setStatus(_:)), name: UIApplication.didBecomeActiveNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(setStatus(_:)), name: UIApplication.willResignActiveNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(setStatus(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(setStatus(_:)), name: UIApplication.didEnterBackgroundNotification, object: nil)
}
if Thread.current == Thread.main {
return super.applicationState
}
return state
}
@objc func setStatus(_ notif: Notification) {
switch notif.name {
case UIApplication.didBecomeActiveNotification:
state = .active
case UIApplication.willResignActiveNotification:
state = .inactive
case UIApplication.willEnterForegroundNotification:
state = .background
case UIApplication.didEnterBackgroundNotification:
state = .background
default:
state = .inactive
}
}
}
Personally I don't like last solution, but I wasn't able to find another workaround