Core data entity disappears when app resumes from background - ios

I have an app that uses a read-only Core Data database (meaning I never write to the database). I have a view controller that fetches some core data entities and displays them in a table. So for example, I have a core data entity named Photo, and in my view controller I have, say #property (nonatomic, strong) NSArray *photos;, which is a list of Photo objects.
Now everything works fine when the app launches fresh. But when I go to that view and exit the app (so it's still in the background) and reenter it, the #property I've set up becomes nil. I still see all UITableViewCells currently on screen. When I attempt to scroll, though, the app crashes.
I have multiple views that use Core Data stuff, but some of them work just fine. I'm wondering why for some view controllers my Core Data object set up as a #property disappears when the app returns to foreground.
Thanks!

First recommendation: you should be using an NSFetchedResultsController rather than an array. This is much more efficient and will take care of all memory considerations for you, beside many other advantages. As a matter of fact, it was designed specifically to manage the datasource of a table view based on Core Data.
If you still want to use your array, I speculate that you failed to initialize it properly.
In your viewDidLoad method, do not just assign the fetched results from core data but do it with proper initialization:
self.photos = [[NSArray alloc] initWithArray:coreDataResults];
Now the array should persist.

I think I figured out what's wrong with my code. So this is what I did to fetch the core data stuff:
[DatabaseHelper openDatabase:#"database" usingBlock:^(UIManagedDocument *document) {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"Shop"];
request.predicate = [NSPredicate predicateWithFormat:#"ecategory = %#", self.category];
NSError *error = nil;
NSArray *matches = [document.managedObjectContext executeFetchRequest:request error:&error];
self.shops = matches;
[self updateTable];
}];
This correctly fetches the results, but the problem is, although I think I retained the Core Data objects, they actually belong to a UIManagedDocumentContext, and I didn't retain that. So when the app returns to foreground, the context is gone, and the core data objects become nil.
So what I did is declare a new property: #property (nonatomic, strong) UIManagedDocument *database; and then set self.database = document; in the block. Now the controller will retain the context and everything works fine.

Related

Fine Grained Notifications old/new RLMResults

I am trying to find out the inserted/deleted objects after a write transaction.
With the fine grained notification block I can get:
/// The indices of objects in the previous version of the collection which have
/// been removed from this one.
#property (nonatomic, readonly) NSArray<NSNumber *> *deletions;
/// The indices in the new version of the collection which were newly inserted.
#property (nonatomic, readonly) NSArray<NSNumber *> *insertions;
My question is in this snippet:
RLMResults *contacts = [CYRLMAddressBookContact allObjects];
RLMNotificationToken *token = [contacts addNotificationBlock:^(RLMResults *_Nullable results,
RLMCollectionChange *_Nullable change,
NSError *_Nullable error) {}];
Could contacts be considered a "previous version" of the collection?
If not is it safe to convert contacts to an NSArray and that would be a "previous version" of the collection?
In this code sample, when the notification block fires, contacts will already be in the updated state. The deletions and insertions values will be in relation to the updated state so that any UI elements still displaying the previous state of each item in contacts may be updated to match.
No, I don't think it's unsafe to do that. If you copied the contents of contacts to an NSArray, that would certainly capture the previous ordering of contacts before the notification. Realm objects are live though, so if the property values inside any of the contacts objects changed, that would be reflected in the array as well.
That being said, you do need to be careful when copying objects from RLMResults to an NSArray. Directly touching each object as you pass it to the array will cause it to be lazy-loaded by Realm, which may result in a performance hit.

Single fetchRequest for multiple entities - use relationships and/or entity inheritance?

I have a simple app which uses Core Data. As the user progresses through each ViewController, the managedObject gets passed through each view then saved to the store on completion. At the minute my Model has a single Entity with over 100 properties, which I would now like to separate into multiple entities. I am fairly new to iOS programming and not overly confident with database work, so apologies if my terminology is not correct.
An example of how my app works: On my ClientViewController, I declare managedObjectNGLS which currently stores all attributes.
// Identify the app delegate
NGLSAppDelegate *appDelegate = (NGLSAppDelegate *)[[UIApplication sharedApplication]delegate];
// Use appDelegate object to identify managed object context
NSManagedObjectContext *context = [appDelegate managedObjectContext];
// Create new managed object using the NGLS entity description
NSManagedObject *ManagedObjectNGLS;
ManagedObjectNGLS = [NSEntityDescription insertNewObjectForEntityForName:#"NGLS"
inManagedObjectContext:context];
// Declare managed object
self.managedObjectNGLS = ManagedObjectNGLS;
This gets passed through to ServicesViewController.
// Allocate & initialise ServicesViewController
ServicesViewController *services = [[ServicesViewController alloc]initWithNibName:#"ServicesViewController"
bundle:nil];
// Pass managedObject to view
services.managedObjectNGLS = self.managedObjectNGLS;
// Push next view
[self.navigationController pushViewController:services animated:YES];
Once the user has selected the desired services, the managedObjectNGLS gets stored to my Model.
[[self.managedObjectNGLS managedObjectContext] save:&error];
In the AdminViewController, the user can export the entire contents of the NGLS entity using a fetchRequest. Please see this post on how I am achieving this (apologies if this seems like a duplicate post, but I am still trying to understand all of this).
My question: I would very much like someone to explain the best solution regarding my entity situation. I have tried to set a Parent Entity which seems to work fine when only two entities are involved, but when more are introduced I get lost. I am not sure about relationships or an abstract entity?
Ideally I would like to have separate entities for NGLS (holds various important attributes like date stamp), Admin (username, site location, etc), Client, Employer (possibly separated further into Employer1,...,Employer10 eventually), and Services. I am not sure whether to create an Abstract or Parent entity with no attributes, then just hold relationships to that with the separate entities?
I have laid out what I would expect the entities to look like in the editor, but I am unsure if this is correct. The idea of the app is to store details about one Client per time, so on that basis the Client can have many Employers and many Services. I have implemented an extremely simple "login" system where the lastObject in my Admin entity gets stored in the NGLS entity (see this post for a description), so the Admin entity doesn't necessarily have a relationship with anything (I may be wrong here?).
I have tried the following code but it just crashes the app:
NSMutableArray *entityArray = [NSMutableArray array];
for (NSString *entitySuffix in #[#"NGLS", #"Client", #"Services", #"Employer"]) {
NSString *entitySelect = [NSString stringWithFormat:#"%#", entitySuffix];
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:entitySelect];
[entityArray addObjectsFromArray:
[context executeFetchRequest:request error:nil]];
}
// CHCSVParser
NSOutputStream *stream = [[NSOutputStream alloc]initToMemory];
CHCSVWriter *writer = [[CHCSVWriter alloc]initWithOutputStream:stream
encoding:NSUTF8StringEncoding
delimiter:','];
// Fetch objects to write to .csv
for (Model *results in entityArray) {
[writer writeLineOfFields:#[results.site,
results.username,
results.dateStamp,
results.clientTitle,
results.clientForename,
// results from different entities
Again, I apologise for this post as it seems like I am asking for an outright answer, but I really just need someone to point me in the right direction on how to set up separate entities and use a single fetchRequest to fetch data from the entire Model with all of it's entities. Thanks in advance!

Using Core Data in app without UITableView

I have a simple to do list application. It uses dynamically generated text fields spaced programmatically for the tasks (I didn't use UITableView because of some custom animations and whatnot that I want to use).
In the app the user can create multiple lists (home, work, school, etc.) each with their own tasks.
I want to use Core Data to store the information
Saving the information is straightforward enough but updating my Core Data objects (List and Task) are where I'm getting stuck. Also how, with Core Data, to associate in a specific tasks with a specific list.
let me know if I need to clarify anything.
Your best bet is NSFetchedResultsController. You can use it exactly like in the pattern suggested by the Xcode templates (you can look at it by creating a new project Master-Detail and checking "User Core Data").
You can device your object model (entity Task) with a string attribute for the name as well as a NSNumber boolean attribute for done etc. I also recommend a timestamp and maybe a serial number for ordering it (I find NSOrderedSet unreliable). Your entity List should have a to-many relationship to Task. Pretty straight forward.
List <---->> Task
The only difference is now to find the right object, because you cannot use objectAtIndexPath. You can use the index in fetchedResultsController.fetchedObjects for that. Just make sure your objects are ordered as expected.
I'm not totally clear on your question, however, the task of updating a managed object is straightforward. When you're doing an initial add (similar to an "insert" in SQL) you might use code like this:
NSManagedObject *obj;
obj = [NSEntityDescription insertNewObjectForEntityForName:#"UserData" inManagedObjectContext:context];
[obj setValue:user forKey:#"userName"];
[obj setValue:goalCategory forKey:#"goalCategory"];
[obj setValue:goalDetail forKey:#"goalDetail"];
NSError __autoreleasing error;
[context save:&error];
That's about it for inserting a new item. For updating after you're found the managed object you're working on, you just change the values in the managed object and use [context save:&error]. Here's an example:
UserData *uData = (UserData *) managedObj;
uData.itemName = nameText;
NSError __autoreleasing *error;
[context save:&error];
That's pretty much it.
As to the update, once you have selected the object(s) to be updated, they are contained in
fetchedResultsController.fetchedObjects
which is an NSArray. So, you might do something like this:
UserData *uData = (UserData *) [fetchedResultsController.fetchedObjects objectAtIndex:3];
uData.completed = YES;
NSError __autoreleasing *error;
[context save:&error];
So, this would update the field completed in the UserData entity to be == YES for the object at index 3 in the fetchedObjects array.
I know there are other methods of updating and lots of options but I haven't found any need for them. fetchedObjects is an array containing the items returned by your fetch; to update them, cast each object to the entity (which is defined as a NSManagedObject), make the change then context save..
HTH.
First of all, think is it good idea to use Core Data for your project. If your model is light and simple, maybe it will be better to use plists.
If you choose Core Data, just remember 2 rules:
Each thread owns separate NSManagedObjectContext;
Perform operations with context only in its thread.
And don't worry about optimizations now. Realize any scheme of updating your storage. Make sure it works. And then you should try some other update methods.

Core Data Conundrum: Cannot call NSFetchedResults twice

I have a custom UITableViewController, called mainViewController.
It has a public property in the header file
#property (strong, nonatomic) NSMutableArray *someGroups;
This property is set in the viewDidLoad method
_someGroups = [[GGGroups findAllSortedBy:#"lastUpdated" ascending:NO withPredicate:[NSPredicate predicateWithFormat:#"ANY users = %#", [GGUser currentUser]]] mutableCopy];
However, for some reason, if I ever call the following, once the view has loaded, it returns 0 objects.
[[GGGroups findAllSortedBy:#"lastUpdated" ascending:NO withPredicate:[NSPredicate predicateWithFormat:#"ANY users = %#", [GGUser currentUser]]] mutableCopy];
This is quite annoying especially when I want to reloadData in the table view controller, since this array is the data source, so the table view becomes blank since this method returns 0 objects now.
I am currently using MagicalRecord, does anyone know why this occurs.
Thank you!
There is nothing in MagicalRecord that caches your find* requests. Every call to any of the find methods will generate a new request and perform a new fetch on that new request. My guesses as to why your second fetch is failing:
The default context (which you are implicitly using by not specifying one) is nil somehow
Your currentUser method is returning nil
Your data store is empty on that second call
You aren't actually calling your method to reload data at the right time
You are not refreshing your view of data
MagicalRecord has logging built in. I suggest turning it on and seeing if there are any errors that show up in the logs. Those will help pinpoint any issues that might be arising with Core Data.

Momento and 1 to Many Joins : CoreData Approach, Design Considerations and Opinions Sought

Given and coredata based app using an Indexcard metaphor. Each Indexcard can optionally have a one-to-many relationship with a number of other entities/tables; i.e. I'll use Momento's 'Moment' as a proxy for my Indexcard object and Momento's ancillaries of tags, locations, etc. , for these other objects/tables.
What is the 'fastest' way to show whether or not these foreign table relationships exist on probably the most important tableView in the entire app?
and
What would be the best approach for laying out the cell portion showing whether or not a relationship exists and the count of the number of each type of relationship?
Again, using Momento as a design pattern. With a link to a screenshot on Flickr (because stackOverflow won't let me post an image since I'm a noob.)
Maybe my ex-RDBMS stuff is contaminating my thinking, but they didn't do a mongo-join to get the values off to the right did they? [tags,events,people,locations]. There has to be a more elegant way that I'm just not seeing.
My thoughts for laying out the cells on the right was to possibly use some boolean if YES put up the icon and the count, but that seems pretty expensive for every cell.
I'm sure that the answer to this layout question would be driven by the approach taken in the first part of the problem. It doesn't seem that I would want to store ancillary relationships in the 'main/moment' IndexCard object for maintenance reasons.
Thanks in advance for any help.
If you set up a one-many relationship of indexcards to tags(or whatever), a fetched indexcard object should have an NSSet of tags as a property. Same for the others, and you should just be able to get the count of the set and display that next to each of the appropriate icons.
..Unless I'm misunderstanding your question.
edit: to answer the second part, you should indeed have a conditional in cellForRowAtIndex path that checks the count of each set and either just display it with the icon (possible to have 0 then, which is normally fine), or check whether it is 0 and hide the image if it is as you said. I don't think either solution will slow down your app since the data has already been fetched anyway by the time the cell is being rendered, but the solution where you just pass the count right through without checking if it's 0 would generally be fast overall.
edit to provide some sample code:
Your Core Data model would have an IndexCard entity and then an entity for each type of possible related object.
1)Model:
IndexCard - has a one-many relationship with each of the other entities
Tag
Location
Person
2)After creating this model and the corresponding Object classes you will end up with an IndexCard class that has the following in its header
#property (nonatomic, strong) NSSet *tags
#property (nonatomic, strong) NSSet *locations
#property (nonatomic, strong) NSSet *people
and of course the following in its implementation
#dynamic tags
#dynamic locations
#dynamic people
3)Now that we've established this Core Data model, we can perform an nsfetchrequest (of course when using a tableview, you should use an nsfetchedresultscontroller as it will dynamically fetch the IndexCards it needs as you're scrolling through the table). This code assumes that we have a usable NSManagedObjectContext in its scope (ideally passed in from the AppDelegate and set as an ivar) and that our IndexCard object has some sort of key/id property we can search by, lets call it "number"
NSNumber *numberWeWant = [NSNumber numberWithInt:1];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = [NSEntityDescription entityForName:#"IndexCard" inManagedObjectContext:ourContext];
request.predicate = [NSPredicate predicateWithFormat:#"number == %#", numberWeWant];
NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];
//didn't bother error checking in case no IndexCard matches
IndexCard *ourCard = [results lastObject];
//you can now use these to display in the cell or hide the appropriate icons if they = 0
int numTags = ourCard.tags.count;
int numLocations = ourCard.locations.count;
int numPeople = ourCard.people.count;
//just a sample of how we would access the individual related objects
for(Tag *tag in ourCard.tags)
{
//do whatever you want with each tag here
}
Again, this code is just to fetch a single IndexCard. In an actual table you would be initializing an nsfetchedresultscontroller when loading the view that contains it, and then just accessing the IndexCard at the position matching IndexPath.row in cellForRowAtIndexPath.
This also assumes there are a finite number of types of objects that IndexCard can be related to. If the types can change and increase randomly, this approach would need to be modified.
Hopefully this helps.

Resources