views:

67

answers:

2

I am building a social app that has a chatView. Message is an entity. On the usersViewController (rootViewController), I want to just display the number of unread messages from each user. But on the chatView, I want to show the messages sent between me and the selected user.

How should I fetch the messages from Core Data?

  1. Is there a way to fetch them all into an NSMutableDictionary with userIds as keys, each pointing to an NSMutableArray of Message entities? Or, would I have to fetch all the Messages and then iterate through them with Objective-C to arrange them as described?

  2. Either way, should I load them all upfront in the appDelegate? Or, should I only load the messages for the specific conversation upon viewing the corresponding chatView?

  3. To get the unread counts, should I iterate through the fetched Message entities to make an NSMutableDictionary of userID keys pointing to count values? Or, should I make a predicate that acts like a SQL sum query grouped by userId (Is that even possible with Core Data?)?

  4. Should I create a model, call it UnreadMessageCount, that stores the number of unread messages that I have from each user and a reference to the User model? I want to have all the unread messages on the first page of the app, so this would make that query faster. But receiving messages would be slower because I would not only have to add it to the Message model but also I would have to increment the count for that user in the UnreadMessageCount model. Also, the data would sort of be duplicated. Also, I'll only make the query for all message counts once per application launch, but i'll make queries to update the unread message count every time I receive a new message and am not on the chatView page with that user.

Please fill me in on iPhone memory management & Core Data possibilites and performance. And also please give your opinion on the best way to engineer this.

Thanks!

Matt

+3  A: 

To get the message count, I would ask the managed object context for the count of messages given a certain fetch request, for example, to count the messages for a certain user:

NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];

NSEntityDescription *entity = [NSEntityDescription entityForName:@"Message" inManagedObjectContext:context];
[request setEntity:entity];

NSPredicate *predicate = [NSPredicate predicateWithFormat:@"user == %@", user];
[request setPredicate:predicate];

NSInteger count = [context countForFetchRequest:request error:nil];

Here I'm assuming that the Message entity has a user relationship.

To get the actual messages for that same user, I would just replace the last line for:

NSArray *messages = [context executeFetchRequest:request error:nil];
Martin Cote
I like this solution as it avoids loading the messages into memory. Depending on the number of users this should perform quite nicely. Although setting the `error:` to `nil` even in an example is discouraged, people like to copy and paste too much :)
Marcus S. Zarra
I usually leave proper error handling as an exercise to the reader ;) But you are right, people do copy/paste too much.
Martin Cote
This is great. Thanks for including the source code. What if I wanted to get the unread message counts from every user that I've loaded on the usersView? Would I loop this code for each user or is there a way to get it with one execution of a fetch request, like a sum, groupby user sql query where message.unread = 1?
MattDiPasquale
+1 Concisely put.
TechZen
I would quibble that putting autorelease on same line as init is bad practice. You can get away with it 99% of the time but when it bites you it will be almost impossible to track down. `Autorelease` should be used like release i.e. you should only send it when you are done with the object.
TechZen
@MattDiPasquale You can adjust the NSPredicate for the NSFetchRequest accordingly, for example @"user == %@ AND unread == YES" and do a count on that request.
Martin Cote
A: 

iPhone generally does a great job managing resources when you use CoreData (especially simple structures as in your example). So to answer your questions, this is what I think you should do:

  1. You will need fetch all users, then iterate through them and create dictionary keys that will point at messages property of each of the user entities
  2. You shouldn't, use NSFetchedResultsController instead, it efficiently loads entities that are requested and does all the required prefetching for optimized display in table views
  3. the answer above is probably the way to go when it comes to counts. Creating proper indices on your entities will make it lightning fast.
Nick
Cool, Thanks! To clarify #2, I should not load them all upfront in the appDelegate but instead request only the messages with a specific user upon loading the chatView for that user? If so, then I wont need to do #1 anyway. If I want to display the unread counts for all users on the usersView, then would I loop @Martin's code for each user? Or could I do that in one request?
MattDiPasquale
oh heh, yeah you don't need #1, I thought it was an unrelated question. And yes, there's a way to do it in one request, just remove the "user = %@" predicate and add something that checks the message flag, depending on your data model could be `"isUnread = 1"` or smth like that
Nick