Objective C - NSFetchRequest with category unique - objective-c

I'm using Core Data for my iPhone app.
Initially I have a UITableViewController that just lists all the "Stores".
However, now we realized that list is getting too long, and would like to break it down into 2 layers of UITableViewController. The first one being "States" and the 2nd being the stores in the selected State.
I figured out how to grab all stores in a certain State
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:#"state.abbreviation == 'AA'"]];
How can I using NSFetchRequest, just to grab the list of States? Something like this sql statement maybe? Is it possible?
SELECT DISTINCT abbreviation FROM Stores;
Below is my Model (simplified)
Store.h
#class State;
#interface Store : ManagedAppObject {
#private
}
#property (nonatomic, retain) NSString * title;
#property (nonatomic, retain) State * state;
#end
State.h
#interface State : ManagedAppObject {
#private
}
#property (nonatomic, retain) NSString * abbreviation;
#property (nonatomic, retain) NSString * name;
#end
Thank you,
Tee

To get unique results you'd usually use
fetchRequest.propertiesToFetch = [NSArray arrayWithObject:#"<#propertyName#>"];
fetchRequest.returnsDistinctResults = YES;
fetchRequest.resultType = NSDictionaryResultType;
But unless I'm missing something here, that's probably not what you should do. Why don't you just set your fetchRequest to fetch State entities in the first place? Then use the state.stores relation (which hopefully you have modeled as the inverse of store.state) to get the data for your second 'layer' tableView.

Related

NSPredicate confusion with Nested Arrays of custom objects

I have a custom object, which is in the format:
#interface GroupModel : NSObject
#property (strong, nonatomic) NSString *groupId;
#property (strong, nonatomic) NSArray *children;
#property (strong, nonatomic) NSArray *instruments;
#property (strong, nonatomic) NSString *name;
#end
The instruments array is an array of custom models in the form of:
#interface InstrumentModel : NSObject
#property (strong, nonatomic) NSString *instrumentId;
#property (strong, nonatomic) NSString *name;
#end
And the children array in the GroupModel, is an array of GroupModel objects.
The reason behind this is to create a tree-like structure, so a root group can have multiple child groups, each of them can contain instruments and/or child groups.
Kinda like a folder/file format
Now what I'm trying is to search for a given instrument Id in a given GroupModel.
The instrumentId might be in the given model in the instruments array, or it might be in any children group models instruments array.
What kind of predicate would I need to be using, as I'm confused.
Would this require a subquery ?
Ok, This has worked for me:
//Create an array to add all InstrumentGroup Objects
NSMutableArray * allChildren = [NSMutableArray new];
//add the root group instruments
[allChildren addObjectsFromArray:rootGroup.instruments];
//now add the children's of children Instruments using the unionOfArrays Collection operator
[allChildren addObjectsFromArray:[rootGroup.children valueForKeyPath:#"#unionOfArrays.instruments"]];
//add the children's children instruments
[allChildren addObjectsFromArray:[rootGroup.children valueForKeyPath:#"#unionOfArrays.children.#unionOfArrays.instruments"]];
//filter the Instrument objects
predicate = [NSPredicate predicateWithFormat:#"ANY instruments.instrumentId == %#",selectedInstrumentId];
filtered = [allChildren filteredArrayUsingPredicate:predicate];
if (filtered.count>0) {
//found the instrument!
InstrumentModel *instrument = filtered.firstObject;
}

How to refresh relationships based on _id (none-transient) with objects fetched out of sync

I have the following model:
#interface EquipmentModel : NSManagedObject
#property (nonatomic, retain) NSDate * creationDate;
#property (nonatomic, retain) NSString * desc;
#property (nonatomic, retain) NSNumber * identifier;
#property (nonatomic, retain) NSDate * lastModifiedDate;
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSNumber * manufacturerID;
#property (nonatomic, retain) Manufacturer *manufacturer;
I fetch that object using RESTKit and the following mapping:
RKEntityMapping *equipmentModelMapping = [RKEntityMapping mappingForEntityForName:#"EquipmentModel" inManagedObjectStore:managedObjectStore];
equipmentModelMapping.identificationAttributes = #[#"identifier"];
[equipmentModelMapping addAttributeMappingsFromDictionary:#{
#"id": #"identifier",
#"description": #"desc",
#"manufacturer_id": #"manufacturerID",
#"creation_date": #"creationDate",
#"last_modified_date": #"lastModifiedDate",
}];
[equipmentModelMapping addAttributeMappingsFromArray:#[ #"name"]];
[equipmentModelMapping addConnectionForRelationship:#"manufacturer" connectedBy:#{ #"manufacturerID": #"identifier"}];
However, it is possible that sometimes the manufacturers are not fetched before the models, and hence the relationship "manufacturer" relationship will equal to nil for the model, once the getObjectsAtPath call is completed. Since I made the manufacturerID none-transient, I will still have it store. How can I update the relationship then?
One possibility is to fetch the models once the manufacturers are fetch. However, I have other Objects that are more complex and such approach will not work and not be efficient. How can I link relationship in CoreData based on that ID?!
Thanks a lot!
Your suggested solution is correct, you just want to choose how to predicate your fetches so that only the models that aren't connected are returned. You can also batch the fetches, updates and saves if there are a large number of items to process. There isn't a way to have Core Data or RestKit make the relationship for you in this case.

Using MagicalRecord, how do I get data out of NSArray and into a NSManagedObject?

I am using MagicalRecord to work with Core Data objects. I am having problems with retrieving Core Data objects using the NSArray that MR puts them into... I can't seem to get the data out of the array and into a NSManagedObject so I can work with it. Here is my code:
// create the predicate
NSArray *apptDataArray = [NSMutableArray new];
NSPredicate *predicate = ([NSPredicate predicateWithFormat:#"((aStartTime > %#) AND (aStartTime <= %#))", startDate, endDate]);
// find all appointments with that selected date
apptDataArray = [AppointmentInfo MR_findAllWithPredicate:predicate];
// now find the appointments for the selected date and put them in the schedule
if(apptDataArray.count > 0) {
for (NSManagedObject *AppointmentInfo in apptDataArray) {
NSLog(#"\n\n-->apptDataArray: %#", apptDataArray);
}
}
This is the definition for AppointmentInfo which is in a different class:
#interface AppointmentInfo : NSManagedObject
#property (nonatomic, retain) NSDate * aStartTime;
#property (nonatomic, retain) NSDate * aEndTime;
#property (nonatomic, retain) NSString * aServiceTech;
#property (nonatomic, retain) NSString * aApptKey;
#property (nonatomic, retain) NSDate *aShortDate;
Somehow, I need to get to the data which is in the returned array and place it in AppointmentInfo. I have tried all kinds of permutations, looked in Google and SO, but I couldn't find anything. I'm stumped! How do I do it?
I am not sure if I understand your problem correctly, but
the result apptDataArray of the fetch request is just an array of AppointmentInfo objects, so you can access individual objects with
AppointmentInfo *appt = [apptDataArray objectAtIndex:i]; // 0 <= i < apptDataArray.count
or enumerate all objects of the result array with
for (AppointmentInfo *appt in apptDataArray) {
NSLog(#"startTime=%#, endTime=%#", appt.aStartTime, appt.aEndTime);
}

Unable to refresh UITableView with modified data

I have a iPad app, using XCode 4.5, Storyboards, Core Data and iOS 6. I select a row, make a change to the contents of the record (which is successful), but the row doesn't change. I have tried to refresh the UITableView, but cellForRowAtIndexPath is never called. I have searched SO and Google to no avail; I don't see what's wrong. Can someone please tell me how to fix this? (with an explanation of what I'm doing wrong for the next time?)
Here is the pertinent code:
- (IBAction)btnModify:(UIButton *)sender {
//NSLog(#"btnModify clicked");
NSManagedObjectContext *localContext = [NSManagedObjectContext MR_contextForCurrentThread];
// find client by primary telephone number
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"aClientPrimaryPhone ==[c] %#", cvPrimaryPhone.text];
ClientInfo *clientDataFound = [ClientInfo MR_findFirstWithPredicate:predicate inContext:localContext];
if(clientDataFound) {
clientDataFound.aClientName = cvCustName.text; // now start moving the data
clientDataFound.aClientAddr1 = cvAddress1.text;
clientDataFound.aClientAddr2 = cvAddress2.text;
clientDataFound.aClientCity = cvContactCity.text;
clientDataFound.aClientPostalCode = cvPostalCode.text;
clientDataFound.aClientCellPhone = cvCellPhone.text;
clientDataFound.aClientPrimaryPhone = cvPrimaryPhone.text;
clientDataFound.aClientEMail = cvPersonalEmail.text;
clientDataFound.aClientNotes = cvNotes.text;
[localContext MR_saveNestedContexts];
[self reloadClientList];
}
}
-(void) reloadClientList {
//Init Array to hold TableView Data
tableDataArray = [NSMutableArray new];
[tableDataArray addObjectsFromArray:[ClientInfo findAll]]; // Load
[self.clientList reloadData];
}
and this is ClientInfo.m
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#interface ClientInfo : NSManagedObject
#property (nonatomic, retain) NSString * aClientAddr1;
#property (nonatomic, retain) NSString * aClientAddr2;
#property (nonatomic, retain) NSString * aClientCellPhone;
#property (nonatomic, retain) NSString * aClientCity;
#property (nonatomic, retain) NSString * aClientEMail;
#property (nonatomic, retain) NSData * aClientImage;
#property (nonatomic, retain) NSString * aClientName;
#property (nonatomic, retain) NSString * aClientNotes;
#property (nonatomic, retain) NSString * aClientPostalCode;
#property (nonatomic, retain) NSString * aClientPrimaryPhone;
#end
I found it... my "clientList" was NOT connected to the object... don't know how I missed that one!
There are a few reasons I can think of:
Your table view reference clientList is nil. (not connected)
Your table view's DataSource & Delegate is not set (Actually I'm not sure if it compiles when DataSource is not set)
Your table view is a subclass of UITableView and in that subclass reloadData method is overridden.

NSPredicate for latest item in a collection

I have the following objects in my chat app (with one-to-many relationship)
#interface ChatEntry : NSManagedObject
#property (nonatomic, retain) NSString * text;
#property (nonatomic, retain) NSDate * timestamp;
#property (nonatomic, retain) ChatContext *context;
#end
#interface ChatContext : NSManagedObject
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * userId;
#property (nonatomic, retain) NSSet *entries;
#end
Each conversation (ChatContext) groups all messages send by this user (uniquely identified by userId field).
I'm trying to display a list of conversations which displays the last message of each conversation (similar to iMessage).
I'm using the following code:
NSFetchRequest* request=[[NSFetchRequest alloc] initWithEntityName:#"ChatEntry"];
request.predicate=[NSPredicate predicateWithFormat:#"timestamp=context.entries.#max.timestamp"];
request.sortDescriptors=[NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"timestamp" ascending:NO]];
m_chat=[[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:m_context sectionNameKeyPath:nil cacheName:#"chat"];
m_chat.delegate=self;
This works fine until I receive a new message (with NSFetchedResultsChangeInsert)
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
...
}
At that point, I get ANOTHER entry in my table view. The fetch controller keeps the previous entry (even though its timestamp is not the latest one anymore). I'm not sure how to solve this and force the controller to always evaluate the predicate.
Alternatively, I've tried to change my query to fetch ChatContext instances, but then I'm not sure how to actually read the latest entry in that context.
OK, I've managed to solve that. What I did is to add a "lastMessage" object to the context, which I keep updating on every new message. I then query the list of contexts, sorted by reversed lastMessage.timestamp. New messages update the lastMessage property in their context - which triggers an event and update the UI automatically.