NSFetchedResultsController with external changes? - core-data

I'm reading a CoreData database in a WatchKit extension, and changing the store from the parent iPhone application. I'd like to use NSFetchedResultsController to drive changes to the watch UI, but NSFetchedResultsController in the extension doesn't respond to changes made to the store in the parent application. Is there any way to get the secondary process to respond to changes made in the first process?

Some things to try/consider:
Do you have App Groups enabled?
If so, is your data store in a location shared between your host app and the extension?
If so does deleting the cached data, as referenced here help?

Read this answer to very similar question: https://stackoverflow.com/a/29566287/1757229
Also make sure you set stalenessInterval to 0.

I faced the same problem. My solution applies if you want to update the watch app on main app updates, but it could be easily extended to go both ways.
Note that I'm using a simple extension on NSNotificationCenter in order to be able to post and observe Darwin notification more easily.
1. Post the Darwin notification
In my CoreData store manager, whenever I save the main managed object context, I post a Darwin notification:
notificationCenter.addObserverForName(NSManagedObjectContextDidSaveNotification, object: self.managedObjectContext, queue: NSOperationQueue.mainQueue(), usingBlock: { [weak s = self] notification in
if let moc = notification.object as? NSManagedObjectContext where moc == s?.managedObjectContext {
notificationCenter.postDarwinNotificationWithName(IPCNotifications.DidUpdateStoreNotification)
}
})
2. Listen for the Darwin notification (but only on Watch)
I listen for the same Darwin notification in the same class, but making sure I am on the actual watch extension (in order to avoid to refresh the context that just got updated). I'm not using a framework (must target also iOS 7) so I just added the same CoreDataManager on both main app and watch extension. In order to determine where I am, I use a compile time flag.
#if WATCHAPP
notificationCenter.addObserverForDarwinNotification(self, selector: "resetContext", name: IPCNotifications.DidUpdateStoreNotification)
#endif
3. Reset context
When the watch extension receives the notification, it resets the MOC context, and sends an internal notification to tell FRCs to update themselves. I'm not sure why, but it wasn't working fine without using a little delay (suggestions are welcome)
func resetContext() {
self.managedObjectContext?.reset()
delay(1) {
NSNotificationCenter.defaultCenter().postNotificationName(Notifications.ForceDataReload, object: self.managedObjectContext?.persistentStoreCoordinator)
}
}
4. Finally, update the FRCs
In my case, I was embedding a plain FRC in a data structure so I added the observer outside of the FRC scope. Anyway you could easily subclass NSFetchedResultsController and add the following line in its init method (remember to stop observing on dealloc)
NSNotificationCenter.defaultCenter().addObserver(fetchedResultController, selector: "forceDataReload:", name: CoreDataStore.Notifications.ForceDataReload, object: fetchedResultController.managedObjectContext.persistentStoreCoordinator)
and
extension NSFetchedResultsController {
func forceDataReload(notification: NSNotification) {
var error : NSError?
if !self.performFetch(&error) {
Log.error("Error performing fetch update after forced data reload request: \(error)")
}
if let delegate = self.delegate {
self.delegate?.controllerDidChangeContent?(self)
}
}

At WWDC ‘17, Apple introduced a number of new Core Data features, one of which is Persistent History Tracking or NSPersistentHistory. But as of the time of writing, its API is still undocumented. Thus, the only real reference is the What’s New in Core Data WWDC session.
More info and an example here

Related

How to handle watchOS CoreData background save correctly?

My watchOS app uses core data for local storage. Saving the managed context is done in background:
var backgroundContext = persistentContainer.newBackgroundContext()
//…
backgroundContext.perform {
//…
let saveError = self.saveManagedContext(managedContext: self.backgroundContext)
completion(saveError)
}
//…
func saveManagedContext(managedContext: NSManagedObjectContext) -> Error? {
if !managedContext.hasChanges { return nil }
do {
try managedContext.save()
return nil
} catch let error as NSError {
return error
}
}
Very rarely, my context is not saved. One reason I can think of is the following:
After my data are changed, I initiate a background core data context save operation.
But before the background task starts, the watch extension is put by the user into background, and is then terminated by watchOS.
This probably also prevents the core data background save to execute.
My questions are:
- Is this scenario possible?
- If so, what would be the correct handling of a core data background context save?
PS: On the iOS side, I do the same, but here it is possible to request additional background processing time using
var bgTask: UIBackgroundTaskIdentifier = application.beginBackgroundTask(expirationHandler: {
//…
application.endBackgroundTask(bgTask)
}
By now, I think I can answer my question:
If the watch extension is put by the user into background, the extension delegate calls applicationDidEnterBackground(). The docs say:
The system typically suspends your app shortly after this method
returns; therefore, you should not call any asynchronous methods from
your applicationDidEnterBackground() implementation. Asynchronous
methods may not be able to complete before the app is suspended.
I think this also applies to background tasks that have been initiated before, so it is actually possible that a core data background save does not complete.
Thus, the core data save should be done on the main thread. My current solution is the following:
My background context is no longer set up using persistentContainer.newBackgroundContext(), since such a context is connected directly to the persistentContainer, and when this context is saved, changes are written to the persistent store, which may take relatively long. Instead, I now set up the background context by
var backgroundContext = NSManagedObjectContext.init(concurrencyType: .privateQueueConcurrencyType)
and set its parent property as
backgroundContext.parent = container.viewContext
where container is the persistent container. Now, when the background context is saved, it is not written to the persistent store, but to its parent, the view content that is handled by the main thread. Since this saving is only done in memory, it is pretty fast.
Additionally, in applicationDidEnterBackground() of the extension delegate, I save the view context. Since this is done on the main thread, The docs say:
The applicationDidEnterBackground() method is your last chance to
perform any cleanup before the app is terminated.
In normal circumstances, enough time should be provided by watchOS. If not, other docs say:
If needed, you can request additional background execution time by
calling the ProcessInfo class’s
performExpiringActivity(withReason:using:) method.
This is probably equivalent to setting up a background task in iOS as shown in my question.
Hope this helps somebody!

Trouble Updating Observable Collection when Bound to Gridview - UWP C#

Observablecollection is bound to gridview in UWP project. If I try to clear and add data it fails with an error because it can only be modified on the UI thread.
I have set up service broker with SQL to notify the app when there is a change to the data. This is working correctly. However, every time I try to clear and modify the observablecollection I get an exception thrown.
using (SqlDataReader dr = cmd.ExecuteReader())
{
while (dr.Read())
{
EmployeeLists.Add(new Employee { Name = dr[0].ToString(), Loc = dr[2].ToString() });
}
}
This is the code I'm using at first to populate the observable collection. I want to listen for changes which is working. But how do I update the changes and sync them to the observable collection?
I have tried clearing the employeelists observablecollection and then adding everything again. It seems clunky, but doesn't work anyway because It says I cannot modify from another thread. I have tried several solutions online, but I'm not that familiar with ASYNC programming. Can anyone point me in the right direction?!

UIDocumentPickerViewController NewBox App Hangs

I am referring WWDC 2014 sample app NewBox for document provider extension.
I am using following code from NeBox app, to import a document from Document Provider to my app.
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url {
BOOL startAccessingWorked = [url startAccessingSecurityScopedResource];
NSURL *ubiquityURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
NSLog(#"ubiquityURL %#",ubiquityURL);
NSLog(#"start %d",startAccessingWorked);
NSFileCoordinator *fileCoordinator = [[NSFileCoordinator alloc] init];
NSError *error;
[fileCoordinator coordinateReadingItemAtURL:url options:0 error:&error byAccessor:^(NSURL *newURL) {
NSData *data = [NSData dataWithContentsOfURL:newURL];
NSLog(#"error %#",error);
NSLog(#"data %#",data);
}];
[url stopAccessingSecurityScopedResource];
}
App totally hangs for coordinateReadingItemAtURL method.
Any inputs will be helpful.
I noticed this problem in NewBox app as well, and decided to trace it. So, there are two extensions in this app: Document Picker and File Provider. To make long story short, there is a race condition between the two when they try to access files within app's document storage folder.
In my opinion, the easiest method to trace down a problem is to put NSLog() in a bunch of locations. The problem is, however, that the debugging output generated by extension won't be visible in Xcode console. The good news is that you can open console in iOS Simulator app by clicking to Debug -> Open System Log menu. This will show all kinds of debugging messages, including those generated by extensions. You can find more about extension debugging here.
By using this method one can easily realize that execution gets stuck in File Provider's startProvidingItemAtURL method. More specifically, the following line causes a deadlock:
[self.fileCoordinator coordinateWritingItemAtURL:url options:0 error:&error byAccessor:^(NSURL *newURL) {
Why is that? Take a look at documentation for coordinateWritingItemAtURL:
If the url parameter specifies a file:
This method waits for other readers and writers of the exact same file to finish in-progress actions.
Function documentPicker that you mentioned calls a read operation, which in its turn triggers a write operation. This is a deadlock. I guess the easiest way to fix it would be to avoid using coordinateWritingItemAtURL in File Provider.
As per documentation:
Each of these methods wait synchronously on the same thread they were invoked on before invoking the passed-in accessor block on the same thread, instead of waiting asynchronously and scheduling invocation of the block on a specific queue.
Apple recommends that you not use file coordination inside this method. The system already guarantees that no other process can access the file while this method is executing. That's the sole reason for this deadlock.
Please refer to this documentation for more details.
You can use block also. Block works too fast, hang problem will get resolve.
Step 1: Take global variable of
UIDocumentPickerViewController *documentPicker;
also decalre
typedef void(^myCompletion)(BOOL);
Step 2: Write a method where allocation takes place and can send callback on completion
-(void) allocateDocumentPicker:(myCompletion) compblock{
//do stuff
documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:#[#"public.content"]
inMode:UIDocumentPickerModeImport];
documentPicker.delegate = self;
documentPicker.modalPresentationStyle = UIModalPresentationFormSheet;
compblock(YES);
}
Step 3: Call the method where allocation is taking place every time you want to open the composer but present it on receiving completion as YES.
-(IBAction)attachmentButtonClicked:(id)sender{
[self allocateDocumentPicker:^(BOOL finished) {
if(finished){
[self.parentScreen presentViewController:documentPicker animated:YES completion:nil];
}
}];
}
Simple Syntax to create own block, take reference from this link
Custom completion block for my own method

SPPlaylistCallbackProxy playlist: message sent to deallocated instance

I'm using cocoalibspotify for a Spotify iOS app.
At one point in my app, I'm creating a new playlist and add a number of tracks to the playlist. When doing so, I'm getting a crash since the created playlist instance seem to have been deallocated.
This is what the code looks like:
[[[SPSession sharedSession] userPlaylists]
createPlaylistWithName:playlistName
callback:^(SPPlaylist *createdPlaylist) {
if (createdPlaylist) {
[SPAsyncLoading waitUntilLoaded:createdPlaylist
then:^(NSArray *playlists) {
// Load all tracks using the URI's and add them to the playlist
SPPlaylist *playlist = [playlists objectAtIndex:0];
for (NSString *trackUri in trackUris) {
[[SPSession sharedSession]
trackForURL:[NSURL URLWithString:trackUri]
callback:^(SPTrack *track) {
if (track != nil) {
[SPAsyncLoading
waitUntilLoaded:track
then:^(NSArray *tracks) {
[playlist addItems:tracks atIndex:0 callback:NULL];
}];
}
}];
}
}];
}}];
This is the log message:
*** -[SPPlaylistCallbackProxy playlist]: message sent to deallocated instance 0x100e0120
I've tried retaining the playlist in my class, but I'm still getting the same problem. Am I missing something obvious here?
Bonus question: After having created a playlist or loaded a track (i.e. using -trackForURL:callback), do I have to use SPAsyncLoading, or is the object always already loaded?
(Note: I'm using ARC in my project.)
EDIT: I ran Zombies in instruments to see what was going on and got the following result when it crashed:
If you have an object (SPTrack, etc), you can add it to a playlist without waiting for it to load since loading only deals with metadata. You might want to wait for the playlist to load so you know the indexes you're using are correct (although, in this case 0 will always be valid).
Looking into the meat of your question now. It's most likely a bug in CocoaLibSpotify - your code looks fine.

Fire Off an asynchronous thread and save data in cache

I have an ASP.NET MVC 3 (.NET 4) web application.
This app fetches data from an Oracle database and mixes some information with another Sql Database.
Many tables are joined together and lot of database reading is involved.
I have already optimized the best I could the fetching side and I don't have problems with that.
I've use caching to save information I don't need to fetch over and over.
Now I would like to build a responsive interface and my goal is to present the users the order headers filtered, and load the order lines in background.
I want to do that cause I need to manage all the lines (order lines) as a whole cause of some calculations.
What I have done so far is using jQuery to make an Ajax call to my action where I fetch the order headers and save them in a cache (System.Web.Caching.Cache).
When the Ajax call has succeeded I fire off another Ajax call to fetch the lines (and, once again, save the result in a cache).
It works quite well.
Now I was trying to figure out if I can move some of this logic from the client to the server.
When my action is called I want to fetch the order header and start a new thread - responsible of the order lines fetching - and return the result to the client.
In a test app I tried both ThreadPool.QueueUserWorkItem and Task.Factory but I want the generated thread to access my cache.
I've put together a test app and done something like this:
TEST 1
[HttpPost]
public JsonResult RunTasks01()
{
var myCache = System.Web.HttpContext.Current.Cache;
myCache.Remove("KEY1");
ThreadPool.QueueUserWorkItem(o => MyFunc(1, 5000000, myCache));
return (Json(true, JsonRequestBehavior.DenyGet));
}
TEST 2
[HttpPost]
public JsonResult RunTasks02()
{
var myCache = System.Web.HttpContext.Current.Cache;
myCache.Remove("KEY1");
Task.Factory.StartNew(() =>
{
MyFunc(1, 5000000, myCache);
});
return (Json(true, JsonRequestBehavior.DenyGet));
}
MyFunc crates a list of items and save the result in a cache; pretty silly but it's just a test.
I would like to know if someone has a better solution or knows of some implications I might have access the cache in a separate thread?!
Is there anything I need to be aware of, I should avoid or I could improve ?
Thanks for your help.
One possible issue I can see with your approach is that System.Web.HttpContext.Current might not be available in a separate thread. As this thread could run later, once the request has finished. I would recommend you using the classes in the System.Runtime.Caching namespace that was introduced in .NET 4.0 instead of the old HttpContext.Cache.

Resources