12

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?

Krunal
  • 77,632
  • 48
  • 245
  • 261
LFHS
  • 295
  • 1
  • 2
  • 15

1 Answers1

8

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   

    })
Krunal
  • 77,632
  • 48
  • 245
  • 261
  • Awesome, this fixed the problem, thanks. Do I need to do `if let` when dealing with ad and context, since they're optionals now? Or can I just assume they won't be nil? – LFHS Nov 09 '17 at 14:03
  • If you use `?` optional declaration like `var ad: AppDelegate?` then you should use `if-let` to get value from variabel `ad` – Krunal Nov 09 '17 at 14:04
  • Right, but I made them implicitly unwrapped. Is there a chance that these will be nil when I access them, crashing my app? Would it be better to not implicitly unwrap them? – LFHS Nov 09 '17 at 14:07
  • 1
    Yes, it depends upon condition, you set around this block of code. Do you set any condition for this code block. I think there must be a condition because you may be using data base operation and as a successful result of database operation, these variables are initialized. So these variable may remain nil, if database operation fails. Check your code and condition. – Krunal Nov 09 '17 at 14:09
  • Thanks, I understand. Essentially, if the app isn't running in the main dispatch, I can't use coreData? – LFHS Nov 09 '17 at 14:19
  • Our operations decides, whether app should run in background or foreground. If will give you this error, when your operations are running in background and you are trying use result of background operation to update UI. – Krunal Nov 09 '17 at 14:30