NSUserDefaults fail to save NSMutableDictionary - iphone

Iam trying to save NSMutableDictionary to NSUserDefaults with this code:
NSUserDefaults *def = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *repositoryData = [[NSMutableDictionary alloc] init];
[repositoryData setObject:personData forKey:#"persondata"];
[def setObject:repositoryData forKey:#"kUserRepository"];
[def synchronize];
[repositoryData removeAllObjects];
[repositoryData release];
But i am getting this msg:
[NSUserDefaults setObject:forKey:]: Attempt to insert non-property value '{
persondata = "<Person: 0x1ed57e70>";
}' of class '__NSDictionaryM'. Note that dictionaries and arrays in property lists must also contain only property values.
and also the Person Class have NSCoding:
Person.h
#interface Person : NSObject <NSCoding> {
NSString *name;
NSString *email;
NSString *phoneNumber;
NSString *gender;
int age;
}
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *email;
#property (nonatomic, retain) NSString *phoneNumber;
#property (nonatomic, retain) NSString *gender;
#property (assign) int age;
#end
Person.m
#implementation Person
#synthesize name,email,phoneNumber,age,gender;
#pragma mark
#pragma mark NSCoder
- (void)encodeWithCoder:(NSCoder *)encoder{
[encoder encodeObject:self.name forKey:#"username"];
[encoder encodeObject:self.email forKey:#"useremail"];
[encoder encodeObject:self.phoneNumber forKey:#"userphonenumber"];
[encoder encodeObject:self.gender forKey:#"usergender"];
[encoder encodeInt:self.age forKey:#"userage"];
}
- (id)initWithCoder:(NSCoder *)decoder {
if(self = [super init]){
self.name = [decoder decodeObjectForKey:#"username"];
self.email = [decoder decodeObjectForKey:#"useremail"];
self.phoneNumber = [decoder decodeObjectForKey:#"userphonenumber"];
self.gender = [decoder decodeObjectForKey:#"usergender"];
self.age = [decoder decodeIntForKey:#"userage"];
}
return self;
}
#end
Any idea why it happen?

There are very specific restrictions as to what type of data can be synced to NSUserDefaults within an NSDictionary. These include NSString, NSNumber, NSData, NSArray and NSDictionary. Use your NSCoder methods to serialize to NSData (using NSKeyedArchiver) before storing in the NSUserDefaults.
Also, take a look at this

Related

Importing NSMutable Array into Map Annotations

Hi all i have a mysql database that consists of 7 columns and i have a script that reads it and extracts the data as JSON which i have pulled into an NSMutableArray, I want to be able to use this array to setup annotations on my Map but i'm not sure what to do here, as you can see i have defined one annotation here which shows up no problem but i'm honestly not sure how to show the NSMutableArray items? The NSMutable array will have more information than needed for the Annotations, I only need, coordinate, title and subtitle so how can i go about doing this? Here is my code so far:
hazards.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface Hazards : NSObject <MKAnnotation>
#property (nonatomic, assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *subtitle;
#property (nonatomic, strong) NSString * ID;
#property (nonatomic, strong) NSString * ROUTE;
#property (nonatomic, strong) NSString * ADDRESS;
#property (nonatomic, strong) NSString * LATITUDE;
#property (nonatomic, strong) NSString * LONGITUDE;
#property (nonatomic, strong) NSString * HAZARD;
#property (nonatomic, strong) NSString * RISK;
// Methods
- (id) initWithID: (NSString *) hazardsID andROUTE: (NSString *) hazardsROUTE andADDRESS: (NSString *) hazardsADDRESS andLATITUDE: (NSString *) hazardsLATITUDE andLONGITUDE: (NSString *) hazardsLONGITUDE andHAZARD: (NSString *) hazardsHAZARD andRISK: (NSString *) hazardsRISK;
#end
hazards.m
#import "Hazards.h"
#implementation Hazards
#synthesize coordinate, title, subtitle, ID, ROUTE, ADDRESS, LATITUDE, LONGITUDE, HAZARD, RISK;
- (id) initWithID: (NSString *) hazardsID andROUTE: (NSString *) hazardsROUTE andADDRESS: (NSString *) hazardsADDRESS andLATITUDE: (NSString *) hazardsLATITUDE andLONGITUDE: (NSString *) hazardsLONGITUDE andHAZARD: (NSString *) hazardsHAZARD andRISK: (NSString *) hazardsRISK {
self = [super init];
if (self)
{
ID = hazardsID;
ROUTE = hazardsROUTE;
ADDRESS = hazardsADDRESS;
LATITUDE = hazardsLATITUDE;
LONGITUDE = hazardsLONGITUDE;
HAZARD = hazardsHAZARD;
RISK = hazardsRISK;
}
return self;
}
#end
viewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import "Hazards.h"
#interface ViewController : UIViewController
#property (nonatomic, strong) NSMutableArray *json;
#property (nonatomic, strong) NSMutableArray *hazardsArray;
#property (weak, nonatomic) IBOutlet MKMapView *mapView;
#pragma mark - Methods
-(void) retrieveData;
#end
viewController.m
#import "ViewController.h"
#import "Hazards.h"
#interface ViewController ()
#end
// Railway Street Ballymena Coordinates
#define BALLYMENA_LATITUDE 54.857719;
#define BALLYMENA_LONGITUDE -6.280654;
// Span
#define THE_SPAN 0.01f;
#define getDataURL #"localhost:8888/rmb/json.php"
#implementation ViewController
#synthesize json, hazardsArray, mapView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Create the region
MKCoordinateRegion myRegion;
// Center
CLLocationCoordinate2D center;
center.latitude = BALLYMENA_LATITUDE;
center.longitude = BALLYMENA_LONGITUDE;
//Span
MKCoordinateSpan span;
span.latitudeDelta = THE_SPAN;
span.longitudeDelta = THE_SPAN;
myRegion.center = center;
myRegion.span = span;
// Set our mapview
[mapView setRegion:myRegion animated: YES];
// Annotation
NSMutableArray *locations = [[NSMutableArray alloc] init];
CLLocationCoordinate2D location;
Hazards *myAnn;
// Pin to show Royal Mail Ballymena delivery office
myAnn = [[Hazards alloc] init];
location.latitude = BALLYMENA_LATITUDE;
location.longitude = BALLYMENA_LONGITUDE;
myAnn.coordinate = location;
myAnn.title = #"Royal Mail Ballymena";
myAnn.subtitle = #"111, Railway Street";
[locations addObject:myAnn];
[self.mapView addAnnotations:locations];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Methods
-(void) retrieveData {
NSURL *url = [NSURL URLWithString:getDataURL];
NSData *data = [NSData dataWithContentsOfURL:url];
json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
// Setup our hazards array
hazardsArray = [[NSMutableArray alloc] init];
for (int i =0; i < json.count; i++) {
// Create hazard object
NSString *hazardsID = [[json objectAtIndex:i] objectForKey:#"ID"];
NSString *hazardsROUTE = [[json objectAtIndex:i] objectForKey:#"ROUTE"];
NSString *hazardsADDRESS = [[json objectAtIndex:i] objectForKey:#"ADDRESS"];
NSString *hazardsLATITUDE = [[json objectAtIndex:i] objectForKey:#"LATITUDE"];
NSString *hazardsLONGITUDE = [[json objectAtIndex:i] objectForKey:#"LONGITUDE"];
NSString *hazardsHAZARD = [[json objectAtIndex:i] objectForKey:#"HAZARD"];
NSString *hazardsRISK = [[json objectAtIndex:i] objectForKey:#"RISK"];
Hazards *myHazards = [[Hazards alloc] initWithID:hazardsID andROUTE:hazardsROUTE andADDRESS:hazardsADDRESS andLATITUDE:hazardsLATITUDE andLONGITUDE:hazardsLONGITUDE andHAZARD:hazardsHAZARD andRISK:hazardsRISK];
// Add our hazards object to our hazards array
[hazardsArray addObject:myHazards];
}
// [self.mapView addAnnotation:hazardsArray];
}
#end
Many Thanks in Advance
Assuming your retrieveData correctly builds Hazard objects and is called at the right time you could either add them one at a time with this line inside the loop
[self.mapView addAnnotation:myHazards];
or all at once after the loop is finished
[self.mapView addAnnotations:hazardsArray];

NSMutableArray or NSArray structures

In NSMutableArray or NSArray how do you set up the row structure. All the examples I can find just deal with one dimensional integers or strings.
So how do you go about setting up an array row structure like:
Integer, String, Date
You would need to create an array of arrays, or an array of dictionaries, the second approach being better if you'd like to refer to the data by name rather than index:
NSMutableArray *rows = [[NSMutableArray alloc] init];
NSMutableDictionary *firstRow = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:12], #"fieldWithNumber",
[NSString stringWithFormat:#"A formatted string %ld", 34], #"fieldWithString",
[NSDate date], #"fieldWithDate",
nil];
[rows addObject:firstRow];
// etc.
You can make custom class and add collection of these objects in an array.
For example please assume that we have to do some rows about places then create a class PLACE.h
#interface PLACE : NSObject
{
NSString *title;
NSString *summary;
NSString *url;
NSString *latitude;
NSString *longitude;
}
#property (nonatomic, retain) NSString *title;
#property (nonatomic, retain) NSString *summary;
#property (nonatomic, retain) NSString *url;
#property (nonatomic, retain) NSString *latitude;
#property (nonatomic, retain) NSString *longitude;
#end
PLACE.m
#implementation PLACE
#synthesize title;
#synthesize summary;
#synthesize url;
#synthesize latitude;
#synthesize longitude;
- (void) dealloc
{
[title release];
[summary release];
[url release];
[latitude release];
[longitude release];
[super dealloc];
}
#end
Create instance of the class PLACE for each place and hold the objects in the array. I think that solves your problem.
An array is just a collection of objects.
Just create an object that has an integer, date and string and then add instances of those to the array.

cannot pass the obj-c class obj to another view

I have 3 classes
First -> MainViewController:
#interface MainViewController : UIViewController {
UtilityBadah *utility;
}
#property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
- (IBAction)option;
#end
#implementation MainViewController
#synthesize managedObjectContext = _managedObjectContext;
(IBAction)option{
UtilityBadah *util = [[UtilityBadah alloc] initWithContext:_managedObjectContext];
OptionController *ovc = [[OptionController alloc] init];
ovc.util = util;
ovc.managedObjectContext = _managedObjectContext;
[self.navigationController pushViewController:ovc animated:YES];
[util release];
[ovc release];
}
#end
Second -> UtilityBadah:
#interface UtilityBadah : NSObject {
NSManagedObjectContext *managedObjectContext;
NSString *kitab;
NSString *lagu;
NSString *font;
NSString *sizefont;
}
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain) NSString *kitab;
#property (nonatomic, retain) NSString *lagu;
#property (nonatomic, retain) NSString *font;
#property (nonatomic, retain) NSString *sizefont;
(id) initWithContext: (NSManagedObjectContext *) context;
#end
#implementation UtilityBadah
#synthesize managedObjectContext;
#synthesize kitab;
#synthesize lagu;
#synthesize font;
#synthesize sizefont;
-(id) initWithContext: (NSManagedObjectContext *) context {
NSError *err;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *enDesc = [NSEntityDescription entityForName:#"OptionDB" inManagedObjectContext:context];
[request setEntity:enDesc];
NSArray *arrData = [context executeFetchRequest:request error:&err];
for (OptionDB *data in arrData) {
lagu = data.lagu;
kitab = data.kitab;
font = data.font;
sizefont = data.sizefont;
}
return self;
}
Thrid -> OptionController:
#interface OptionController : UIViewController{
NSManagedObjectContext *managedObjectContext;
UtilityBadah *util;
}
#property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;
#property (nonatomic, retain) UtilityBadah *util;
#end
(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
bla..bla..
NSLog(#"value is %#",self.util.kitab);
cell.textLabel.text = [listData objectAtIndex:row];
return cell;
}
i wonder why this piece of code:
NSLog(#"value is %#",self.util.kitab);
cause an error "Receiced signa: "EXC_BAD_ACCESS" ?
i wonder ther somthing wrong the way i passed the UtilityBadah object from MainViewController to my OptionController.
Many thanks for any answer.
P.S what is wrong with my xcode 4.2 error information, why it always show "Program received signal: "EXC BAD ACCESS"? cant it be informative?
i think your init method is wrong, try assigning self to [super init] at the start, and doing all your set up after checking that self != nil. this is the basic form of an init:
- (id)init
{
self = [super init];
if (self)
//do setup
return self;
}
also, you are using a subclass of NSObject and associating a managed object context to it, you should look into core data in more detail, you should be using NSManagedObjects
The property name in the OptionController is util not utility
If you want to access the utility property in the UtilityBadah class you will have to do:
self.util.utility
In the UtilityBadah class, initWithContext method, change the for loop as following
for (OptionDB *data in arrData) {
self.lagu = data.lagu;
self.kitab = data.kitab;
self.font = data.font;
self.sizefont = data.sizefont;
}
In your case, the value is not retained since you have assigned without accessing through the property.
You need to allocate those objects first. Here's how your initWithContext method should look like:
-(id) initWithContext: (NSManagedObjectContext *) context {
self = [super init];
if (self) {
NSError *err;
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *enDesc = [NSEntityDescription entityForName:#"OptionDB" inManagedObjectContext:context];
[request setEntity:enDesc];
NSArray *arrData = [context executeFetchRequest:request error:&err];
for (OptionDB *data in arrData) {
self.lagu = [NSString stringWithString:data.lagu];
self.kitab = [NSString stringWithString:data.kitab];
self.font = [NSString stringWithString:data.font];
self.sizefont = [NSString stringWithString:data.sizefont];
}
}
return self;
}

IPhone persist Model

I would like to persist an object of a class (not just NSString´s). For instance, I have this class:
** News.h:**
#import <Foundation/Foundation.h>
#interface News : NSObject
#property (nonatomic, retain) NSString * atrib1;
#property (nonatomic, retain) NSString * atrib2;
#end
** News.m:**
#import "News.h"
#implementation News
#synthesize atrib1;
#synthesize atrib2;
#end
Should I have to use a plist to storage it? How should I do it?
Using NSCoding:
In News.m, I have added:
- (void) encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:atrib1 forKey:#"key1"];
[encoder encodeObject:atrib2 forKey:#"key2"];
}
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
atrib1 = [[decoder decodeObjectForKey:#"key1"] retain];
atrib2 = [[decoder decodeObjectForKey:#"key2"] retain];
return self;
}
-(void)dealloc{
[super dealloc];
[atrib1 release];
[atrib2 release];
}
In News.h:
#interface News : NSObject<NSCoding>{
NSCoder *coder;
}
#property (nonatomic, retain) NSString * atrib1;
#property (nonatomic, retain) NSString * atrib2;
#end
To read update and persist a new object in the plist:
- (IBAction)addANewNews:(id)sender {
//Plist File
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *plistPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"myplist.plist"];
//Reading current news
NSData *oldNews = [NSData dataWithContentsOfFile:plistPath];
NSMutableArray *news = (NSMutableArray *)[NSKeyedUnarchiver unarchiveObjectWithData:oldNews];
if (news == nil)
news = [[NSMutableArray alloc] init];
//Adding a new news
[news addObject:aNewNews];
NSError *error;
NSData* newData = [NSKeyedArchiver archivedDataWithRootObject:news];
//persisting the updated news
BOOL success =[newData writeToFile:plistPath options:NSDataWritingAtomic error:&error];
if (!success) {
NSLog(#"Could not write file.");
}else{
NSLog(#"Success");
}
}

What is the best way to save an NSMutableArray to NSUserDefaults?

I have a custom object called Occasion defined as follows:
#import <Foundation/Foundation.h>
#interface Occasion : NSObject {
NSString *_title;
NSDate *_date;
NSString *_imagePath;
}
#property (nonatomic, retain) NSString *title;
#property (nonatomic, retain) NSDate *date;
#property (nonatomic, retain) NSString *imagePath;
Now I have an NSMutableArray of Occasions which I want to save to NSUserDefaults. I know it's not possible in a straight forward fashion so I'm wondering which is the easiest way to do that? If serialization is the answer, then how? Because I read the docs but couldn't understand the way it works fully.
You should use something like NSKeyedArchiver to serialize the array to an NSData, save it to the NSUserDefaults and then use NSKeyedUnarchiver to deserialize it later:
NSData *serialized = [NSKeyedArchiver archivedDataWithRootObject:myArray];
[[NSUserDefaults standardUserDefaults] setObject:serialized forKey:#"myKey"];
//...
NSData *serialized = [[NSUserDefaults standardUserDefaults] objectForKey:#"myKey"];
NSArray *myArray = [NSKeyedUnarchiver unarchiveObjectWithData:serialized];
You will need to implement the NSCoding protocol in your Occasion class and correctly save the various properties to make this work correctly. For more information see the Archives and Serializations Programming Guide. It shouldn't be more than a few lines of code to do this. Something like:
- (void)encodeWithCoder:(NSCoder *)coder {
[super encodeWithCoder:coder];
[coder encodeObject:_title forKey:#"_title"];
[coder encodeObject:_date forKey:#"_date"];
[coder encodeObject:_imagePath forKey:#"_imagePath"];
}
- (id)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
_title = [[coder decodeObjectForKey:#"_title"] retain];
_date = [[coder decodeObjectForKey:#"_date"] retain];
_imagePath = [[coder decodeObjectForKey:#"_imagePath"] retain];
return self;
}
NSUserDefaults is intended for user preferences, not storing application data. Use CoreData or serialize the objects into the documents directory. You'll need to have your class implement the NSCoding protocol for it to work.
1) Implement NSCoding in Occasion.h
#interface Occasion : NSObject <NSCoding>
2) Implement the protocol in Occasion.m
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
self.title = [aDecoder decodeObjectForKey:#"title"];
self.date = [aDecoder decodeObjectForKey:#"date"];
self.imagePath = [aDecoder decodeObjectForKey:#"imagePath"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:title forKey:#"title"];
[aCoder encodeObject:date forKey:#"date"];
[aCoder encodeObject:imagePath forKey:#"imagePath"];
}
3) Archive the data to a file in documents directory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *path= [documentsPath stringByAppendingPathComponent:#“occasions”];
[NSKeyedArchiver archiveRootObject:occasions toFile:path];
4) To unarchive...
NSMutableArray *occasions = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
You could implement NSCoding in Occasion.
You then use [NSKeyedArchiver archivedDataWithRootObject:myArray] to create an NSData object from the array. You can put this into user defaults.