0

Hi I hope somebody can help me out with this. When a user first logs into my application, an API call is made which will fetch existing data for that user in a database on the web server. When they first log in (if successful) I segue to another viewcontroller that displays a progress indicator which stops animating when the downloaded data is fully saved to Core Data, then segue's to the main vc of the app (where the user can start using the app).

Instead of having a progress indicator, I would rather have a real time indicator like a count of currently downloaded items that gets updated each time an item is saved to core data, but in this case I have no choice but to use NSNotificationCenter to listen for when each item downloaded from the API is saved to core data. So far I have:

override func viewDidAppear(animated: Bool) {
   super.viewDidAppear(true)
   NSNotificationCenter.defaultCenter().addObserver(self,   selector:"contextDidSave:", name: NSManagedObjectContextDidSaveNotification, object:nil)
} 

func contextDidSave(notification: NSNotification) {
  if notification.name == NSManagedObjectContextDidSaveNotification {
    var c = CoreMessage.getMessages(self.moc, ascending:true).count
    //the println works - i see the updated count
    println(c)
    //this doesnt work, the lbl does not get updated
    self.labelCount.text = "\(c)"
  }
}

I also tried to update the lable like this:

dispatch_async(dispatch_get_main_queue(), { () -> Void in
  self.labelCount.text = "\(c)"
})

Can anybody shed a light on what I might be doing wrong?

fredp613
  • 107
  • 1
  • 8
  • Perhaps your label is not connected to the UI? By the way - saving the context after each insert is not performant - use a batch instead: http://stackoverflow.com/questions/4145888/ios-coredata-batch-insert – charlyatwork Jul 15 '15 at 22:43
  • Thanks, its connected via IB outlet so no issue there. I think it has something to do with the work being performed on a separate thread, just not sure why I cannot access the main thread to update the label. Thanks for the batching comment, but I'm limiting the initial download to 500 records which I profiled to take less than 3secs and the user never needs to fetch that many records again. – fredp613 Jul 15 '15 at 23:42
  • Have you tried checking if the NSManagedObjectContext was initiated on the main thread - NSThread.isMainThread() – charlyatwork Jul 16 '15 at 05:47
  • Why you are setting observer in viewDidAppear? – Muhammad Hasan Irshad Apr 27 '21 at 21:22

1 Answers1

0

Several things here.

  1. You most likely have a threading issue. Turn on the CurrencyDebug flag and confirm. That will impact what makes it to the UI.
  2. You do not want to be doing a fetch here. First your fetch is going to slow down the import. Worse, you already have the data you just need to aggregate it.

Note: your if with a string compare is unneeded also.

The notification that you receive contains a -userInfo dictionary. Inside of that dictionary are three collection objects representing what was inserted, updated and deleted in the referenced save. You can and should be just querying those collections to see what has been changed and update your user interface from there.

Marcus S. Zarra
  • 46,571
  • 9
  • 101
  • 182
  • I believe you are correct regarding threading. I did a bit more research and it looks like the MOC is not thread safe, instead I should be passing the objectID when creating the core data record(s). I will turn on concurrencydebug flag to confirm this and report back. The issue seems to be with the getMessages function where I'm basically fetching all the messages from the API and saving them to core data using an MOC instantiated from a singleton in this method. – fredp613 Jul 16 '15 at 17:00
  • Yes that is a threading problem. But **again**, you do NOT need to fetch at all. Read the rest of my answer. – Marcus S. Zarra Jul 16 '15 at 19:35
  • To provide more context, this is a messaging app for a VOIP company who offers SMS but have no iOS client. Instead ppl use their web client to send and recieve text messages. So when they first sign into the app i'm building, I perform a download of all their existing messages using NSURLConnection async request and save each downloaded message into CoreData. Once the download / saving is completed, the app segues to a message summary view (very similar to messaging app tableview in iMessage). – fredp613 Jul 16 '15 at 20:14
  • This happens only upon the first login. The name "getMessages" might be a bit misleading, it basically just calls the API and saves to core data - i'm not actually fetching the core data messages. I will post my solution soon once I figure it out, but I believe i need to initialize a private MOC (instead of the regular MOC whcih always uses the main thread) and use perform block when saving/updating the objects I fetch from the API so ensure that the main thread remains unaffected. – fredp613 Jul 16 '15 at 20:14
  • I have explained the solution to updating your UI in the answer already. Processing of data should **always** be done with a private MOC to avoid blocking the UI. Updating the UI must **always** be done on the main thread as the UI is not thread safe. Best of luck. – Marcus S. Zarra Jul 18 '15 at 15:48
  • I'm already using a private context and the UI is still locked for some reason (even when using a performblock when saving the context). It might have something to do with the main run loop but thanks for your help I will have to do a bit more digging. Once I have a solution I will post it. – fredp613 Jul 20 '15 at 03:26
  • Run Instruments and the Time Profiler. Find out what is blocking your main thread or post the profile here and I will take a look. – Marcus S. Zarra Jul 20 '15 at 15:10