I'm trying to decode my object from NSUserdefaults.
If I debug the function initWithCoder, it's executed okay, but when I look at my object that was decoded, all the properties are nil.
My initWithCoder function:
-(id)initWithCoder:(NSCoder *)decoder
{
self = [super init];
if ( self != nil )
{
//decode the properties
self.SGTIN = [decoder decodeObjectForKey:#"SGTIN"];
self.GTIN = [decoder decodeObjectForKey:#"GTIN"];
self.SerialNumber = [decoder decodeObjectForKey:#"SerialNumber"];
self.Product = [decoder decodeObjectForKey:#"Product"];
self.DateTimeScanned = [decoder decodeObjectForKey:#"DateTimeScanned"];
self.Loaded = [decoder decodeBoolForKey:#"Loaded"];
}
return self;
}
My code to decode the object:
NSDictionary *tempDictionary = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
NSArray *tempArray =[tempDictionary allKeys];
for (NSString *key in tempArray){
if ([key length] > 8) {
if ([[key substringToIndex:8] isEqualToString:#"INXITEM:"]) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *encodedObject = [defaults objectForKey:key];
ScannedItem *SITemp = (ScannedItem *)[NSKeyedUnarchiver unarchiveObjectWithData:encodedObject];
}
}
}
Just found it...
It was because the property Loaded (bool) is a new property and the object that was saved in NSUserDefaults didn't contain that property.
Cleared my NSUserDefaults and everything is okay now...
You aren't calling the correct superclass method.
It should be
self = [super initWithCoder:decoder];
Try using - (void)encodeWithCoder:(NSCoder *)coder method in ScannedItem object class
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:self.SGTIN forKey:#"SGTIN"];
//encode all properties
}
Related
I apologize for duplicate Question . I am new in ios . I want to store custom objects in userdefaults. I am using objective-c .
Thanks in advance
First you create custom class Like below.
CustomObject.h
#import <Foundation/Foundation.h>
#interface CustomObject : NSObject<NSCoding>
#property(strong,nonatomic)NSString *name;
#end
CustomObject.m
#import "CustomObject.h"
#implementation CustomObject
- (void)encodeWithCoder:(NSCoder *)encoder {
//Encode properties, other class variables, etc
[encoder encodeObject:self.name forKey:#"name"];
}
- (id)initWithCoder:(NSCoder *)decoder {
if((self = [super init])) {
//decode properties, other class vars
self.name = [decoder decodeObjectForKey:#"name"];
}
return self;
}
Then create CustomObject class object and store in NSUserDefaults
Stored your object like this
CustomObject *object =[CustomObject new];
object.name = #"test";
NSMutableArray *arr = [[NSMutableArray alloc]init];
[arr addObject:object];
NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:arr];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:encodedObject forKey:#"storeObject"];
[defaults synchronize];
get custom object from NSUserDefaults like this
NSData *storedEncodedObject = [defaults objectForKey:#"storeObject"];
NSArray *arrStoreObject = [NSKeyedUnarchiver unarchiveObjectWithData:storedEncodedObject];
for (int i=0; i<arrStoreObject.count; i++)
{
CustomObject *storedObject = [arrStoreObject objectAtIndex:i];
NSLog(#"%#",storedObject.name);
}
In Swift 3
// set a value for key
let encodedData = NSKeyedArchiver.archivedData(withRootObject: #YOUR OBJECT#)
UserDefaults.standard.set(encodedData, forKey: #YOUR KEY#)
// retrieving a value for a key
if let data = UserDefaults.standard.data(forKey: #YOUR KEY#),
let obj = NSKeyedUnarchiver.unarchiveObject(with: data) as? #YOUR OBJECT# {
} else {
print("There is an issue")
}
this is my first post, so please be kind and constructive with me.
Im trying to share an array of custom objects with my Today Extension via NSuserDefauls.
I know that I can't put custom objects in NSUserdefaults so i coded them by NSKeyedArchiver - NSKeyedUnarchiver to transform them in NSData.
This is my custom class
#import "Counter.h"
#implementation Counter
#dynamic city;
#dynamic address;
#dynamic event;
#dynamic date;
#dynamic time;
#dynamic counter;
#dynamic latitude;
#dynamic longitude;
- (void)encodeWithCoder:(NSCoder *)encoder
{
//Encode properties, other class variables, etc
[encoder encodeObject:self.city forKey:#"city"];
[encoder encodeObject:self.address forKey:#"address"];
[encoder encodeObject:self.event forKey:#"event"];
[encoder encodeObject:self.date forKey:#"date"];
[encoder encodeObject:self.time forKey:#"time"];
[encoder encodeObject:self.counter forKey:#"counter"];
[encoder encodeObject:self.latitude forKey:#"latitude"];
[encoder encodeObject:self.longitude forKey:#"longitude"];
}
- (id)initWithCoder:(NSCoder *)decoder
{
self = [super init];
if( self != nil )
{
self.city = [decoder decodeObjectForKey:#"city"];
self.address = [decoder decodeObjectForKey:#"address"];
self.event = [decoder decodeObjectForKey:#"event"];
self.date = [decoder decodeObjectForKey:#"date"];
self.time = [decoder decodeObjectForKey:#"time"];
self.counter = [decoder decodeObjectForKey:#"counter"];
self.latitude = [decoder decodeObjectForKey:#"latitude"];
self.longitude = [decoder decodeObjectForKey:#"longitude"] ;
}
return self;
}
#end
This is the coding function inside the app.
- (void)saveInUserDefault {
//Counter is my Custom class
//I'm Using MagicalRecord
NSArray *array = [Counter MR_findAllSortedBy:#"date" ascending:NO];
NSMutableArray *defaultMutableArray = [NSMutableArray arrayWithCapacity:array.count];
NSUserDefaults *defaultArray = [[NSUserDefaults alloc] initWithSuiteName:#"group.LorenzoLoRe.Everycount"];
for (Counter *cnt in array) {
NSData *dataToDefault = [NSKeyedArchiver archivedDataWithRootObject:cnt];
[defaultMutableArray addObject:dataToDefault];
}
[defaultArray setObject:defaultMutableArray forKey:#"counts"];
[defaultArray synchronize];
}
At this point looking at the log, data seems to be write in NSUserDefaults
And here i try to retrive the data in the Extension
-
(void)takeLastCounts {
NSUserDefaults *defaultArray = [[NSUserDefaults alloc] initWithSuiteName:#"group.company.product"];
[defaultArray synchronize];
NSArray *dummyArray = [defaultArray arrayForKey:#"counts"];;
NSMutableArray *arrayFromDefault= NULL;
Counter *cter = nil;
for (NSData *data in dummyArray) {
cter = [NSKeyedUnarchiver unarchiveObjectWithData:data];
[arrayFromDefault addObject:cter];
}
self.cnt_1 = arrayFromDefault[0];
self.cnt_2 = arrayFromDefault[1];
NSDateFormatter * formatter = [[NSDateFormatter alloc] init];
[formatter setDateStyle:NSDateFormatterShortStyle];
self.eventLabel.text = self.cnt_1.event;
self.dateLabel.text = [formatter stringFromDate:self.cnt_1.date];
self.counterLabel.text = self.cnt_1.counter.stringValue;
self.timeLabel.text = self.cnt_1.time;
}
When i try to execute the Extension, theres no errors but just empty Arrays.
In particular NSArray *dummyArray = [defaultArray arrayForKey:#"counts"]; seems to be empty, so somewhere the data don't flows.
What I'm doing wrong??
Any kind of tips will be ver appreciate.
I am trying to save a NSMutableArray when the app shuts down, and then load it on startup to a tableview.
Here's my code:
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:_savedText forKey:#"savedText"];
}
- (id)initWithCoder:(NSCoder *)decoder {
if((self = [super initWithCoder:decoder])) {
_savedText = [decoder decodeObjectForKey:#"savedText"];
}
return self;
}
I use this code to save the MutableArray:
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:_savedText];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"savedText"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
And this to load it:
- (void)viewDidLoad {
NSData *savedData = [[NSUserDefaults standardUserDefaults] objectForKey:#"savedText"];
NSMutableArray *array = [NSKeyedUnarchiver unarchiveObjectWithData:savedData];
array = [_savedText copy];
}
There are no errors, but the table view is empty on startup. Have I forgotten something?
You should read the data from the defaults and set to _savedText object. You are reading the data into var named array and then assigning array = [_savedText copy]. This will probably be nil. It should be the reverse.
- (void)viewDidLoad {
NSData *savedData = [[NSUserDefaults standardUserDefaults] objectForKey:#"savedText"];
NSMutableArray *array = [NSKeyedUnarchiver unarchiveObjectWithData:savedData];
_savedText = [array copy];
}
I want to persist an NSDictionary, filled with custom Object to disc:
NSDictionary *menuList = [[NSMutableDictionary alloc]initWithDictionary:xmlParser.items];
//here the "Menu List"`s Object are filled correctly
//persisting them to disc:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *directory = [paths objectAtIndex:0];
NSString *fileName = [NSString stringWithUTF8String:MENU_LIST_NAME];
NSString *filePath = [directory stringByAppendingPathComponent:fileName];
//saving using NSKeyedArchiver
NSData* archiveData = [NSKeyedArchiver archivedDataWithRootObject:menuList];
[archiveData writeToFile:filePath options:NSDataWritingAtomic error:nil];
//here the NSDictionary has the correct amount of Objects, but the Objects` class members are partially empty or nil
NSData *data = [NSData dataWithContentsOfFile:filePath];
NSDictionary *theMenu = (NSDictionary*)[NSKeyedUnarchiver unarchiveObjectWithData:data];
Here the .m of the Custom Object (the kind of object stored in the NSDictionary)
- (id)initWithTitle:(NSString*)tTitle level:(NSString*)tLevel state:(NSString*)tState visible:(BOOL)tVisible link:(NSString*)tLink linkType:(NSString*)tLinkType anId:(NSString*)tId {
if ((self = [super init])) {
self.anId = tId;
self.title = tTitle;
self.level = tLevel;
self.state = tState;
self.visible = tVisible;
self.link = tLink;
self.linkType = tLinkType;
}
return self;
}
- (void) encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.anId forKey:#"anId"];
[encoder encodeObject:self.level forKey:#"level"];
[encoder encodeObject:self.state forKey:#"state"];
[encoder encodeBool:self.visible forKey:#"visible"];
[encoder encodeObject:self.title forKey:#"title"];
[encoder encodeObject:self.link forKey:#"link"];
[encoder encodeObject:self.linkType forKey:#"linkType"];
}
- (id)initWithCoder:(NSCoder *)decoder {
if(self == [super init]){
self.anId = [decoder decodeObjectForKey:#"anId"];
self.level = [decoder decodeObjectForKey:#"level"];
self.state = [decoder decodeObjectForKey:#"state"];
self.visible = [decoder decodeBoolForKey:#"visible"];
self.title = [decoder decodeObjectForKey:#"title"];
self.link = [decoder decodeObjectForKey:#"link"];
self.linkType = [decoder decodeObjectForKey:#"linkType"];
}
return self;
}
#end
I dont know why the objects are unarchived correctly, but the object´s members are lost somewhere. I assume there must be an error somewhere in the NSCoding methods, but I just can´t find it, any help much appreciated.
When you implement the initWithCoder: method, you need to call super properly:
if (self = [super initWithCoder:decoder]) {
The instance that is being unarchived has more attributes than just the additions in your specific class. You also don't want to be checking equality with self, you want to be assigning to self.
What am I doing wrong? Am I encoding/decoding the enum type properly?
GameSettings interface:
typedef enum {
CatWhite = 0,
CatBlack = 1,
CatOrange = 2
} CatColor;
...
CatColor catColor;
...
#property CatColor catColor;
GameSettings implementation:
#synthesize catColor;
...
+ (GameSettings*)GetInstance
{
if (sharedSingleton == nil) {
sharedSingleton = [[super allocWithZone:NULL] init];
sharedSingleton.catColor = CatWhite;
}
return sharedSingleton;
}
-(void)encodeWithCoder:(NSCoder *)coder {
[coder encodeInt:self.catColor forKey:#"CatColor"];
}
-(id)initWithCoder:(NSCoder *)coder {
if((self = [super init])) {
self.catColor = [coder decodeIntForKey:#"CatColor"];
}
NSLog(#"initWithCoder: %d", self.catColor); //this logs the correct int
return self;
}
AppDidFinishLaunching:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [defaults objectForKey:#"GameSettings"];
GameSettings *gGameData = [NSKeyedUnarchiver unarchiveObjectWithData:data];
if(gGameData != NULL) {
GameSettings *settings = [GameSettings GetInstance];
settings.catColor = gGameData.catColor;
}else {
GameSettings *settings = [GameSettings GetInstance];
settings.catColor = CatWhite;
}
...
}
Save settings:
GameSettings* settings = [GameSettings GetInstance];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:settings];
[defaults setObject:data forKey:#"GameSettings"];
[defaults synchronize];
The crash (Program received signal: “EXC_BAD_ACCESS”) comes when I re-launch the app and try to access the game settings:
GameSettings* settings = [GameSettings GetInstance];
NSLog(#"settings: %d", settings); //EXC_BAD_ACCESS
NSLog(#"catColor: %d", settings.catColor); //EXC_BAD_ACCESS
Why can't I access the GameSettings singleton after a re-launch?
Are you sure you want to use applicationDidFinishLaunching there? When you resurrect the program, it may be invoking awakeFromNib instead of applicationDidFinishLaunching (source: http://www.cimgf.com/2008/03/26/cocoa-tutorial-awakefromnib-vs-applicationdidfinishlaunching/ ).
Finally figured this out. GameSettings was getting deallocated because NSKeyedUnarchiver was autoreleasing it:
GameSettings *gGameData = [NSKeyedUnarchiver unarchiveObjectWithData:data];
Fix:
GameSettings *gGameData = [[NSKeyedUnarchiver unarchiveObjectWithData:data] retain];