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

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

Related

How do I initialize the Core Data's persistent container in AppDelegate and use it throughout the app?

I'm trying to use NSPersistentContainer in a number of view controllers throughout the app for Core Data.
Initially, I had set up the container in a single view controller, which worked fine:
First View Controller
var container: NSPersistentContainer!
container = NSPersistentContainer(name: "MyCoreData")
container.loadPersistentStores { storeDescription, error in
self.container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
if let error = error {
print("Unresolved error \(error.localizedDescription)")
}
}
But, I migrated this code to AppDelegate according to Apple's documentation:
AppDelegate
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "MyCoreData")
container.loadPersistentStores { description, error in
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
if let error = error {
fatalError("Unable to load persistent stores: \(error)")
}
}
return container
}()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if let firstVC = window?.rootViewController as? FirstViewController {
firstVC.container = persistentContainer
}
if let secondVC = window?.rootViewController as? SecondViewController {
secondVC.container = persistentContainer
}
return true
}
}
Each of my view controllers now have a variable called container of the NSPersistentContainer type. However, when I load the view controllers I get the following error:
Unexpectedly found nil while implicitly unwrapping an Optional value
This error points towards where container.viewContext is. More specifically:
fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: container.viewContext, sectionNameKeyPath: "title", cacheName: nil)
I'm not sure if I'm getting the error from rootViewController because I don't have a single root view controller due to the fact that I'm using the Tab Bar Controller.
My suggestion is a lazy instantiated computed property in an extension of UIViewController and actually you need only the managed object context.
extension UIViewController {
var context : NSManagedObjectContext {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}
}
You can also add a save function.

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.

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

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
})

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
}

className.type does not have a member called 'appDel'

I have a reference to my app delegate
let appDel: AppDelegate = (UIApplication.sharedApplication().delegate) as AppDelegate
That works just fine, but this line of code below gives a error:
let context: NSManagedObjectContext = appDel.managedObjectContext
It gives me the following error:
'vctGebruikers.Type' does not have a member named 'appDel'
I declared them right below my class like this:
class vctGebruikers: UIViewController, UITableViewDelegate, UITableViewDataSource {
let appDel: AppDelegate = (UIApplication.sharedApplication().delegate) as AppDelegate
let context: NSManagedObjectContext = appDel.managedObjectContext
}
The weird thing is when I paste the code in viewDidLoad or in a function the code just works fine. And I can't figure out what the problem is.
How can I solve this error?
EDIT:
I also need to acces context here:
let results: NSArray = context.executeFetchRequest(request, error: nil)
This is what I got working thanks to #Antonio, but now im not able to acces context and appDel
init(coder aDecoder: NSCoder!) {
let appDelegate = (UIApplication.sharedApplication().delegate) as AppDelegate
self.appDel = appDelegate
self.context = appDelegate.managedObjectContext
super.init(coder: aDecoder)
}
In swift you cannot reference self until all class properties have been initialized.
You are implicitly using self in this line:
let context: NSManagedObjectContext = appDel.managedObjectContext
The solution is to initialize them in an initializer, but using something like:
let appDelegate = (UIApplication.sharedApplication().delegate) as AppDelegate
self.appDel = appDelegate
self.context = appDelegate.managedObjectContext
Also read here, a similar question asked a couple days ago on SO.
One method of instantiating the context is to drill down instead of up from AppDelegate
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if let vc = window?.rootViewController as? UIViewController {
vc.context = self.managedObjectContext}
`
And in your view controller, tell it to populate dynamically
var context: NSManagedObjectContext!
Also don't forget to call
import CoreData everywhere you are using said objects
One more final method: Use a closure so that your reference will happen at run time:
var context: NSManagedObjectContext {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
return appDelegate.managedObjectContext
}

Resources