CoreData and Entity Relationship Issue - objective-c

In my core data model I have two entities:
InventoryItem
InventoryAction
InventoryItem has the following attributes:
Attributes:
#property (nonatomic, retain) NSNumber * allowsActions;
#property (nonatomic, retain) NSNumber * assetID;
#property (nonatomic, retain) NSNumber * inventoryObjectID;
#property (nonatomic, retain) NSString * objectDescription;
#property (nonatomic, retain) NSNumber * quantity;
#property (nonatomic, retain) NSNumber * retired;
#property (nonatomic, retain) NSString * serialNumber;
Relationships:
#property (nonatomic, retain) NSSet *action;
InventoryAction has the following attributes
Attributes:
#property (nonatomic, retain) NSDate * actionDate;
#property (nonatomic, retain) NSNumber * actionID;
#property (nonatomic, retain) NSString * actionLongValue;
#property (nonatomic, retain) NSString * actionShortValue;
#property (nonatomic, retain) NSString * notes;
#property (nonatomic, retain) NSNumber * userActionID;
#property (nonatomic, retain) NSString * userAuthorizingAction;
#property (nonatomic, retain) NSString * userPerformingAction;
#property (nonatomic, retain) NSNumber * userPerformingActionExt;
#property (nonatomic, retain) NSNumber * inventoryObjectID;
Relationships
#property (nonatomic, retain) InventoryItem *object;
Now that you have all that information I will tell you that all of the values that are being stored in the CoreData model are coming from a web service in JSON format. All of the values are storing properly.
The problem that I'm having is that when I fetch the data from CoreData it only brings back values that are unique.
For instance:
The last InventoryItem has 6 actions associated with it:
"",
"",
"it looks kinda slimy",
"oh god....why would you do that to an inanimate object you sicko",
"its all slimy now",
"everything looks good"
As 1 and 2 have the same value it only retrieves value 1, 3, 4, 5, and 6.
Here is the code I'm using to fetch the data:
- (void)loadDetails
{
_fetchRequest = [[NSFetchRequest alloc] init];
_entity = [NSEntityDescription entityForName:#"InventoryObject" inManagedObjectContext:[self managedObjectContext]];
_sort = [NSSortDescriptor sortDescriptorWithKey:#"inventoryObjectID" ascending:YES];
_sortDescriptors = [[NSArray alloc]initWithObjects:_sort, nil];
[_fetchRequest setEntity:_entity];
[_fetchRequest setSortDescriptors:_sortDescriptors];
NSError *error;
_fetchedObjects = [[self managedObjectContext] executeFetchRequest:_fetchRequest error:&error];
for (InventoryItem *inventoryItem in _fetchedObjects) {
NSLog(#"Object Description: %#", [inventoryItem valueForKey:#"objectDescription"]);
NSLog(#"Object ID: %#", [inventoryItem valueForKey:#"inventoryObjectID"]);
InventoryAction *action = (InventoryAction *)inventoryItem.action;
NSLog(#"Action Long Value: %#", [action valueForKey:#"actionLongValue"]);
NSLog(#"Notes: %#", [action valueForKey:#"notes"]);
}
}
Here is the JSON return:
[{"MediaInventoryObjectsId":1,"AssetId":15,"Quantity":1,"SerialNumber":"R45DFL5","Description":"Test Camera 1","AllowActions":true,"Retired":false,"Actions":[{"MediaInventoryActionsId":3,"MediaInventoryObjectsId":1,"UserPerformingActionExt":4444,"UserActionId":1,"ActionDate":"2014-05-19T15:31:45.6","UserPerformingAction":"myersb","UserAuthorizingAction":"mccroskeyl","Notes":null,"ActionShortValue":"OUT","ActionLongValue":"Check Out"},{"MediaInventoryActionsId":4,"MediaInventoryObjectsId":1,"UserPerformingActionExt":4444,"UserActionId":2,"ActionDate":"2014-05-19T15:31:45.6","UserPerformingAction":"myersb","UserAuthorizingAction":"mccroskeyl","Notes":"everything looks good","ActionShortValue":"IN","ActionLongValue":"Check In"}]},{"MediaInventoryObjectsId":2,"AssetId":15,"Quantity":2,"SerialNumber":"IDKMYBFFJILL","Description":"Vanilla Ice Cream","AllowActions":true,"Retired":false,"Actions":[]},{"MediaInventoryObjectsId":3,"AssetId":15,"Quantity":1,"SerialNumber":"R2D23P0","Description":"Droid Bee Box","AllowActions":true,"Retired":false,"Actions":[{"MediaInventoryActionsId":5,"MediaInventoryObjectsId":3,"UserPerformingActionExt":4444,"UserActionId":1,"ActionDate":"2014-05-20T14:48:29.53","UserPerformingAction":"myersb","UserAuthorizingAction":"mccroskeyl","Notes":null,"ActionShortValue":"OUT","ActionLongValue":"Check Out"},{"MediaInventoryActionsId":6,"MediaInventoryObjectsId":3,"UserPerformingActionExt":4444,"UserActionId":2,"ActionDate":"2014-05-20T14:48:29.58","UserPerformingAction":"myersb","UserAuthorizingAction":"mccroskeyl","Notes":"everything looks good","ActionShortValue":"IN","ActionLongValue":"Check In"},{"MediaInventoryActionsId":7,"MediaInventoryObjectsId":3,"UserPerformingActionExt":5555,"UserActionId":1,"ActionDate":"2014-05-20T14:48:29.6","UserPerformingAction":"farmer","UserAuthorizingAction":"mccroskeyl","Notes":null,"ActionShortValue":"OUT","ActionLongValue":"Check Out"},{"MediaInventoryActionsId":8,"MediaInventoryObjectsId":3,"UserPerformingActionExt":5555,"UserActionId":2,"ActionDate":"2014-05-20T14:48:29.6","UserPerformingAction":"farmer","UserAuthorizingAction":"mccroskeyl","Notes":"its all slimy now","ActionShortValue":"IN","ActionLongValue":"Check In"},{"MediaInventoryActionsId":9,"MediaInventoryObjectsId":3,"UserPerformingActionExt":6666,"UserActionId":1,"ActionDate":"2014-05-20T14:48:29.61","UserPerformingAction":"intern1","UserAuthorizingAction":"mccroskeyl","Notes":"it looks kinda slimy","ActionShortValue":"OUT","ActionLongValue":"Check Out"},{"MediaInventoryActionsId":10,"MediaInventoryObjectsId":3,"UserPerformingActionExt":6666,"UserActionId":2,"ActionDate":"2014-05-20T14:48:29.62","UserPerformingAction":"intern1","UserAuthorizingAction":"mccroskeyl","Notes":"oh god....why would you do that to an inanimate object you sicko","ActionShortValue":"IN","ActionLongValue":"Check In"}]}]
I read somewhere that NSSet possibly only brings back unique records. Is this true and if so what would be the solution as the relationship: action is an NSSet?

Your relationship is declared like this:
#property (nonatomic, retain) NSSet *action;
But you access it like this:
InventoryAction *action = (InventoryAction *)inventoryItem.action;
That's incorrect. The action relationship is an NSSet containing instances of InventoryAction, it's not an InventoryAction itself. Even though you assign this to an InventoryAction *, what you actually have is an NSSet. Then you do this:
NSLog(#"Notes: %#", [action valueForKey:#"notes"]);
If you call valueForKey: on an NSSet, it returns all unique values of that key for objects in the set. In this case it returns all unique values of the notes attribute on the InventoryAction objects in the set, which is what you're seeing. If you want to find every instance instead of every unique value, you need something like:
NSSet *actions = inventoryItem.action;
for (InventoryAction *action in actions) {
NSLog(#"Action note: %#", action.note);
}
[Warning, the above is just typed into my web browser....]

Related

Subquery between two objects

I have two different models Department and employee
#interface Department : RLMObject
#property NSString *name;
#property (nonatomic, strong) RLMArray<Employee> *employee;
#end
#interface Employee : RLMObject
#property NSString *department;
#property NSString *email;
#property NSString *firstname;
#property NSString *lastname;
#property NSString *fullname;
#property NSString *imgUrl;
#property NSString *imgWall;
#property NSString *nickname;
#end
I want to search which department of employee has contains "a" in their first name and last name. please help. thanks.
What you'd like to do is as follows:
RLMResults *r = [Department objectsWhere:
#"SUBQUERY(employee, $e, $e.firstname CONTAINS 'a' AND $e.lastname CONTAINS 'a').#count > 0"];

Objective C Acessing NSMutableDictionay inside NSMutableDictionary

I am a newcomer to objective C and I have serious problems in accessing NSMutableDictionarys.
I have two objects (Network and Beacon), and I want to create a NSMutableDictionary of Networks with a NSMutableDictionary of Beacons inside.
Network.h
#import <Foundation/Foundation.h>
#interface Network : NSObject{
NSString *id_network;
NSString *major;
NSString *active;
NSString *name;
NSString *status;
NSMutableDictionary *beaconsDictionary;
}
#property (nonatomic, strong) NSString *id_network;
#property (nonatomic, strong) NSString *major;
#property (nonatomic, strong) NSString *active;
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *status;
#property (nonatomic, strong) NSMutableDictionary *beaconsDictionary;
#end
Beacon.h
#import <Foundation/Foundation.h>
#interface Beacon : NSObject{
NSString *id_beacon;
NSString *major;
NSString *minor;
NSString *active;
NSString *detected;
}
#property (nonatomic, strong) NSString *id_beacon;
#property (nonatomic, strong) NSString *major;
#property (nonatomic, strong) NSString *minor;
#property (nonatomic, strong) NSString *active;
#property (nonatomic, strong) NSString *detected;
#end
I can create the NSMutableDictionary like this:
Beacon *beacon = [[Beacon alloc]init];
beacon.id_beacon=#"1";
beacon.major=#"1";
beacon.minor=#"1";
beacon.active=#"1";
beacon.detected=#"0";
NSMutableDictionary *beaconDic = [[NSMutableDictionary alloc]init];
[beaconDic setObject:beacon forKey:beacon.id_beacon];
Network *net = [[Network alloc]init];
net.id_network=#"1";
net.major=#"1";
net.active=#"1";
net.name=#"network 1";
net.status=#"1";
net.beaconsDictionary=beaconDic;
NSMutableDictionary *networkDic = [[NSMutableDictionary alloc]init];
[networkDic setObject:net forKey:net.id_network];
Ok, but now how can i access to beacon property "detected" directly and modify it?
I know this this is a very bad example, but I have no idea how to do it.
It looks like you'll have to have a network id and a beacon id to get where you need to go. It would looks something like:
Network *net = networkDic[netId];
Beacon *beacon = net.beaconsDictionary[beaconId];
beacon.detected = newDetectedValue;
This is for arbitrary network ids and beacon ids. You can hardcode values if you wish.
Edit:
It's worth noting in your example code that you can use the more modern dictionary assignment. Rather than [dictionary setValue:value forKey:key];, you can do dictionary[key] = value;. It's, of course, personal preference but you're very likely to see the latter in more recent things and I find it to be clearer.
You can get your Network and Beacon objects back by providing keys that match their keys in the dictionary:
NSString *nwKey = #"1";
Network *n = networkDic[nwKey];
NSDictionary *bDict = n.beaconsDictionary;
NSString *bnKey = #"1";
Beacon *b = bDict[bnKey];
Note: This is the new syntax. Here is the old one:
NSString *nwKey = #"1";
Network *n = [networkDic objectForKey:nwKey];
NSDictionary *bDict = n.beaconsDictionary;
NSString *bnKey = #"1";
Beacon *b = [bDict objectForKey:bnKey];

Creating Dictionary from a model that contains nil values

I have the following Model:
#interface Person : NSObject
#property (nonatomic, copy) NSString *firstName;
#property (nonatomic, copy) NSString *middleName;
#property (nonatomic, copy) NSString *lastName;
#property (nonatomic, copy) NSString *status;
#property (nonatomic, copy) NSString *favoriteMeal;
#property (nonatomic, copy) NSString *favoriteDrink;
#property (nonatomic, copy) NSString *favoriteShow;
#property (nonatomic, copy) NSString *favoriteMovie;
#property (nonatomic, copy) NSString *favoriteSport;
-(NSDictionary *)getSomeInfo;
-(NSDictionary *)getAllInfo;
#end
Part 1:
I want getSomeInfo to return NSDictionary (e.g. {"firstName", self.firstName}) for all the fields that does not contain nil. How can I do that? (I could check every value but I wonder if there's a better way)
Part 2:
I want getAllInfo to return NSDictionary with all the property and if one contains nil then it should throw an error. Again do I have to write a long conditional statement to check or is there a better way?
Note: I want to do this without using external library. I'm new to the language so I'm open to suggestions if there's a better pattern in Objective-C.
There are two approaches.
1) Check each value:
- (NSDictionary *)getSomeInfo {
NSMutableDictionary *res = [NSMutableDictionary dictionary];
if (self.firstName.length) {
res[#"firstName"] = self.firstName;
}
if (self.middleName.length) {
res[#"middleName"] = self.middleName;
}
// Repeat for all of the properties
return res;
}
2) Use KVC (Key-value coding):
- (NSDictionary *)getSomeInfo {
NSMutableDictionary *res = [NSMutableDictionary dictionary];
NSArray *properties = #[ #"firstName", #"middleName", #"lastName", ... ]; // list all of the properties
for (NSString *property in properties) {
NSString *value = [self valueForKey:property];
if (value.length) {
res[property] = value;
}
}
return res;
}
For the getAllInfo method you can do the same but instead return nil if any value is missing. Treat the nil results as your indication that not all properties have a value.

Store scalar values (NSInteger, BOOL, double) in CoreData without converting to NSNumber

I have read a lot of articles where they say to explicitely convert from and to NSNumber when I want to store scalars in CoreData:
#property (nonatomic, assign) NSInteger value;
- (NSInteger)value
{
return [value integerValue];
}
- (void)setValue:(NSInteger)val
{
value = [NSNumber numberWithInteger:val];
}
But in our old project we have a bunch of properties where we doesn't do those manipulations (and they don't have custom accessors)! Why it works?
Example code
Declaration. Scalar values are not transient.
#interface ProductProperty : NSManagedObject
#property (nonatomic, strong, readonly) NSString * propertyID;
#property (nonatomic, strong, readonly) NSString * title;
#property (nonatomic, strong, readonly) NSSet * values;
#property (nonatomic, assign, readonly) BOOL filter;
#property (nonatomic, strong, readonly) NSDate *update;
#property (nonatomic, strong, readonly) NSNumber *index;
#property (nonatomic, assign, readonly) BOOL system;
#end
#import "ProductProperty.h"
#implementation ProductProperty
#dynamic propertyID;
#dynamic title;
#dynamic values;
#dynamic filter;
#dynamic update;
#dynamic index;
#dynamic system;
#end
Mapping into objects. Called if received JSON differs from existing. Otherwise it fetches from the CoreData storage.
- (void)updateProperties:(NSArray*)properties
{
for (NSDictionary *_property in properties) {
NSString *propertyID = [_property objectForKey:#"id"];
ProductProperty *property = [state.productPropertiesWithIDs objectForKey:propertyID];
if (!property) {
property = [state.ctx newObjectWithEntityName:ProductProperty.entityName];
property.propertyID = propertyID;
[state.productPropertiesWithIDs setObject:property forKey:propertyID];
}
property.update = state.update;
property.title = [_property objectForKey:#"title"];
property.filter = [_property objectForKey:#"filter"] ? [[_property objectForKey:#"filter"] boolValue] : YES;
property.index = [propertyIndexes objectForKey:propertyID] ? [propertyIndexes objectForKey:propertyID] : [NSNumber numberWithInt:propertyIndex++];
property.system = [SYSTEM_PROPERTY_IDS containsObject:propertyID] ? YES : NO;
[self updatePropertyValues:[_property objectForKey:#"values"] forProperty:property];
}
}
- (ProductProperty*)productPropertyWithID:(NSString*)propertyId error:(NSError**)error
{
NSFetchRequest *req = [ProductProperty request];
req.predicate = [NSPredicate predicateWithFormat:#"propertyID == %#", propertyId];
return [[ctx executeFetchRequest:req error:error] lastObject];
}
The answer is that since iOS 5 CoreData support auto generating accessors for scalars so I don't need to implement them manually.

Does this copy class method leak memory?

- (id)copyWithZone:(NSZone *)zone {
PoolFacility *copy = [[[self class] allocWithZone:zone]init];
copy.name = [self.name copy];
copy.type = [self.type copy];
copy.phoneNumber = [self.phoneNumber copy];
//make sure I get proper copies of my dictionaries
copy.address = [self.address mutableCopy];
copy.webAddress = [self.webAddress copy];
copy.prices = [self.prices mutableCopy];
copy.pools = [self.pools mutableCopy];
return copy;
}
Can anyone see any memory leaks?
Here's the property types:
NSString *name;
NSString *type;
NSMutableDictionary *address;
NSString *phoneNumber;
NSString *webAddress;
NSMutableArray *prices;
NSMutableArray *pools;
Here are the property declarations:
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *type;
#property (nonatomic, copy) NSString *phoneNumber;
#property (nonatomic, retain) NSMutableDictionary *address;
#property (nonatomic, copy) NSString *webAddress;
#property (nonatomic, retain) NSMutableArray *prices;
#property (nonatomic, retain) NSMutableArray *pools;
The properties defined as copy and not retain will have an extra copy when set as below (your code)
copy.name = [self.name copy];
copy.type = [self.type copy];
copy.phoneNumber = [self.phoneNumber copy];
copy.webAddress = [self.webAddress copy];
it should be sufficient to only write them as
copy.name = self.name;
copy.type = self.type;
copy.phoneNumber = self.phoneNumber;
copy.webAddress = self.webAddress;
This almost certainly leaks like a sieve. You need to provide your #property and other method declarations for us to recommend the best way to fix it.