Fine Grained Notifications old/new RLMResults - ios

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.

Related

Core Data Installation

I am using core data to save data from web services in my app. On the first time of running, app creates the core data instance and attributes and properties and save all the data. My question is that, when the application runs second time or many times, Is the core data creates its instance and properties and save all data again, or again and again? I am sorry if my question is not relevant.
Thanks
If the Webservice + code for storing data to coredata gets called every time you run app, core data will store objects again and again
You can solve this by 2 ways
If your data from server remains the same, you can set a flag and check that coredata insertion should execute only once
If you data from server may keep on changing, you can Update data instead of inserting it again to avoid duplicate records.
If u want to make sure the data is unique, u should make sure the data object should have a unique key.
For example:
#interface Person : NSManagedObject
#property (nonatomic, retain) NSNumber * pid;
#property (nonatomic, retain) NSString * name;
#end
The property of Person.pid should be unique. It means that u can only get one person max with the appointed pid.
So before u insert new object, u should query the db with NSFetchRequest(NSPredicate) like this:
NSNumber *aimPid;// the person's pid
NSPredicate *predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"pid == %#", aimPid]];
If person exist, then just update and save.
If person not exist, then insert a new one and save.
More:https://developer.apple.com/library/mac/documentation/Cocoa/Reference/CoreDataFramework/Classes/NSFetchRequest_Class/

Parse - Using relations versus pointers?

I am using Parse as my backend. I have problems setting up the correct relation between objects.
I basically have a class named Post, each post belongs to a user(PFUser), and when fetching a post I want the user info to be fetched along with the post.
#interface Post : PFObject<PFSubclassing>
#property (nonatomic, strong) NSDate *time;
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) NSString *body;
#property (nonatomic, strong, readonly) PFRelation *user;
// User in backed is defined as a relationship to _user
#end
// Saving the post
[post.user addObject:[PFUser currentUser];
[post saveInBackground];
This works fine and relates the post to that user, but when I try to fetch the post later, it doesn't seem like I can get an instance of _user from PFRelation.
What is the correct way to handle this?
Tried to change PFRelation to PFUser but that would crash because it tries to call save on the PFUser object
A Relation is for when you want a long list of related classes, where an array doesn't work, or when you want to query the related objects as needed and not have the list included every time you load the containing object.
Basically you have 4 options with Parse:
Pointer - single reference to another class (1 to 0..1)
Array - collection of pointers, loaded with the object every time (1 to 0..n, small lists)
Relation - collection of pointers, like a join table in SQL (handled under the covers for you), you must run a query against it to load values (1 to 0..n)
Custom join class - really just another object (like many-to-many join in SQL) with a Pointer to each side plus any related information (1..n to 1..n)
In your case a simple Pointer would do what you want.
In your usecase, a pointer is preferable over a PFRelation. You can include the user info by adding includeKey in your query:
[query includeKey:#"user"];
A way to get a comment count on your post is to add every new comment to an array of pointers in your Post.
It is easy to get stuck in the old SQLish ways when you start using NoSQL databases. If a counter is desirable, you could add a cloud code afterSave function on the comment object that updates the "comments" column of the Post class by adding a pointer to the saved comment to the "comments" array in Post. This way, when you fetch a post you can also use
[query includeKey:#"comments"];
which will give you the Post AND all the comments in one query. If you only need the count, you ommit the includeKey, but you still have an array in "comments" with the pointers, so the comment count is the length of the array.
You must create a query from the PFRelation object, like in this code snippet (taken from Parse documentation) and do an explicit query to retrieve the referenced object:
[[relation query] findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
// There was an error
} else {
// user(s)
}
}];
Parse provides the possibility to retrieve also referenced objects in the original query, and this is done using the "includeKey:" method in the original query (that is the query you setup to get the posts), by asking to return the user data and not just the reference. But I'm not sure if it works for PFRelation as in the documentation it is stated that includeKey works for PFObject and PFRelation is not a PFObject. You may try this code in any case and see if it works or not:
[query includeKey:#"user"]
Create A PFSubClass like
yourClassName:PFObject with PFSubclassing
and then in the header file create a Pointer relation
#property(nonatomic, strong) PFUser *userLikeRelation;
add in m file add
+ (NSString *)parseClassName {
return #"parseTableName";
}
+ (void)load {
[self registerSubclass];
}
finally in the View Controller set relation in query when you are save data in parse.
yourClassName *yourClassNameObj = [yourClassName objectWithClassName:[yourClassName parseClassName]];
[yourClassName setUserCommentRelation:[PFUser currentUser]];
for fetching data you can get data with include key
[yourClassNameObj includeKey:#"NameofRelation"];

Delete/Insert versus Update for NSManagedObjects when data should be updated

I'm trying to solve problem which appears during this case:
User can initiate data loading from external source, when data is loaded, it is saved via CoreData. Then it is displayed in some views and some other classes got references to NSManagedObjects.
Data loading can be initiated by other condition (for example, when application resumes from background). New external data is received, dataController deletes previous and creates new data. And here is the problem.
I want to notify all data consumers-classes that they should load new instances (send them references to deleted objects, so they can compare references with ones they own and determine should they ask for new data version or not). But after deletion consumer-class has reference to fault without properties, its ObjectID is useless (because new instance was saved) and I don't know how load its new version.
I can implement some NSManagedObject wrapper:
#interface Model : NSObject
- (id)initWithUniqueId:(id)uniqueId dataObject:(NSManagedObject *)dataObject;
#property (nonatomic, strong, readonly) id uniqueId;
#property (nonatomic, strong, readonly) NSManagedObject *dataObject;
#end
This object can reload itself after dataObject becomes fault. But maybe this approach is wrong and this overhead is not needed? And NSManagedObject should be deleted only if it is really deleted, not updated? And then if object is updated, we can use KVO to handle properties changes, and if object is deleted, we can observe NSManagedObjectContext notifications for changes and look for deleted objects.
I just want to know which way would you prefer and why (maybe you like some other approach)? Thanks in advance.
If you are using an external data source, your own version of some kind of unique ID makes sense.
Then everything becomes simple. You load the data, update the persistent store when you save the context, and send of a notification via the NSNotificationCenter. All listeners can now simply update their data by re-fetching.
(Fetched results controllers that implement the delegate methods do not even have to be notified via the notification center.)

Core data entity disappears when app resumes from background

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.

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