UIApplication.delegate must be used from main thread only [duplicate] - ios

This question already has answers here:
-[UIApplication delegate] must be called from main thread only
(2 answers)
Closed 5 years ago.
I have the following code in my app delegate as a shortcut for working with CoreData in my other viewControllers:
let ad = UIApplication.shared.delegate as! AppDelegate
let context = ad.persistentContainer.viewContext
However, I now get the error message:
"UI API called from background thread" and "UIApplication.delegate must be used from main thread only".
I am working with CoreData while my app is in the background, but this is the first time I've seen this error message. Does anybody know what's going on here?
Update: I tried to move this inside the appDelegate class itself, and using the following code -
let dispatch = DispatchQueue.main.async {
let ad = UIApplication.shared.delegate as! AppDelegate
let context = ad.persistentContainer.viewContext
}
Now, I can no longer access the ad and context variables outside the AppDelegate. Is there something I'm missing?

With ref to this (-[UIApplication delegate] must be called from main thread only) in Swift (for your query resolution)
DispatchQueue.main.async(execute: {
// Handle further UI related operations here....
//let ad = UIApplication.shared.delegate as! AppDelegate
//let context = ad.persistentContainer.viewContext
})
With edit: (Where is the correct place to declare ad and context? Should I declare these in my viewControllers in the main dispatch)
Place of variables (ad and context) declaration defines scope for it. You need to decide what would be scope of these variable. You can declare them Project or application level (Globally), class level or particular this function level.
If you want to use these variable in other ViewControllers then declare it globally or class level with public/open/internal access control.
var ad: AppDelegate! //or var ad: AppDelegate?
var context: NSManagedObjectContext! //or var context: NSManagedObjectContext?
DispatchQueue.main.async(execute: {
// Handle further UI related operations here....
ad = UIApplication.shared.delegate as! AppDelegate
context = ad.persistentContainer.viewContext
//or
//self.ad = UIApplication.shared.delegate as! AppDelegate
//self.context = ad.persistentContainer.viewContext
})

Related

Swift: avoid to force unwrap constant variable

I'm trying to avoid a force unwrap of global variables. How can I do that in this particular example:
let AppDelegate = UIApplication.shared.delegate as! AppDelegate
If you don't want to force-unwrap (which is fine here in this case), then use if let.
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
// Do something with appDelegate
}
But the force-unwrap is fine for this. You want the app to crash during development if you make a typo on the type of the app delegate. It will never fail at runtime unless you make a change to the code and ship it to Apple without at least running your app once.
Made this an extension so no copy paste anymore. This is as type safe as it gets since it only makes sense for the AppDelegate to implement UIApplicationDelegate and the one is always reachable via UIApplication.shared.delegate.
#if os(macOS)
import Cocoa
typealias ApplicationDelegate = NSApplicationDelegate
typealias Application = NSApplication
#else
import UIKit
typealias ApplicationDelegate = UIApplicationDelegate
typealias Application = UIApplication
#endif
public extension ApplicationDelegate {
static var shared: Self {
return Application.shared.delegate as! Self
}
}
If you want to use the variable locally then:
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
//logic here
}
or
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {return}
I don't think there is a way to use AppDelegate globally without using force casting.
Edited
To create a global variable, just declare it anywhere outside a class. For example:
var globalVariable = 1
class Person {
//logic here
}

UI API called on a background thread error [duplicate]

This question already has answers here:
-[UIApplication delegate] must be called from main thread only
(2 answers)
Closed 4 years ago.
I have a function that gets the context for storing to Core Data. I have it for a few view controllers, however I am getting an error.
private class func getContext() -> NSManagedObjectContext {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.weatherPersistentContainer.viewContext
}
However I get an error about accessing UI API being called from a background thread.
As you see from the error, you can't call UIApplication.shared from a background thread. Since you don't want to have to wrap every call to our getContext method in DispatchQueue.main.async, you can update your getContext method to do the necessary wrapping as needed:
private class func getContext() -> NSManagedObjectContext {
let appDelegate: AppDelegate
if Thread.current.isMainThread {
appDelegate = UIApplication.shared.delegate as! AppDelegate
} else {
appDelegate = DispatchQueue.main.sync {
return UIApplication.shared.delegate as! AppDelegate
}
}
return appDelegate.weatherPersistentContainer.viewContext
}
This code ensures that UIApplication.shared is only called on the main queue, even if getContext is called from a background thread. The nice part is that the result is returned on the original thread.

UILabel.text must be used from main thread only [duplicate]

When I using Swift4in Xcode 9 gives me
UIApplication.delegate must be used from main thread only
.... must be used from main thread only
UI API called from background thread Group
Purple Warning.
My codes;
var appDelegate = UIApplication.shared.delegate as! AppDelegate
public var context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let prefs:UserDefaults = UserDefaults.standard
var deviceUUID = UIDevice.current.identifierForVendor!.uuidString
Warning line is;
public var context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
Another warning like this;
let parameters = [
"tel": "\(self.phone.text!)"
] as [String : String]
Gives
UITextField.text must be used from main thread only
Same error again..
How can I fix it ? Any idea ?
You're making this call on a background queue. To fix, try something like…
public var context: NSManagedObjectContext
DispatchQueue.main.async {
var appDelegate = UIApplication.shared.delegate as! AppDelegate
context = appDelegate.persistentContainer.viewContext
}
Although this is a pretty bad way to do this… you're using your App Delegate as a global variable (which we all know is bad!)
You should look at passing the managed object context from view controller to view controller…
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:
(window?.rootViewController as? MyViewController)?.moc = persistentContainer.viewContext
}
and so on

How do I refactor my code to call AppDelegate on the main thread?

I recently started migrating my project from Swift3/Xcode8 to Swift4/Xcode9. My app crashes at runtime because the main thread sanitizer allows access to UIApplication.shared.delegate only on the main thread, resulting in a crash at startup. I have the following code, which worked fine in Swift 3 -
static var appDelegate: AppDelegate {
return UIApplication.shared.delegate as! AppDelegate;
}
Other classes in my code make access to appDelegate. I need to figure out a way to return UIApplication.shared.delegate from the main thread.
Note: Using an DispatchQueue.main.async{} block from wherever access is made to appDelegate is not an option. Need to use it only within the static var appDelegate declaration only.
Looking for a clever workaround.
Relevant crash message:
Main Thread Checker: UI API called on a background thread: -[UIApplication delegate]
PID: 1094, TID: 30824, Thread name: (none), Queue name: NSOperationQueue 0x60400043c540 (QOS: UNSPECIFIED), QoS: 0
Solved by using Dispatch Groups.
static var realDelegate: AppDelegate?;
static var appDelegate: AppDelegate {
if Thread.isMainThread{
return UIApplication.shared.delegate as! AppDelegate;
}
let dg = DispatchGroup();
dg.enter()
DispatchQueue.main.async{
realDelegate = UIApplication.shared.delegate as? AppDelegate;
dg.leave();
}
dg.wait();
return realDelegate!;
}
and call it somewhere else for
let appDelegate = AppDelegate(). realDelegate!
I use the following:
static var shared: AppDelegate? {
if Thread.isMainThread {
return UIApplication.shared.delegate as? AppDelegate
}
var appDelegate: AppDelegate?
DispatchQueue.main.sync {
appDelegate = UIApplication.shared.delegate as? AppDelegate
}
return appDelegate
}

How to use a variable in other class that is defined in AppDelegate

I have a variable var window: UIWindow? in AppDelegate.swift file inside class AppDelegate that I want to use in other class xyz.swift file inside class xyz as explained in here Get current view controller from the app delegate (modal is possible) but I am getting error at the first line any help will be appreciated. here is the code from xyz.swift
func CurrentView() -> UIView
{
let navigationController = window?.rootViewController as? UINavigationController // Use of Unresolved identifier 'window'
if let activeController = navigationController!.visibleViewController {
if activeController.isKindOfClass( MyViewController ) {
println("I have found my controller!")
}
}
}
Even if I use let navigationController = AppDelegate.window?.rootViewController as? UINavigationController error is 'AppDelegate.Type' does not have member named 'window'
You may have to do as follows:
var appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let navigationController = appDelegate.window?....
As #Dave Durbin has pointed out you are trying to use a variable defined in one class into another class without the reference of the defining class.
This line of code is in xyz.swift
let navigationController = window?.rootViewController as? UINavigationController // Use of Unresolved identifier 'window'
You don't provide any context for window so it's expected to be in this class or a global variable.
This is closer:
navigationController = AppDelegate.window?.rootViewController as? UINavigationController
and you seem to realise that you need to reference the window variable within your AppDelegate instance however the syntax you are using references a static variable and window is a member variable.
I suggest you read through the swift manual and gain a better understanding of variable scopes and check this:
How do I get a reference to the app delegate in Swift?

Resources