How can I get the NSManagedObjectID of an object directly after saving?
I've tried using the NSNotification NSManagedObjectContextDidSaveNotification and getting the updated/inserted values and getting the object id from one (the only) managed object, but it's giving me "Unrecognized Selector" when I try to grab the object id.
Can I even get the Object Id right after I save?
- (void)handleDidSaveNotification:(NSNotification *)note
{
NSDictionary *dict = [note userInfo];
NSDictionary *updatedDict = [dict valueForKey:#"updated"];
NSLog(#"Notification: %#", dict);
NSLog(#"Updated Info: %#", updatedDict);
NSManagedObject *core = [updatedDict valueForKey:#"entity"];
NSManagedObjectID *objectId = [core objectID];
}
You are trying to set a dictionary (updatedDict) when the returned data is a NSSet.
you might simply need to get it from the set collection it is in ...
NSSet* s = [dict valueForKey:#"updated"];
[s valueForKey:#"objectID"]
This will return a set of NSManagedObjectIDs.
See NSSet on how to access objects.
Related
I just learned how to make use of KVO, but only the basics. What I need to achieve is something like this:
I have a delegate call that passes a Speaker object.
- (void)onSpeakerFound:(Speaker *)speaker
Once I receive this Speaker in the UI part, from there I will assign observers for this object.
But, this is just for one speaker. What if I have multiple speakers to keep track of. I need to assign observers separately for those speakers and then at the same time I wish to keep their references for further updates to the values.
Each speaker could be updated from time to time. So when I notice that there is a change that happened on a speaker, I wish to access the reference to that speaker and update the values just like how NSMutableDictionary works.
NSMutableDictionary makes a copy of an object set to it so it will be a difference object if I get it again from the dictionary.
So, is there a class that allows me to keep track of an object by just keeping a reference only to that object without making a copy of it?
EDIT: A Test Made To Verify That When An Instantiated Object is Set in an NSMutableDictionary, The Instantiated Object is not referenced with the one set inside NSMutableDictionary.
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
NSString *obj = #"initial value";
NSString *key = #"key";
[dict setObject:obj forKey:key];
NSLog(#"Object is now %#", [dict objectForKey:key]);
obj = #"changed value";
NSLog(#"Object is now %#", [dict objectForKey:key]);
}
Log:
2016-07-26 21:04:58.759 AutoLayoutTest[49723:2144268] Object is now initial value
2016-07-26 21:04:58.761 AutoLayoutTest[49723:2144268] Object is now initial value
NSMutableDictionary makes a copy of an object set to it...
That is not correct; it will add a reference to the object. It will be the same object referenced inside and outside the Objective-C collection.
So, is there a class that allows me to keep track of an object...?
Probably NSMutableSet if you just want a list of the objects. That will take care that you have a unique reference to each object, however you need to implement the methods hash and isEqual on those objects so they behave correctly. Otherwise NSMutableDictionary if you want fast look-up by key.
-try this one
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
NSString *obj = #"initial value";
NSString *key = #"key";
[dict setObject:obj forKey:key];
NSLog(#"Object is now %#", [dict objectForKey:key]);
obj = #"changed value";
[dict setObject:obj forKey:Key];
NSLog(#"Object is now %#", [dict objectForKey:key]);
}
I have some parsing code I'm using for serialising and deserialising objects from our web service and I've hit a bit of a problem when serialising booleans.
The serialisation looks like this:
- (NSDictionary *)dictionaryRepresentationWithMapping:(NSDictionary *)mappingDictionary
{
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc]init];
for (id key in[mappingDictionary allKeys])
{
id value = [self valueForKey:key];
if ((value != [NSNull null]) && (![value isKindOfClass:[NSNull class]]) && (value != nil))
{
[dictionary setObject:value forKey:mappingDictionary[key]];
}
}
return [NSDictionary dictionaryWithDictionary:dictionary];
}
The problem is that when I call valueForKey: on my NSManagedObject and then add this to my dictionary I end up with the value being set as if I was calling:
[dictionary setObject:#1 forKey:mappingDictionary[key]];
instead of:
[dictionary setObject:#YES forKey:mappingDictionary[key]];
This means that when I turn this into JSON, in the next stage, I'm sending 1 instead of true to the server.
So what I need is a way of retaining the fact that this is an NSNumber representing a bool as opposed to a number. I've tried asking for the class but I just get back NSNumber. Is there a way I can retain this automatically or failing that, is there a way I can consult the model to see what the attribute type was set to?
Each entity has its metadata stored in NSEntityDescription and NSAttributeDescription. You can access them from NSManagedObject in a following way:
//you can put this inside the for loop
NSAttributeDescription *attributeDescription = self.entity.attributesByName[key];
if(attributeDescription.attributeType == NSBooleanAttributeType) {
//it is a boolean attribute
}
When sending a call to the server, you could do like this:
[dict setValue:[NSNumber numberWithBool:YES] forKey:mappingDictionary[key]]; ;
Or another way, you can model server side to retain its value as Boolean, and at that time, just need to send like this [dict setValue:YES] forKey:mappingDictionary[key]];
Hope it could help
In my iOS application, I am doing a heavy weight migration of an Entity, where I convert the type of an attribute from Integer64 in the old data model to a type of String in the new data model. The conversion of this attribute appears to be working fine. However, the problem I have run into is that another attribute of the same Entity is null (which is what it is supposed to be), and when this attribute is being migrated to the same entity in the new schema, an error is being flagged:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for attribute: property = "date"; desired type = NSDate; given type = NSNull; value = <null>.'
I am not sure why this error is being flagged, because the attribute is marked as optional in the data model. I would like to simply migrate the nil value of the attribute "as is" to the same attribute in the new data model without any changes or modifications.
Here is the relevant code of my subclass of NSEntityMigrationPolicy that I am using:
- (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)sInstance entityMapping:(NSEntityMapping *)mapping manager:(NSMigrationManager *)manager error:(NSError *__autoreleasing *)error {
NSManagedObject *newObject;
NSEntityDescription *sourceInstanceEntity = [sInstance entity];
//correct entity? just to be sure
if ([[sourceInstanceEntity name] isEqualToString:#"MyEntity"]) {
newObject = [NSEntityDescription insertNewObjectForEntityForName:#"MyEntity" inManagedObjectContext:[manager destinationContext]];
//obtain the attributes
NSDictionary *keyValDict = [sInstance committedValuesForKeys:nil];
NSArray *allKeys = [[[sInstance entity] attributesByName] allKeys];
//loop over the attributes
for (NSString *key in allKeys) {
//get key and value
id value = [keyValDict objectForKey:key];
if ([key isEqualToString:#"integerType"]) {
//here retrieve old value
NSNumber *oldValue = [keyValDict objectForKey:key];
//here do conversion as needed
NSString *stringType = [oldValue stringValue];
//then store new value
[newObject setValue:stringType forKey:key];
} else { //no need to modify the value, Copy it across -- this is where I believe the problem is
[newObject setValue:value forKey:key];
}
}
[manager associateSourceInstance:sInstance withDestinationInstance:newObject forEntityMapping:mapping];
}
return YES;
}
Can anyone see what it is I'm doing wrong?
The issue is that you are getting back a NSNull class in your dictionary which means you are trying to pass the wrong type of class to the new NSManagedObject instance.
If you read the documentation on -committedValuesForKeys: you will see that:
nil values are represented by an instance of NSNull.
Which is your problem.
Personally I would not approach the values this way. Instead I would do something like:
NSDictionary *allAttributes = [[sInstance entity] attributesByName];
for (NString *key in allAttributes) {
id value = [sInstance valueForKey:key];
if ([key isEqualToString:#"integerType"]) {
//here retrieve old value
NSNumber *oldValue = [keyValDict objectForKey:key];
//here do conversion as needed
NSString *stringType = [oldValue stringValue];
//then store new value
[newObject setValue:stringType forKey:key];
} else { //no need to modify the value, Copy it across -- this is where I believe the problem is
[newObject setValue:value forKey:key];
}
}
Whereby you are grabbing the values directly from the object and you will get a proper nil back.
I want to get the inserted and the update objects from NSPersistentStoreDidImportUbiquitousChangesNotification to do some check on them.
Objects can be of two kind of classes: "Alpha" and "Beta". Both classes have the
property (nonatomic, retain) NSString* name
which is the one I should check.
How do I get it?
The following code doesn't work because it says "name" is an unknown selector:
-(void) checkObjects
{
NSDictionary *insertedObjects = [[note userInfo] objectForKey: #"inserted"];
NSDictionary *updatedObjects = [[note userInfo] objectForKey: #"updated"];
for(NSManagedObject *obj in insertedObjects){
if([obj.entity.managedObjectClassName isEqualToString:#"Alpha"]){
Alpha *alpha = (Alpha*) obj;
if (alpha.name isEqualToString:#"xyz"){
//Do some check
}
}else if([obj.entity.managedObjectClassName isEqualToString:#"Beta"]){
Beta *beta = (Beta*) obj;
if (beta.name isEqualToString:#"xyz"){
//Do some check
}
}
}
}
If I change:
Alpha *alpha = (Alpha*) obj;
Beta *beta = (Beta*) obj;
To:
Alpha *alpha = (Alpha*) obj.entity;
Beta *beta = (Beta*) obj.entity;
alpha = Alpha <-- It is the name of the class, not of the object I want!
beta = Beta <--- It is the name of the class, not of the object I want!
When you get NSPersistentStoreDidImportUbiquitousContentChangesNotification, the objects in userInfo are not managed objects, they're managed object IDs. That is, instances of NSManagedObjectID. If you want to look up attributes on the managed object, you need to get the object corresponding to the ID. Something like
NSDictionary *insertedObjectIDs = [[note userInfo] objectForKey:NSInsertedObjectsKey];
for(NSManagedObjectID *objID in insertedObjects) {
NSError *error = nil;
NSManagedObject *obj = [self.managedObjectContext existingObjectWithID:objID error:&error];
....continue...
}
You may need to change that if self doesn't have a managed object context.
Also, on a slight tangent-- it's generally better to use NSInsertedObjectsKey instead of #"inserted" and NSUpdatedObjectsKey instead of #"updated". Apple probably won't change the key names, but they could, so using the key names instead of string literals is a better choice.
I'm looking for a way to track the attribute change of an NSManagedObject.
Currently I use a NSNotifactionCenter to see the changes of my managedobjectcontext:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleDataModelChange:) name:NSManagedObjectContextObjectsDidChangeNotification object:self.managedObjectContext];
It fires the handleDataModelChange Methode which looks like this:
- (void)handleDataModelChange:(NSNotification *)note
{
NSSet *updatedObjects = [[note userInfo] objectForKey:NSUpdatedObjectsKey];
if (updatedObjects.count > 0) {
for (NSManagedObject *obj in updatedObjects.allObjects) {
NSLog(#"Object updated: %# with values:",obj.entity.name);
NSDictionary *theAttributes = [self getAllAttributesOf:obj];
for (NSString *attributeName in theAttributes) {
NSLog(#"Name: %# : %#",attributeName,[obj valueForKey:attributeName]);
}
}
}
}
This logs the new attributes of the object if it changed. How can I achieve a way to get the old attribute values as well?
From the NSManagedObject Class Reference:
changedValues
Returns a dictionary containing the keys and (new) values of persistent properties that have been changed since last fetching or saving the receiver.
changedValuesForCurrentEvent
Returns a dictionary containing the keys and old values of persistent properties that have changed since the last posting of NSManagedObjectContextObjectsDidChangeNotification.