Serialising between versions - ios

How do I deserialise the object which got modified in new version.
For Example
I have a person class in my project for version 1.0.
Array of Person objects gets serialized when app is terminated
#interface Person : NSObject <NSCoding>
#property NSString *name;
#property NSUInteger age;
#property NSString *location;
#end
This is how I am serialising my data
- (void)serializePersonList {
NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:personList];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:encodedObject forKey:kPersonKey];
[defaults synchronize];
}
Now I am planning to release version 2. And Person Class has been evolved to
#interface Person : NSObject <NSCoding>
#property NSString *name;
#property NSUInteger age;
#property NSString *phone;
#property KSAddress *location;
#end
Note
Type of location has beed updated to KSAddress
phone attribute has been added.
How do I make my app deserialize the old Person data ?

Hopefully you're using a keyed archiver. You write the code to connect the archived data for each key to the instance variable it belongs to. As you have changed type your code now needs to read the data into an id local variable, check the data type (isKindOfClass) and then either store the value or convert it to your new format and then store it.

Related

NString value of custom object gets lost after segue (Objective C)

Heyo Guys
So I am more or less new to Objective C and got to a problem which I seem unable to solve.
I created a class called "Students" which all have a name surname etc. All those Students are put into a NSMutableArray (before they get created from JSON , that seems to work without a problem though). Then all the names of the students are put into a ListView. Afterwards when a name is clicked (segue passes the object), one should see the details of said student (i.e. his full name, his id, his street).
The problem is that all the string values of the student object seem to get lost.
I checked that all the student object work just fine. I think the problem lies at the #property in my student class.
Any comments and suggestions are appreciated
This is an excerpt of student.h As said only the string values get lost, the int (here the plz value) remains correct
#property (nonatomic, weak)NSString *lastname;
#property (nonatomic, weak)NSString *surname;
#property (nonatomic, weak)NSString *street;
#property (nonatomic, assign)int plz;
EDIT:
Here is where i parse my json.
for (NSDictionary *dic in jsonArray){
NSNumber *identity = [dic valueForKey:#"id"] ;
NSString *firstName = (NSString*) [dic valueForKey:#"first_name"];
NSString *lastName = (NSString*) [dic valueForKey:#"last_name"];
NSString *street = (NSString*) [dic valueForKey:#"street"];
NSNumber *plz = [dic valueForKey:#"plz"] ;
NSString *birth = (NSString*) [dic valueForKey:#"date_of_birth"];
NSArray *bills = dic[#"bills"];
NSArray *hours = dic[#"hours"];
NSLog(#"First %#",firstName);
Student *student = [[Student alloc]initWithLastName:lastName withSurname:firstName withStreet:street withPLZ:plz withOrt:#"Uitikon" withBirthDate:birth withOccupation:#"Schüler"];
[ApprenticeList addObject:student];
}
EDIT 2 :
I found out that the string values get lost even before the segue. All these objects are created in
ViewDidLoad
But in
prepareforsegue
all the values are allready null (except for the int) .So the only place where the student objects work is in
ViewdidLoad
#property (nonatomic, weak)NSString *lastname;
#property (nonatomic, weak)NSString *surname;
#property (nonatomic, weak)NSString *street;
change to
#property (nonatomic, strong)NSString *lastname;
#property (nonatomic, strong)NSString *surname;
#property (nonatomic, strong)NSString *street;
You can also use 'copy'. It will cause the setter for that property to create a copy of the object, otherwise it is identical to strong.

How to get Custom Objects from JSON in Xcode?

Let us say I have an NSObject Class Person.
#interface Person : NSObject
#property NSString *id;
#property NSString *name;
#property Address *billingAddress;
#end
#interface Address : NSObject
#property NSString *lane;
#property NSString *country;
#property NSString *zip;
#end
Now when I fetch the response from a URL, the response is in the form:
{
"response":
{
"Persons":[{"id":"2232","name":"John","Address":{"lane":"Adelaide Avenue","country":"Canada","zip":"45503"}}{"id":"3422","name":"Jane","Address":{"lane":"Victoria Avenue","country":"Australia","zip":"34903"}}]
}
}
I want to parse the response directly into objects without having to write a method to read and assign objects from NSDictionary. Is there are no objects to parse directly from the response to Object based on the Object parameters similar to "GSon" in Android.
EDIT:
I have used the below code to have generic class that does the job for strings without having to know about the object itself.
for (NSString *key in [dct allKeys]) {
if ([cat respondsToSelector:NSSelectorFromString(key)]) {
[cat setValue:[dct valueForKey:key] forKey:key];
}
}
There is no such magic, not even in Android's GSon!!!
Some where down the line you need to write code for converting JSON to your object.
You may create a generic class, or a method (just once) to convert all dictionary values to your object.
After some digging I did get a JSON framework that does exactly what I wanted - JSONModel.
We just need to specify Models and relationships and all the logic for converting JSON response to the models is handled by the framework. Very handy.
Basic usage :
Consider you have a JSON response like
{"id":"10", "country":"Germany", "dialCode": 49, "isInEurope":true}
The corresponding model will be
#import "JSONModel.h"
#interface CountryModel : JSONMode
#property (assign, nonatomic) int id;
#property (strong, nonatomic) NSString* country;
#property (strong, nonatomic) NSString* dialCode;
#property (assign, nonatomic) BOOL isInEurope;
#end
We don't need to write additional code in the .m file to parse and assign values to the variables. Now to initialise the model from the response we just need to do the below
NSString* json = (fetch JSON here)
NSError* err = nil;
CountryModel* country = [[CountryModel alloc] initWithString:json error:&err];
The works well with complex data structures as well.

How can I store a string and corresponding tags?

I have a bunch of activities that are specific to age groups and genders (not trying to be agist or sexist here, just some groups are more likely to take part in these activities so I am trying to suggest them using tags)
I basically need an array that would store this information.
I want a multidimensional array that essentially looks like this:
["Playing Video Games", 10, 24, "male"],
["Putting on Makeup", 14, 50, "female"],
["Sleeping", 0, 100, "both"]
["Activity name string", age lower limit, age upper limit, "genders allowed"];
How can I create this in iOS?
I'll be storing it in the userDefaults.
Objective-C is an object-oriented programming language. So, you need to create a custom class called, maybe 'Person' and then add the required properties.
For example, the custom class's .h file you require will be something like :
#interface Person : NSObject
#property (nonatomic, assign) NSString *activity;
#property (nonatomic, assign) NSString *sex;
#property int ageLowerLimit;
#property int ageUpperLimit;
#end
Then you can manage such objects by first,
Importing this custom class in your code :
#import "Person.h"
And then creating a new Person type object :
Person *firstPerson = [[Person alloc] init];
[firstPerson setActivity : #"Sleeping"];
[firstPerson setSex : #"Male"];
[firstPerson setAgeLowerLimit : 0];
[firstPerson setAgeUpperLimit : 100];
To store them you could use CoreData or just the good old NSUserDefaults.
Here's how to go about the NSUserDefaults approach :
You first store the various Person objects in an NSMutableArray, then you synchronise the defaults :
NSMutableArray *people = [[NSMutableArray alloc] init];
[people addObject : firstPerson];
NSUserDefaults *defaults = [NSUserDefaults standardDefaults];
[defaults setObject:people forKey:#"PEOPLE"];
[defaults synchronise];
To use these objects later, do this :
NSUserDefaults *defaults = [NSUserDefaults standardDefaults];
NSMutableArray *savedPeople = [defaults objectForKey:#"PEOPLE"];
Person *person1 = [savedPeople objectAtIndex:0];
Robert is right and u should use CoreData.
There is an nice [Core Data Tutorial][1] which explains the basics.
WHile using UserDefaults you can store your Data in an Array
[[NSUserDefaults standardUserDefaults] setObject:myArray forKey:#"myArray"];

NSDictionary custom object store and retrieve

Trying to store value in NSDictionary and retrieve it
Objects
#import <Foundation/Foundation.h>
#class ATTTEstOBJ;
#interface ATTTEst : NSObject
#property (nonatomic, retain) NSString *string1;
#property (nonatomic, retain) NSString *string2;
#property (nonatomic, retain) ATTTEstOBJ *obj1;
#end
#interface ATTTEstOBJ : NSObject
#property (nonatomic, retain) NSString *string3;
#property (nonatomic, retain) NSString *string4;
#property (nonatomic, retain) NSString *array1;
#end
I know it needs to be encoded properly to save and retrieve values.but In this case it is a composite object and I have no idea, how to deal it with.
- (void) encodeWithCoder: (NSCoder *)coder
So TLDR , How to save the composite value into dictionary and retrieve it back
I want to store ATTTest into a dictionary and retrieve it back.
EDIT : Detailed explanation
ATTTEst *test=[[ATTTEst alloc]init];
test.string1=#"a";
test.string2=#"b";
ATTTEstOBJ *obj=[[ATTTEstOBJ alloc]init];
obj.string3=#"c";
obj.string4=#"d";
test.obj1=obj;
NSMutableDictionary *dict=[[NSMutableDictionary alloc]initWithCapacity:3];
[dict setObject:test forKey:#"test"];
NSLog(#"%#",dict);
ATTTEst *tester=[dict objectForKey:test];
NSLog(#"%#",tester.obj1.string3);
IT shows null.as output I want to get the value as c for tester.obj1.string3
ATTTEst *tester=[dict objectForKey:test];
should be
ATTTEst *tester=[dict objectForKey:#"test"];
You have used the object test (instead of the string #"test") as key when retrieving the object. I don't think that
was intentionally.
In order to store them into NSDictionary, you don't need to encode them.
Just do:
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:, attestObject,#"attestObject", attest2Object,#"atttest2" nil];
Where attestObject and attest2Object are the objects you want to store, and strings are their keys.
This has nothing to do with encoding...

iCloud - How to save archive containing array of custom objects

I have developed a small app that stores locally in iOS through archiving an array of custom objects containing:
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *dateCreated;
#property (nonatomic, copy) NSString *desc;
#property (nonatomic, copy) NSString *url;
I want to sync said archive using iCloud and I believe the recommended mechanism is through a UIDocument subclass.
All UIDocument examples I found utlilized a single instance with 1 single NSString, so I am a little confused how to go about syncing a whole array of custom objects but utilizing UIDocument (like I do today locally through NSCoding).
Should I create an array of UIDocument objects containing the properties listed above, should I create an instance of UIDocument containing 1 instance of the data object described above and then create an array containing all the instances, or should 1 single UIDocument contain the complete array of custom objects ?
I have done some research but I am still confused.
In the end I would need to sync just 1 file containing an array of said custom objects.
Thanks in advance for your help
What I have today is a custom class as described above with 4 strings called Snippet and in my Root view Controller I have an NSMutableArray called list where I add each new instance of that Snippet Class.
self.list = [[NSMutableArray alloc] init];
Snippet *newEntry = [[Snippet alloc] init];
[self.list addObject:newEntry];
Should I create an UI Document subclass that owns the array of custom objects ?
The example in the docs does indeed show a UIDocument subclass that just has one string, but it returns a NSData from -contentsForType:error:. You can store as many objects as you like in an NSData using an NSKeyedArchiver. Read Serializing Objects to learn how to encode objects using NSKeyedArchiver (and keep reading to learn how to get them back!).
Using your properties as an example...
#interface MyDocument : UIDocument
{
}
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *dateCreated;
#property (nonatomic, copy) NSString *desc;
#property (nonatomic, copy) NSString *url;
#end
#implementation MyDocument
//...
- (id)contentsForType:(NSString *)typeName error:(NSError **)outError
{
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:name forKey:#"name"];
[archiver encodeObject:dateCreated forKey:#"created"];
[archiver encodeObject:desc forKey:#"desc"];
[archiver encodeObject:url forKey:#"url"];
[archiver finishEncoding];
// release archiver if you're not using ARC
return data;
}
#end;
WARNING: I haven't compiled the code above, so no guarantees. This should serve as an example to illustrate using an archiver to store multiple objects in a single data object which you can return as your document's content.

Resources