iOS -- updating data structures in new app version - ios

I am not using core data. Just to keep it simple, let's say I have data which were formerly of type NSString, and now they are supposed to be objects of a custom class Person whose only ivar is a "name" ivar of type NSString. In the updated version of the app, I want my Person objects to have their "name" set to whatever the NSString was in the saved data. But suppose my people appear in lots of different places in the app, so telling it how to handle each one individually would be a pain.
What is the best way to handle this? In particular, is there some trick I can do to catch it in the un-archiving process? Or do I have to go through every un-archived object and turn the appropriate NSStrings into Person objects?

You can create a utility class that check the value that come back from your unarchiver, run it through this method. If the value is an NSString, then you can construct a new Person object, if not, then just returns that object back.
+ (Person *)personFromStringOrPersonObject:(id)object {
if ([object isKindOfClass:[NSString class]]) {
// Construct your Person object
Person person = [Person new];
person.name = object;
return person;
} else {
return object;
}
}

Related

containsObject is returning NO after the app is restarted

I have a UITabelView displaying a list of items(returned from the server), users will be able to select any item and add it to his own section.
Upon selecting an item, I am retrieving the selected item details and saving it to a pList file, the list will then be displaying a small icon informing the user that the item is added to his section. User will be able to remove it from his section by tapping the icon again.
To know if the item is already in his section, i am using
[self.myItemsArray containsObject:item]
Everything is working perfect if the user don't exit the application. The issue is occurring once the app is restarted. When I retrieve the tableview list from the database or from server, all the items will not be shown as on my list and [self.myItemsArray containsObject:item] will return NO for the previous added items.
I was searching for an alternative way, by creating an NSArray of my items id's and will then check if the new array will contains the item id to display the icon.
the new issue is that the id is returned as double from the server and stored as it is. The application is crashing when creating the array:
[self.myItemsArray valueForKey:#"Id"]
myItemsArray is an array of items(item is an NSDictionary containing all the details)
So i am desperate now,could anyone help me by resolving any of the above issues?
I prefer the first one, since containsObject will be more simple, but I don't mind to solve it with the second choice if the first will not work.
You'll need to make sure you have a good isEqual: method defined for your custom class, since that's what NSArray uses to determine containment: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSArray_Class/#//apple_ref/occ/instm/NSArray/containsObject:
Let's say this class has something like a 'key' or 'id' value that is unique for each instance of the class (which in your case is a double type). This will not always be the case, of course; it's often an aggregate of individual properties/ivars that constitutes a unique 'key', but for the purposes of this discussion let's say that such a field exists. Your code might look something like this (all in the .m):
static double const EPSILON = .000000001; // You'll need to be the judge of how "close" values can be to each other but still be distinct
#interface MyObjectClass: NSObject
#property (nonatomic, readonly) double uniqueKey;
#end
#implementation MyObjectClass
// ...
- (BOOL)isEqual:(id)object
{
if (self == object) { return YES; }
if (!object isKindOfClass:[MyObjectClass class]]) { return NO; }
MyObjectClass *myObject = (MyObjectClass *)object;
if (abs(self.uniqueKey - myObject.uniqueKey) < EPSILON) { return YES; }
return NO;
}
//...
#end
Note that you should NOT check for direct equality between two float or double values; see How dangerous is it to compare floating point values? for a good discussion about the pitfalls of working with floating point and double precision values.

Keeping a specific Order of an NSMutableDictionary Object

I am trying to write a method which will allow me to keep the order of my NSMutableDictionary keys when they are being inserted into the data structure. I know that the NSMutableDictionary works of a hash map, hence not maintaining specific order.
So I need to somehow keep track of the keys which are being inserted into the dictionary, and when retrieving the values from the dictionary, the keys are to be printed(key values) in this same order as when originally inserted. The keys which are inserted into the dictionary are alphanumeric. They just need to be printed out in the same order as when inserted into the NSMutableDictionary.
Can this be achieved? I would like to remain using the NSDictionary Data Structure.
NSDictionary (and all its relations) are unordered collections so to "keep its order" makes no sense as there is no order.
If you are wanting to retrieve objects in a specific order then you need to be using an NSArray. (Or NSOrderedSet if uniqueness of hashes is important).
Simple and naive option
If you have a dictionary structure of...
{
key1:value1,
key2:value2,
key3:value3,
//and so on
}
Then you might be better using something like...
[
{
key1:value1
},
{
key2:value2
},
{
key3:value3
}
]
// i.e. an array of dictionaries
More code but much better option
Or you could create a new collection class as a subclass of NSObject.
In the class you could have something like...
- (void)addObject:(id)object forKey:(id)key
{
self.dictionary[key] = object;
[self.array addObject:key];
}
And...
- (id)objectForKey:(key
{
return self.dictionary[key];
}
And...
- (id)objectAtIndex:(NSUInteger)index
{
return self.dictionary[self.array[index]];
}
And even...
- (void)removeObjectForKey:(id)key
{
[self.dictionary removeObjectForKey:key];
[self.array removeObject:key];
}
You could even make it conform to fast enumeration so you can do...
for (id object in mySuperSpecialCollection) {
}
and make it dispense objects in the order of the array.
Can this be achieved? I would like to remain using the NSDictionary Data Structure.
No. When using instances of NSDictionary the actual class is private. As often with class clusters, it's not possible to subclass NSDictionary and use derived functionality (storing key value pairs).
The best way to go is to set up your own data structure, maybe using an NSOrderedSet and an NSDictionary in conjunction.

In Objective-C, using Core Data, how to fetch all of the Entity classes and their meta information?

I'd like to fetch all the entities I've created and filter all the ones that have a particular parent entity. In the end, I'm looking for a collection of the class names for all of these entities so I can iterate on them.
I couldn't find any info on how I can do this. Is it possible? How can it be done?
NSArray *allEntityNames = [[yourManagedObjectModel.entitiesByName allKeys] allObjects];
Gets you an array of all your entity string names, ordered alphabetically. Just calling entitiesByName on your NSManagedObjectModel gets you all the meta information you might want too but is very verbose.
If you want you can check for parent entities right in a block by seeing if the entity's parent entity is NSManagedObject or one of your subclasses instead, or do it after the block if you want to keep the "master list" for a few cases.
NSArray *allEntityNames = [[yourManagedObjectModel.entitiesByName keysOfEntriesPassingTest:^BOOL(id key, id obj, BOOL *stop) {
if (hasParent)
return YES;
else
return NO;
}] allObjects];

NSManagedObject timeStamp update

I want to track changes of NSManagedObject properties, in order to keep NSData *lastUpdate property "up to date"
There are several approaches to get Notified when NSManagedObject changes its properties
I. First is to override the setter Methods of all properties you want to track. Which is quite complicated in NSManaged object - check it here
II. Second could be a good one. You can just override "didChangeValueForKey" method That is called on every property change.
-(void)didChangeValueForKey:(NSString *)key{
[super didChangeValueForKey:key];
NSLog(#"Value for key:%# has changed", key);
}
Unfortunately we should not override this method due to the documentation that says...:
"You must not override this method."
III. Key-value-observing leads us back to IInd approach, with overriding "didChangeValueForKey".
upd.
IV. I tried to override -willSave method
-(void)willSave{
NSArray *observedKeys = #[#"name", #"imageType"];
NSDictionary * changesALL = self.changedValues;
for (id key in changesALL){
if ([observedKeys containsObject:key]){
self.lastUpdate = [NSDate date];
NSLog(#"updated For key: %#", key);
}
}
}
This led infinitive loop, which is described in documentation.
(altho the right approach is described here, so I've answered this question already)
If you want to update a persistent property value, you should typically test for equality >of any new value with the existing value before making a change. If you change property >values using standard accessor methods, Core Data will observe the resultant change >notification and so invoke willSave again before saving the object’s managed object >context. If you continue to modify a value in willSave, willSave will continue to be called >until your program crashes.
For example, if you set a last-modified timestamp, you should check whether either you >previously set it in the same save operation, or that the existing timestamp is not less >than a small delta from the current time. Typically it’s better to calculate the timestamp >once for all the objects being saved (for example, in response to an >NSManagedObjectContextWillSaveNotification).
A suitable solution for your use case to override the willSave method and use it to set the new lastUpdated value. This method is called automatically on dirty objects before they are saved into the context.
If you need to verify what is dirty you can use the contents of the changedValues property.
So after all I figured out that the best solution to track changes of Managed Object is to register for NSManagedObjectContextWillSaveNotification instead, and set the timestamp on all updated and inserted objects in the managed object context. The registered method could look like this:
-(void)contextWillSave:(NSNotification *)notify
{
NSManagedObjectContext *context = [notify object];
NSDate *dateOfTheLastModification = [NSDate date];
for (NSManagedObject *obj in [context insertedObjects]) {
[obj setValue:dateOfTheLastModification forKey:#"lastUpdate"];
}
for (NSManagedObject *obj in [context updatedObjects]) {
[obj setValue:dateOfTheLastModification forKey:#"lastUpdate"];
}
}
This assumes that all your entities have a lastModifiedDate attribute, otherwise you have to check the class of the objects.
To avoid the infinite loop, try this magic:
- (void)willSave{
if(![self.changedValues objectForKey:#"localModificationDate"]){
self.localModificationDate = [NSDate date];
}
else{
[super willSave];
}
}
Once the modification date has been set it won't set it again for the current save. There is a side affect that if the save fails and you save successfully again, I reckon the date will be the from the previous save attempt.
This is fine if you are saving the context after every edit, but the usual design of core data is to only save either at app suspend or after a long time. So it's likely the lastUpdate will be needed for something before then and it won't have the new value yet.

iOS How to save a dictionary of class objects?

Up until now, I've been using NSUserDefaults to save my NSMutableDictionaries, but now I want to save a dictionary which would look like this: It'll hold different Car objects with keys: the model of the car. The Car class will have a dictionary of its basic characteristics and a dictionary of Person objects (who use the car). Every Person class will have personal information as properties. I can do everything else, but not the saving of the first NSMutableDictionary which will hold all of that info.(dictionaries must contain only non-property values error) What is an appropriate way to save it?
You have to use an archiver like NSKeyedArchiver to serialise / deserialize your objects to an NSData archive.
All objects in your graph
Car
Person
All properties of these objects not being a value-object
(i.e not NSString, NSDate, NSArray, NSNumber,...)
must adopt the NSCoding protocol. To do so you use:
-(id)initWithCoder:(NSCoder *)coder
{
// Tell the unarchiver how to read your object properties from the archive
_oneObjectProperty = [coder decodeObjectForKey:#"propertyKey"];
.....
}
-(void)encodeWithCoder:(NSCoder *)coder
{
// Tell the archiver how to serialise your object properties
[coder encodeObject:_oneObjectProperty ForKey:#"propertyKey"];
....
}
You'll find code example in here:
Why NSUserDefaults failed to save NSMutableDictionary in iPhone SDK?
You could use NSKeyedArchiver to save objects and NSKeyUnarchiver to retrieve them.

Resources