How to add object in singleton NSMutableArray - ios

I used to store the array data downloaded from the server.
But I can not save them in the singleton array.
It seems without access to the object.
Why ulatitude, ulongitude, uaccuracy, uplacename is nil?...
in .h file
#import <Foundation/Foundation.h>
#interface LocationData : NSObject
{
NSMutableArray *ulatitude;
NSMutableArray *ulongitude;
NSMutableArray *uaccuracy;
NSMutableArray *uplacename;
}
#property (nonatomic, retain) NSMutableArray *ulatitude;
#property (nonatomic, retain) NSMutableArray *ulongitude;
#property (nonatomic, retain) NSMutableArray *uaccuracy;
#property (nonatomic, retain) NSMutableArray *uplacename;
+ (LocationData*) sharedStateInstance;
#end
in .m file
#import "LocationData.h"
#implementation LocationData
#synthesize uaccuracy;
#synthesize ulatitude;
#synthesize ulongitude;
+ (LocationData*) sharedStateInstance {
static LocationData *sharedStateInstance;
#synchronized(self) {
if(!sharedStateInstance) {
sharedStateInstance = [[LocationData alloc] init];
}
}
return sharedStateInstance;
}
#end
use
[manager POST:urlStr parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"%#",responseObject);
// json response array
if ([responseObject isKindOfClass:[NSArray class]]) {
NSArray *responseArray = responseObject;
NSDictionary *responseDict = [[NSDictionary alloc] init];
LocationData* sharedState = [LocationData sharedStateInstance];
for(NSUInteger i=0; i < responseArray.count; i++)
{
responseDict = [responseArray objectAtIndex:i];
double dlat = [[responseDict objectForKey:#"lat"] doubleValue];
double dlng = [[responseDict objectForKey:#"lng"] doubleValue];
[[sharedState ulatitude] addObject:[NSString stringWithFormat:#"%f",dlat]];
[[sharedState ulongitude] addObject:[NSString stringWithFormat:#"%f",dlng]];
[[sharedState uaccuracy] addObject:[responseDict objectForKey:#"rad"]];
[[sharedState uplacename] addObject:[responseDict objectForKey:#"place_name"]];
}

You always need to initialize your arrays. You should do somewhere before you try to add something to them:
arrayName = [[NSMutableArray alloc] init];
otherwise you'll always get error because they have not been initialized.
In your case you should override your LocationData init function like this:
- (instancetype)init {
self = [super init];
if (self) {
self.yourArrayName = [[NSMutableArray alloc] init];
// And so on....
}
return self;
}

You need to initialize your object properly. Basically your member variables ("ivars") are pointing to nothing ("nil").
This initializer added to your .m file code do the job.
-(instancetype)init {
if ((self = [super init])) {
self.accuracy = [NSMutableArray array];
self.latitude = [NSMutableArray array];
self.longitude = [NSMutableArray array];
self.uplacename = [NSMutableArray array];
}
return self;
}
As a singleton pattern, I'd prefer the following:
+ (LocationData*) sharedStateInstance {
static LocationData *sharedStateInstance = nil;
static dispatch_once_t onceToken = 0;
dispatch_once(&onceToken, ^{
sharedStateInstance = [[LocationData alloc] init];
});
return sharedStateInstance;
}
Although singletons might not be as bad they are often said to be, I don't thing that this is a good usage for them. Your specific problem has nothing to do with that design choice, though.

Try this code. Write getters for your NSMutableArrays.
#import <Foundation/Foundation.h>
#interface LocationData : NSObject
#property (nonatomic, retain) NSMutableArray *ulatitude;
#property (nonatomic, retain) NSMutableArray *ulongitude;
#property (nonatomic, retain) NSMutableArray *uaccuracy;
#property (nonatomic, retain) NSMutableArray *uplacename;
+ (LocationData*) sharedStateInstance;
#end
#import "LocationData.h"
#implementation LocationData
#synthesize uaccuracy = _uaccuracy;
#synthesize ulatitude = _ulatitude;
#synthesize ulongitude = _ulongitude;
+ (LocationData*) sharedStateInstance {
static LocationData *sharedStateInstance;
#synchronized(self) {
if(!sharedStateInstance) {
sharedStateInstance = [[LocationData alloc] init];
}
}
return sharedStateInstance;
}
-(NSMutableArray*)uaccuracy
{
if(_uaccuracy == nil)
{
_uaccuracy = [[NSMutableArray alloc]init];
}
return uaccuracy;
}
-(NSMutableArray*)ulongitude
{
if(_ulongitude == nil)
{
_ulongitude = [[NSMutableArray alloc]init];
}
return ulongitude;
}
-(NSMutableArray*)ulatitude
{
if(_ulatitude == nil)
{
_ulatitude = [[NSMutableArray alloc]init];
}
return ulatitude;
}
-(NSMutableArray*)uplacename
{
if(_uplacename == nil)
{
_uplacename = [[NSMutableArray alloc]init];
}
return uplacename;
}
#end

you don't allocate/init any array...
you can create them in your singleton creation method
+ (LocationData*) sharedStateInstance {
static LocationData *sharedStateInstance;
#synchronized(self) {
if(!sharedStateInstance) {
sharedStateInstance = [[LocationData alloc] init];
sharedStateInstance.ulatitude = [[NSMutableArray alloc] init];
// (add others...)
}
}
return sharedStateInstance;
}

Replace your LocationData.m file with below code , this will work . As you have to alloc and init the array then only you can add object in array
+ (LocationData*) sharedStateInstance {
static LocationData *sharedStateInstance;
#synchronized(self) {
if(!sharedStateInstance) {
sharedStateInstance = [[LocationData alloc] init];
uaccuracy = [[NSMutableArray alloc]init];
ulatitude = [[NSMutableArray alloc]init];
ulongitude = [[NSMutableArray alloc]init];
uplacename = [[NSMutableArray alloc]init];
}
}
return sharedStateInstance;
}

Related

Objective-C Defining a Global Array for use by several ViewControllers

I've been trying to implement a global NSMutableArray from what I think to be a singleton class that I've implemented.
I can enter ViewController # 2, add and remove objects to the array.
However, when I leave ViewController #2 and come back, the data does not persist, and I have an array with 0 objects.
What do you think I'm doing wrong?
.h
// GlobalArray.h
#interface GlobalArray : NSObject{
NSMutableArray* globalArray;
}
+(void)initialize;
.m
#import "GlobalArray.h"
#implementation GlobalArray
static GlobalArray* sharedGlobalArray;
NSMutableArray* globalArray;
+(void)initialize{
static BOOL initalized = NO;
if(!initalized){
initalized = YES;
sharedGlobalArray = [[GlobalArray alloc] init];
}
}
- (id)init{
if (self = [super init]) {
if (!globalArray) {
globalArray = [[NSMutableArray alloc] init];
}
}
return self;
}
View Controller #2
GlobalArray* myGlobalArray;
myGlobalArray = [[GlobalArray alloc] init];
//Various add and remove code
Thank you for your input.
Following is best approach to share data Globally at Application level. Singleton Class is a key. Singleton is only initialised once, rest of times shared data is returned.
#interface Singleton : NSObject
#property (nonatomic, retain) NSMutableArray * globalArray;
+(Singleton*)singleton;
#end
#implementation Singleton
#synthesize globalArray;
+(Singleton *)singleton {
static dispatch_once_t pred;
static Singleton *shared = nil;
dispatch_once(&pred, ^{
shared = [[Singleton alloc] init];
shared.globalArray = [[NSMutableArray alloc]init];
});
return shared;
}
#end
Following is the way to access/use shared data.
NSMutableArray * sharedData = [Singleton singleton].globalArray;
You create separate instance of GlobalArray in your ViewController#2 with this code:
GlobalArray* myGlobalArray;
myGlobalArray = [[GlobalArray alloc] init];
Instead, you should create accessor method to return your shared instance, something like this:
// GlobalArray.h
#interface GlobalArray : NSObject{
NSMutableArray* globalArray;
}
+(void)initialize;
+(GlobalArray*)sharedInstance;
with implementation:
// GlobalArray.m
// ... your existing code
// accessor method
+(GlobalArray*)sharedInstance
{
return sharedGlobalArray;
}
and then call it from your ViewController#2:
GlobalArray* myGlobalArray = [GlobalArray sharedInstance];
However, using global variables to transfer data between view controllers is bad practice; I suggest you to use more safe methods, create a delegate, for example.
To create a shared global array, if that's really what you want, just put this in the header file:
extern NSMutableArray *myGlobalArray;
and this in your main source file:
NSMutableArray *myGlobalArray;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
myGlobalArray = [NSMutableArray new];
}
Use this code for set and get the array views, for adding and removing do it separate in controller itself.
// GlobalArray.h
#interface GlobalArray : NSObject
#property (nonatomic, strong) NSMutableArray* globalArray;
+ (id)sharedManager;
-(NSMutableArray *) getGlobalArray;
-(void) setGlobalArray:(NSMutableArray *)array;
#end
/*-----------------------------------------*/
#import "GlobalArray.h"
#implementation GlobalArray
+ (id)sharedManager {
static GlobalArray *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (id)init{
if (self = [super init]) {
if (!globalArray) {
globalArray = [[NSMutableArray alloc] init];
}
}
return self;
}
-(NSMutableArray *) getGlobalArray{
return self.globalArray;
}
-(void) setGlobalArray:(NSMutableArray *)array{
_globalArray = globalArray;
}
#end
-------------------------
//get array
NSArray * array = [[GlobalArray sharedManager] getGlobalArray];
//set array
[[GlobalArray sharedManager] setGlobalArray:array]
-------------------------

How to store the json values once up to life time of app removing from mobile?

MY CODE
.h
#interface fileHandler : NSObject
#property NSMutableArray *arrCategoryList;
#property NSMutableDictionary *dicCategoryList;
#property NSMutableDictionary *dicAllSubCategoryList;
#property NSMutableDictionary *dicProductList;
+(fileHandler *)getDataHandler;
-(void)categoryStorage:(NSMutableArray *)arrCategory :(NSMutableDictionary *)dicCategory :(NSMutableDictionary *)dicSubCategory;
.m
static fileHandler *internalInstance=Nil;
static dispatch_once_t internalOnceToken=0;
#implementation fileHandler
-(id)init
{
self=[super init];
[self allocateMemory];
return self;
}
+(fileHandler *)getDataHandler
{
dispatch_once(&internalOnceToken,^{
internalInstance = [[fileHandler alloc] init];
if(internalInstance) {
NSLog(#"Internal instance created: %#", internalInstance);
}
});
if(internalOnceToken == -1) {
NSLog(#"Internal instance exists: %#", internalInstance);
}
return internalInstance;
}
-(void)allocateMemory
{ NSLog(#"incoming");
self.arrCategoryList=[[NSMutableArray alloc]init];
self.dicCategoryList=[[NSMutableDictionary alloc]init];
self.dicAllSubCategoryList=[[NSMutableDictionary alloc]init];
self.dicProductList=[[NSMutableDictionary alloc]init];
}
-(void)categoryStorage:(NSMutableArray *)arrCategory :(NSMutableDictionary *)dicCategory :(NSMutableDictionary *)dicSubCategory
{
// arrCategory =[[NSMutableArray alloc]init];
self.arrCategoryList=arrCategory;
// [self.arrCategoryList addObject:arrCategory];
// NSLog(#"inside data handler file==%#",self.arrCategoryList);
// dicCategory=[[NSMutableDictionary alloc]init];
self.dicCategoryList=dicCategory;
// dicSubCategory=[[NSMutableDictionary alloc]init];
self.dicAllSubCategoryList=dicSubCategory;
}
Here i have created the static thread and then i am trying to store all the values in to mentioned array and dictionary in other class ,once i have stored the values here then i have to access those data to any where in my app.
Is it Possible?
Is it Right Way?
Another classFile
.m
Like this i am trying to store the values in NSobject Class Array.
for(NSDictionary *DicHoleCategories in ArrCategory)
{
NSMutableDictionary *DicAllValues=[[NSMutableDictionary alloc]init];
[DicAllValues setObject:[[DicHoleCategories objectForKey:#"name"] length] !=0?[DicHoleCategories objectForKey:#"name"] :#"" forKey:#"name"];
StrName=[DicHoleCategories objectForKey:#"image"];
[DicAllValues setObject:[DicHoleCategories objectForKey:#"subcategory"] forKey:#"subcategory"];
if(StrName!=nil)
{
subimages=[NSString stringWithFormat:LocalImage"%#",StrName];
[DicAllValues setObject:subimages forKey:#"image"];
[arrImages addObject:[DicAllValues objectForKey:#"image"]];
}
[ArrName addObject:DicAllValues];
[arrSubCategory addObject:[DicAllValues objectForKey:#"subcategory"]];
[dicSubCategory setObject:[DicAllValues objectForKey:#"subcategory"] forKey:#"subcategory"];
[dicAllValues setObject:dicAllValues forKey:#"hole"];
}
[file categoryStorage:ArrCategory :dicAllValues :dicSubCategory];
OUTPUT:
Collection <__NSArrayM: 0x7f8c33c7b220> was mutated while being enumerated.'

iOS: Viewcontroller subclassed from MWPhotoBrowser-images are not loaded

I am trying to include MWPhotoBrowser in my project
When its used as given in the sample it working fine.
But when a new viewcontroller is subclassed from MWPhotoBrowser, photos are not loaded except empty black theme.
Delegate methods are not getting called. As the controller is subclass of MWPhotoBrowser, I assume there is no need to set it explicitly.
Storyboard is used and the nib class in it is set.
.h file
#interface MDRPhotoViewerController : MWPhotoBrowser
{
NSMutableArray *_selections;
}
#property (nonatomic, strong) NSMutableArray *photos;
#property (nonatomic, strong) NSMutableArray *thumbs;
#property (nonatomic, strong) NSMutableArray *assets;
#property (nonatomic, strong) NSMutableIndexSet *optionIndices;
#property (nonatomic, strong) UITableView *tableView;
#property (nonatomic, strong) ALAssetsLibrary *ALAssetsLibrary;
- (void)loadAssets;
#end
**.m file **
- (void)viewWillAppear:(BOOL)animated
{
NSMutableArray *photos = [[NSMutableArray alloc] init];
NSMutableArray *thumbs = [[NSMutableArray alloc] init];
//mwphotobrowser options setup
BOOL displayActionButton = YES;
BOOL displaySelectionButtons = NO;
BOOL displayNavArrows = NO;
BOOL enableGrid = YES;
BOOL startOnGrid = NO;
BOOL autoPlayOnAppear = NO;
//loading data
NSArray *photosDataArray = [MDRDataController GetPhotos]; //creating array
for (NSString *urlString in photosDataArray) { //Formating the data source for images
NSString *urlFullString = [NSString stringWithFormat:#"%#%#",KBASEURL,urlString];
//Photos
[photos addObject:[MWPhoto photoWithURL:[NSURL URLWithString:urlFullString]]];
//thumbs
[thumbs addObject:[MWPhoto photoWithURL:[NSURL URLWithString:urlFullString]]];
}
// Options
self.photos = photos;
self.thumbs = thumbs;
// Create browser
self.displayActionButton = displayActionButton;
self.displayNavArrows = displayNavArrows;
self.displaySelectionButtons = displaySelectionButtons;
self.alwaysShowControls = displaySelectionButtons;
self.zoomPhotosToFill = YES;
self.enableGrid = enableGrid;
self.startOnGrid = startOnGrid;
self.enableSwipeToDismiss = NO;
self.autoPlayOnAppear = autoPlayOnAppear;
[self setCurrentPhotoIndex:0];
// Test custom selection images
// browser.customImageSelectedIconName = #"ImageSelected.png";
// browser.customImageSelectedSmallIconName = #"ImageSelectedSmall.png";
// Reset selections
if (displaySelectionButtons) {
_selections = [NSMutableArray new];
for (int i = 0; i < photos.count; i++) {
[_selections addObject:[NSNumber numberWithBool:NO]];
}
}
self.title = #"Phots";
//[self reloadData];
}
Debugging performed
Considering the image template of mwphotobrowser, tried reloading the code.
Shifted the code between viewwillappear and viewdidload.
Doesn't MWPhotoBrowser support this way or am i doing it wrong ?
For those who stumble upon this later...
If you look at MWPhotoBrowser.m you'll see various initializers:
- (id)init {
if ((self = [super init])) {
[self _initialisation];
}
return self;
}
- (id)initWithDelegate:(id <MWPhotoBrowserDelegate>)delegate {
if ((self = [self init])) {
_delegate = delegate;
}
return self;
}
- (id)initWithPhotos:(NSArray *)photosArray {
if ((self = [self init])) {
_fixedPhotosArray = photosArray;
}
return self;
}
- (id)initWithCoder:(NSCoder *)decoder {
if ((self = [super initWithCoder:decoder])) {
[self _initialisation];
}
return self;
}
The problem is there's no awakeFromNib initializer. Simplest solution is to fork the project and create the awakeFromNib initializer.

Parse JSON to a tree structure

I'm working on a project that requires a tableView list of categorized grocery items. Each category can have n depth. The JSON response from the API looks like this.
"items":[
{
"id":"5366f8d3e4b0e44dc2d4a6fb",
"name":"String Cheese"
"description":"Sargento String Cheese",
"categorization":[
[
"Dairy",
"Cheese"
]
]
},
{
"id":"5366f8d3e4b0e44dc2d1a6fb",
"name":"Budlight 6-pk"
"description":"Budlight 12-pk",
"categorization":[
[
"Beverages",
"Alcohol",
"Beer"
]
]
}
]
Right now I'm creating Item objects from the item dictionaries and storing them in a mutable array like below.
NSArray *itemsArray = [response objectForKey:items];
NSMutableArray *itemsMutableArray = [[NSMutableArray alloc] init];
for(NSDictionary *itemDict in itemsArray){
Item *itemObj = [[Item alloc] initWithDictionary:itemDict]
[itemsMutableArray addObject:itemObj];
}
I would like to loop through itemsMutableArray and create a tree data structure that has a path from the root to each of the items. Then, I would like to be able to use the tree as a datasource for tableViews in each level of category.
Here's what my Item class header looks like.
#interface Item : NSObject
#property (nonatomic, strong) NSString *id;
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *description;
#property (nonatomic, strong) NSArray *categorization;
#end
...and the implementation
#import "Item.h"
#implementation Item
- (id)initWithDictionary:(NSDictionary *)objDictionary{
if (self = [super init]) {
self.id = [objDictionary valueForKey:#"id"];
self.name = [objDictionary valueForKey:#"name"];
self.description = [objDictionary valueForKey:#"description"];
self.categorization = [objDictionary valueForKey:#"categorization"];
}
return self;
}
#end
I am not very familiar with tree data structures and recursion. I would greatly appreciate any help on how to approach this. Thanks!
If you need simple node tree data structure. How about this way?
Hope this little help.
Header
#interface ItemCategory : NSObject
#property (nonatomic, strong) NSString *name;
#property (nonatomic) ItemCategory *parent;
#property (nonatomic, strong) NSMutableArray *children;
-(id)initWithName:(NSString *)n parent:(ItemCategory *)p;
#end
#interface CategoryTree : NSObject
#property (nonatomic, strong) ItemCategory *root;
-(ItemCategory *)_getChildCategory:(ItemCategory *)category name:(NSString *)name;
-(ItemCategory *)_addChildCategory:(ItemCategory *)category name:(NSString *)name;
-(void)_dumpCategory:(ItemCategory *)category depth:(int)depth;
-(void)dump;
-(ItemCategory *)getCategory:(NSArray *)arr;
-(void)addCategory:(NSArray *)arr;
#end
Source
#implementation CategoryTree
#synthesize root;
-(id)init {
if (self = [super init]) {
root = [[ItemCategory alloc] initWithName:#"root" parent:nil];
}
return self;
}
-(ItemCategory *)_getChildCategory:(ItemCategory *)category name:(NSString *)name {
for (ItemCategory *child in category.children)
if ([child.name isEqualToString:name])
return child;
return nil;
}
-(ItemCategory *)_addChildCategory:(ItemCategory *)category name:(NSString *)name {
ItemCategory *child = [self _getChildCategory:category name:name];
if (child)
return child;
child = [[ItemCategory alloc] initWithName:name parent:category];
[category.children addObject:child];
return child;
}
-(void)_dumpCategory:(ItemCategory *)category depth:(int)depth{
NSString *parentStr = #"";
ItemCategory *parent = category.parent;
while (parent) {
parentStr = [NSString stringWithFormat:#"%#%#%#", parent.name, parentStr.length > 0 ? #">" : #"", parentStr];
parent = parent.parent;
}
NSLog(#"%#%#%#", parentStr, parentStr.length > 0 ? #">" : #"", category.name);
for (ItemCategory *child in category.children) {
[self _dumpCategory:child depth:depth + 1];
}
}
-(void)dump {
[self _dumpCategory:root depth:0];
}
-(ItemCategory *)getCategory:(NSArray *)arr {
ItemCategory *category = root;
for (NSString *categoryName in arr) {
category = [self _getChildCategory:category name:categoryName];
if (!category)
return nil;
}
return category;
}
-(void)addCategory:(NSArray *)arr {
if ([self getCategory:arr])
return;
ItemCategory *category = root;
for (NSString *categoryName in arr) {
ItemCategory *childCategory = [self _getChildCategory:category name:categoryName];
if (!childCategory) {
childCategory = [self _addChildCategory:category name:categoryName];
}
category = childCategory;
}
}
#end
Usage
CategoryTree *tree = [[CategoryTree alloc] init];
[tree addCategory:#[#"Dairy", #"Cheese"]];
[tree addCategory:#[#"Dairy", #"Milk"]];
[tree addCategory:#[#"Beverages", #"Alcohol", #"Beer"]];
[tree addCategory:#[#"Beverages", #"Alcohol", #"Wine"]];
[tree addCategory:#[#"Beverages", #"Non-Alcohol", #"Cola"]];
[tree dump];
Result
root
root>Dairy
root>Dairy>Cheese
root>Dairy>Milk
root>Beverages
root>Beverages>Alcohol
root>Beverages>Alcohol>Beer
root>Beverages>Alcohol>Wine
root>Beverages>Non-Alcohol
root>Beverages>Non-Alcohol>Cola
well I have found a way to implement what you need. I do not know how optimised it is since i do not how many items you'll be receiving . The implementation is given below.
You need to start with adding this dictionary in Item.h #property (nonatomic, strong) NSMutableDictionary *catTree;
Next do this to get the tree
[itemsMutableArray enumerateObjectsUsingBlock:^(Item *itm, NSUInteger i,BOOL *stop){
itm.catTree = [NSMutableDictionary dictionary];
NSString *dairy = #"",*beverage = #"";
for (NSArray *catArray in itm.categorization) {
/*
Everything below is written assuming the format of the JSON will be "as-is"
*/
if ([catArray containsObject:#"Dairy"]) {
//Take everything except Dairy
NSArray *stripedArray = [catArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF != \"Dairy\""]];
int i = 0;
//Loop through the array to get any sub categories.
while (i < stripedArray.count) {
dairy = [dairy stringByAppendingString:[NSString stringWithFormat:(i == stripedArray.count-1)?#"%# ":#"%#->",stripedArray[i]]]; //Space at the end to account for similar entry in the same category for e.g two dairy products.
i++;
}
} else if ([catArray containsObject:#"Beverages"]) {
NSArray *stripedArray = [catArray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF != \"Beverages\""]];
int i = 0;
while (i < stripedArray.count) {
beverage = [beverage stringByAppendingString:[NSString stringWithFormat:(i == stripedArray.count-1)?#"%# ":#"%#->",stripedArray[i]]];
i++;
}
}
}
//Set the category tree for every item using a dictionary
[itm.catTree setValue:dairy forKey:#"Dairy"];
[itm.catTree setValue:beverage forKey:#"Beverage"];
NSLog(#"%#",itm.catTree);
}];
the above code gives the following output for your json
{
Beverage = "";
Dairy = "Cheese ";
}
{
Beverage = "Alcohol->Beer ";
Dairy = "";
}
For multiple beverages
{
Beverage = "Alcohol->Beer Alcohol->Wine->Red Soda->Coke ";
Dairy = "";
}
Hope this helps.

Saving NSDictionary (with custom classes) to NSUserDefaults [duplicate]

This question already has answers here:
How to store custom objects in NSUserDefaults
(7 answers)
Closed 9 years ago.
I am trying to save an NSDictionary to my NSUserDefualts.
The dictionary consists of 3 different custom classes.
#interface PastOrder : NSObject <NSCoding>
{
NSDate *timeIn;
NSDate *timeOut;
NSString *status;
NSMutableArray *myItems;
}
#property (nonatomic, retain) NSDate *timeIn;
#property (nonatomic, retain) NSDate *timeOut;
#property (nonatomic, retain) NSString *status;
#property (nonatomic, retain) NSMutableArray *myItems;
#end
#implementation PastOrder
#synthesize timeIn, timeOut, status, myItems;
#define PastOrderTimeInKey #"PastOrderTimeInKey"
#define PastOrderTimeOutKey #"PastOrderTimeOutKey"
#define PastOrderStatusKey #"PastOrderStatusKey"
#define PastOrderMyItemsKey #"PastOrderMyItemsKey"
-(id)initWithCoder:(NSCoder*)decoder
{
self = [super init];
if(self)
{
self.timeIn = [decoder decodeObjectForKey:PastOrderTimeInKey];
self.timeOut = [decoder decodeObjectForKey:PastOrderTimeOutKey];
self.status = [decoder decodeObjectForKey:PastOrderStatusKey];
self.myItems = [decoder decodeObjectForKey:PastOrderMyItemsKey];
}
return self;
}
-(void)encodeWithCoder:(NSCoder*)encoder
{
[encoder encodeObject:self.timeIn forKey:PastOrderTimeInKey];
[encoder encodeObject:self.timeOut forKey:PastOrderTimeOutKey];
[encoder encodeObject:self.status forKey:PastOrderStatusKey];
[encoder encodeObject:self.myItems forKey:PastOrderMyItemsKey];
}
-(void)dealloc
{
self.timeIn = nil;
self.timeOut = nil;
self.status = nil;
self.myItems = nil;
}
#end
#interface PastOrderItem : NSObject <NSCoding>
{
NSNumber *itemID;
NSString *status;
NSMutableArray *itemChoices;
}
#property (nonatomic, retain) NSNumber *itemID;
#property (nonatomic, retain) NSString *status;
#property (nonatomic, retain) NSMutableArray *itemChoices;
#end
#implementation PastOrderItem
#synthesize itemID,status,itemChoices;
#define PastOrderItemItemIDKey #"PastOrderItemItemIDKey"
#define PastOrderItemStatusKey #"PastOrderItemStatusKey"
#define PastOrderItemItemChoicesKey #"PastOrderItemItemChoicesKey"
-(id)initWithCoder:(NSCoder*)decoder
{
self = [super init];
if(self)
{
self.itemID = [decoder decodeObjectForKey:PastOrderItemItemIDKey];
self.itemChoices = [decoder decodeObjectForKey:PastOrderItemItemChoicesKey];
self.status = [decoder decodeObjectForKey:PastOrderItemStatusKey];
}
return self;
}
-(void)encodeWithCoder:(NSCoder*)encoder
{
[encoder encodeObject:self.itemID forKey:PastOrderItemItemIDKey];
[encoder encodeObject:self.itemChoices forKey:PastOrderItemItemChoicesKey];
[encoder encodeObject:self.status forKey:PastOrderItemStatusKey];
}
-(void)dealloc
{
self.itemID = nil;
self.itemChoices = nil;
self.status = nil;
}
#end
#interface PastOrderItemChoice : NSObject <NSCoding>
{
NSNumber *modifierID;
NSNumber *modifierChoice;
}
#property (nonatomic, retain) NSNumber *modifierID;
#property (nonatomic, retain) NSNumber *modifierChoice;
#end
#implementation PastOrderItemChoice
#synthesize modifierID, modifierChoice;
#define PastOrderItemChoiceModifierIDKey #"PastOrderItemChoiceModifierIDKey"
#define PastOrderItemChoiceModifierChoiceKey #"PastOrderItemChoiceModifierChoiceKey"
-(id)initWithCoder:(NSCoder*)decoder
{
self = [super init];
if(self)
{
self.modifierID = [decoder decodeObjectForKey:PastOrderItemChoiceModifierIDKey];
self.modifierChoice = [decoder decodeObjectForKey:PastOrderItemChoiceModifierChoiceKey];
}
return self;
}
-(void)encodeWithCoder:(NSCoder*)encoder
{
[encoder encodeObject:self.modifierID forKey:PastOrderItemChoiceModifierIDKey];
[encoder encodeObject:self.modifierChoice forKey:PastOrderItemChoiceModifierChoiceKey];
}
-(void)dealloc
{
self.modifierID = nil;
self.modifierChoice = nil;
}
#end
Those are the three classes that will be inside this NSDictionary.
Here is how I Load and Save it.
-(void)SavePrefs
{
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSData* data=[NSKeyedArchiver archivedDataWithRootObject:self.myDictionary];
[prefs setObject:data forKey:#"SavedOrders"];
[prefs synchronize];
}
- (id)init
{
self = [super init];
if (self)
{
NSData* data = [[NSUserDefaults standardUserDefaults] objectForKey:#"SavedOrders"];
self.myDictionary = [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
return self;
}
I have experimented with the code a bit, and best I have to far, is that when I save the dictionary, it was 135 bytes, same as when I loaded it, but it still didnt fill the dictionary up. So I am at a loss.
Your code seems to be good. I can't find a mistake so try to change line:
self.myDictionary = [NSKeyedUnarchiver unarchiveObjectWithData:data];
to
id unknownObject = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(#"%#",[unknownObject class]);
And look # the console. Maybe you should also try casting if the output will be dictionary. So try to change this to:
self.myDictionary = (NSDictionary*)[NSKeyedUnarchiver unarchiveObjectWithData:data];
EDIT
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:#"object1",#"key1",#"object2",#"key2",#"object3",#"key3", nil];
NSLog(#"before: %#",dictionary);
NSData *myData = [NSKeyedArchiver archivedDataWithRootObject:dictionary];
NSDictionary *myDictionary = (NSDictionary*) [NSKeyedUnarchiver unarchiveObjectWithData:myData];
NSLog(#"after: %#",myDictionary);
Output:
2013-11-13 14:32:31.369 DemoM[175:60b] before: {
key1 = object1;
key2 = object2;
key3 = object3;
}
2013-11-13 14:32:31.372 DemoM[175:60b] after: {
key1 = object1;
key2 = object2;
key3 = object3;
}

Resources