multi-row sqlite db update in iOS CoreData - ios

I'm using an NSFetchedResultsController with a UITableView to present data in an iOS app from an underlying sqlite database. At a certain points in the flow of the app, I need to update flags in multiple rows of the database from a separate controller.
I don't think iterating thru all the objects, changing the flag, then resaving is ideal.
Is there a best way to do this?

You can do this way.
Resign the fetched controller as delegate (very important)
Perform change to CoreData and save context
Assign again fetched controller delegate and execute fetch again
Call reload table, only visible row will be updated
you can deal with this with the usual CoreData notification about save and change of context.
You can perform any operations you want, they will be notified only when the context is saved.

Related

How to handle CRUD operation with Diffable Data Source & NSFetchedResultsController

I have a simple product store screen of 4 rows with each row containing 4-5 products.
Currently I have the following setup
Controller calls the backend server and gets the data
Save the models to Core Data
This triggers an update of NSFetchedResultsController
Apply the new snapshot using Diffable Data Source
This setup works great when new products are added to any row or are updated in some way.
But I am unable to find a way on how to handle the scenario when a product is removed in the API response ? because whenever I receive a response I save it in Core Data base and then rely on the FRC trigger to apply the snapshot.
So If an existing product is removed in the response the product still continues to show in the store as it also needs to be removed in the DB as well. So before saving into DB I always have to delete all the existing data and then save the new data for changes to take effect.
Can anybody suggest a change in my setup to handle this scenario or any particular flow to handle it ?

CoreData refreshAllObjects() does not refresh all objects

We have three separate apps which are in same App Group and access the same CoreData store. Problem is that when I change something in item in NSOrderedSet from relationship in managed object, save go to another app where refresh is performed, changed data are not there.
We are using NSPersistentContainer and only one context in each app, container.newBackgroundContext() saved to property in singleton. For each app when app goes to BG save() is performed on that context and when app goes to FG refreshAllObjects() is called on the context.
When I change some basic attribute in managed object it is changed properly in another app. But when I change some property in item from NSSet which is a relationship on managed object this change is not visible in another app.
While I was debugging I tried to call fetch but it also provides only old data. Only when I called context.reset() and then fetch again it returns valid new data.
Problem is that I cannot use reset on whole context because I will lose all registered objects in app.
Is this valid behavior or bug that referenced objects changes are not applied when refreshAllObjects() is used?
Is there any way how to force fetch request to get data directly from the database and not cached one from context?
I found one solution to my problem. I was able to force fetch to fetch directly from persistence (not from context) by setting shouldRefreshRefetchedObjects property of NSFetchRequest to true.
I was not able to find a solution to just refresh already fetched objects - but this way I was able to get fresh data using new fetch at least.

Coredata safe clear subEntities

I want to ask for a safe way to clear subEntities in coredata.
I have my a many-to-many relationship like this: Product *<->* Product. Therefore, I've got to create a subEntity to hold some special values between (sortPosition, groupName.....).
So it's like this: Product *<->1 ProductSubEntity 1<->*Product.
When I download products from server's API, the easiest way to update correctly correspond to the server's result is:
Remove all child relationship ([self removeProductSubEntities:self.subEntities]).
Add sub from server's result.
Result: There'd be a lot of subEntity in coredata (which won't hold relationship to any product), and this might take storage/memory/cpu when CRUD (I think?). But I can't actual delete the subEntity (in case it's being hold reference to as an viewController's Object somewhere, and it might cause crash: access to a deleted object).
QUESTION:
How can I clear those sub entities (might occur sometimes) if:
No relationship to any product.
No actual reference from anywhere (any viewControllers or objects)???
P/S: I'm thinking of implement a batch delete when terminate app. Could that be consider a safe solution?
I don't consider this to be a datastore issue, rather a UI update issue. You should delete the objects from the datastore when you don't need them any more and you should update the UI accordingly.
1 thing you didn't mention is re-use. It's possible that your download may be an update to an existing item, which you could find and update, then life is easy all round. Arguably everything below still applies in this case though as your UI might not update to reflect changes and you may need to refresh the managed object.
For the UI update it's generally wise to observe the datastore for changes, usually with an NSFetchedResultsController. If you're doing this then your UI would automatically update itself with the changes.
If you're explicitly passing entity instances around then you should have some way to trigger an update explicitly, and exactly how that works depends on your UI. Generally speaking you'd be doing something like posting a UINotification to tell the system that the datastore changed and they need to re-validate their data objects. For the UI you shouldn't be showing now-dead objects to the user, and in your question where you talk about not deleting to avoid crashes, it's probably worse to allow the user to update invalid objects and just quietly not telling them that their updates won't be saved. When the notification is received you may want to pop a (some) controller(s) off the stack, or re-query the datastore for the new data to be displayed.
If for some reason you don't want to do the above, then yes, you can query for all of the entities with a nil relationship and then batch delete them. This should be done on a background thread just like data loading and I'd recommend doing it on app load instead of close (because you won't have so many view controllers loaded and the ones that are should all have only valid references now...).

Does entity relation also get loaded into memory when entity gets fetched in CoreData?

I'm working on an app that allows the user to create a little comic from a photo. I've implemented CoreData so the user can save and reedit their comic. I've created two entities: ComicInfo and ComicDetails which have a relationship with one another (I did it this way from reading the Ray Wenderlich tutorial). So when the user saves a comic, I make a new ComicInfo NSManagedObject, store the name and thumbnail in it, then I create a ComicDetails NSManagedObject, store various attributes in that, and I store the ComicDetails in the ComicInfo and save.
I've created a UITableView that displays the thumbnails and names from all the ComicInfo objects to allow the user to select one so they can edit the comic.
What I'm wondering is, why did I separate this into two entities instead of just doing them in one? I'm assuming that when I load all of the ComicInfos from CoreData, that the ComicDetails will also be loaded into memory, or am I wrong about that? In a perfect world, I'd like it so only the ComicInfos are loaded when the UITableView is launched, then when the user selects one of the rows, the associated ComicDetails will then be loaded into memory. Is that not what's happening here?
The reason to have two entities is to get the behavior you're hoping for. In general when you do a Core Data fetch, only instances of the entity type you request are loaded into memory. Related objects are not immediately loaded. Instead, Core Data loads them automatically on demand.
In your case, if you fetch ComicInfos, only ComicInfos get loaded. But if you take one of those objects and access its details attribute, the related ComicDetails instance is automatically loaded, without needing to do another fetch. The related objects are only loaded when you ask for them. So, you're getting the behavior you want.
If you ever want to override this behavior, you can use setRelationshipKeyPathsForPrefetching: to force NSFetchRequest to load not only the objects you actually fetch, but also specific related objects as well.

NSManagedObject delete not working properly

I am deleting a NSManagedObject using the statement
[managedObjectContext deleteObject:obj];
And after that am storing the changes to that persistent store. And when I tried to view my actual table using some database viewer, I could see the object still there in the table. I was confused and I made a refresh call after deletion as below (just before saving into persistent store).
[managedObjectContext refreshObject:obj mergeChanges:YES];
And when I tried to view the table now, I couldn't see the object. It's working now, but I am confused because of 2 reasons.
1: I am deleting many objects in many other places, and am not making a refresh every where, though am saving into the persistent store.
2: I don't understand the concept of refreshing an object that was already deleted.
Can any one help me out? Thanks in advance!
it is not easy to answer without a little bit more context.
That said, if you delete a managed object, fetchedResultsController won't be updated unless they were created on the same managedobjectContext. In other words two database fetch if issued from two different managed context will have an out of sync view of the database state.
I would advise you to check if this isn't the case.
Also, you shouldn't have to perform a refresh - except in very specific cases - NSFetchresultController is notified about the database changes, provided you implemented the NSFetchedResultsControllerDelegate protocol.

Resources