I have a class "DataStorage", in which i wish to use NSCoding protocols to save data. However, when try to add to an array of that class,
[DataStorage sharedData].firstNameArray addObject:firstName:];
When viewing the breakpoint in the DataStorage class it shows the array to be of "nil" value
Here's the class implementation...
#import "DataStorage.h"
#implementation DataStorage
#synthesize firstNameArray = _firstNameArray;
#synthesize surnameArray = _surnameArray;
#synthesize companyArray = _companyArray;
#synthesize positionArray = _positionArray;
#synthesize emailArray = _emailArray;
#synthesize mobileArray = _mobileArray;
#synthesize productArray = _productArray;
+ (instancetype)sharedData {
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [self loadInstance];
});
return sharedInstance;
}
- (instancetype)initWithCoder:(NSCoder *)decoder
{
self = [self init];
if (self) {
_firstNameArray = [decoder decodeObjectForKey:#"DataStorageFirstNameKey"];
_surnameArray = [decoder decodeObjectForKey:#"DataStorageSurnameKey"];
_companyArray = [decoder decodeObjectForKey:#"DataStorageCompanyKey"];
_positionArray = [decoder decodeObjectForKey:#"DataStoragePositionKey"];
_emailArray = [decoder decodeObjectForKey:#"DataStorageEmailKey"];
_mobileArray = [decoder decodeObjectForKey:#"DataStorageMobileKey"];
_productArray = [decoder decodeObjectForKey:#"DataStorageProductKey"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:_firstNameArray forKey:#"DataStorageFirstNameKey"];
[encoder encodeObject:_surnameArray forKey:#"DataStorageSurnameKey"];
[encoder encodeObject:_companyArray forKey:#"DataStorageCompanyKey"];
[encoder encodeObject:_positionArray forKey:#"DataStoragePositionKey"];
[encoder encodeObject:_emailArray forKey:#"DataStorageEmailKey"];
[encoder encodeObject:_mobileArray forKey:#"DataStorageMobileKey"];
[encoder encodeObject:_productArray forKey:#"DataStorageProductKey"];
}
+ (NSString*)filePath
{
static NSString* filePath = nil;
if (!filePath) {
filePath =
[[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]
stringByAppendingPathComponent:#"datastorage"];
}
return filePath;
}
+ (instancetype)loadInstance
{
NSData* decodedData = [NSData dataWithContentsOfFile: [DataStorage filePath]];
if (decodedData) {
DataStorage * dataStorage = [NSKeyedUnarchiver unarchiveObjectWithData:decodedData];
return dataStorage;
}
return [[DataStorage alloc] init];
}
- (void)save
{
NSData* encodedData = [NSKeyedArchiver archivedDataWithRootObject: self];
[encodedData writeToFile:[DataStorage filePath] atomically:YES];
}
#end
First of all, the _firstNameArray should be a mutable array but the array you get from [decoder decodeObjectForKey:#"DataStorageFirstNameKey"]; is not mutable, so you must create one.
Second, when you first use the instance, there is not file, so you will get nil in _fileNameArray, at this time, you should create a empty mutable array instead so you can add item in it.
- (instancetype)initWithCoder:(NSCoder *)decoder
{
self = [self init];
if (self) {
_firstNameArray = [[decoder decodeObjectForKey:#"DataStorageFirstNameKey"] mutableCopy];
if (!_firstNameArray) {
_firstNameArray = [NSMutableArray array] ;
}
}
return self;
}
So are the other properties.
Related
I'm encoding an array of objects that conform to NSSecureCoding successfully like this.
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array requiringSecureCoding:YES error:nil];
How do I unarchive this data? I'm trying like this but it's returning nil.
NSError *error = nil;
NSArray<MyClass *> *array = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSArray class] fromData:data error:&error];
This is the error it returns.
The data couldn’t be read because it isn’t in the correct format.
Here is a simple example
#interface Secure:NSObject<NSSecureCoding> {
NSString* name;
}
#property NSString* name;
#end
#implementation Secure
#synthesize name;
// Confirms to NSSecureCoding
+ (BOOL)supportsSecureCoding {
return true;
}
- (void)encodeWithCoder:(nonnull NSCoder *)coder {
[coder encodeObject:self.name forKey: #"name"];
}
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
self = [self init];
if (self){
self.name = [coder decodeObjectOfClass:[NSString class] forKey:#"name"];
}
return self;
}
#end
Usage
Secure *archive = [[Secure alloc] init];
archive.name = #"ARC";
NSArray* array = #[archive];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array requiringSecureCoding:YES error:nil];
NSArray<Secure *> *unarchive = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSObject class] fromData:data error:nil];
NSLog(#"%#", unarchive.firstObject.name);
In My Modal Object two values are there
I'm trying to store in NSUserDefaults. This is My Code
interface File:
#interface collectionModel : NSObject <NSCoding> {
}
#property(nonatomic,retain) NSString *name;
#property(nonatomic,retain) NSString *author;
Implementation File:
#implementation collectionModel
#synthesize name = _name;
#synthesize author = _author;
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
self.name = [aDecoder decodeObjectForKey:#"name"];
self.author = [aDecoder decodeObjectForKey:#"author"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_name forKey:#"name"];
[aCoder encodeObject:_author forKey:#"author"];
}
ViewController.M
#import "collectionModel.h"
parsedCollectionArr = [[NSMutableArray alloc] init];
for (NSDictionary *obj in collectionBalk) {
NSString * Name = [obj objectForKey:#"Name"];
NSString * author = [obj objectForKey:#"author"];
collectionModel *dataObj = [[collectionModel alloc] init];
dataObj.name = Name;
dataObj.author = author;
[parsedCollectionArr addObject:dataObj];
}
NSLog(#"parsedCollectionArr count ---->>>> %d",23);
Here I want to store this parsedCollectionArr in NSUserDefaults and retrive from it.can any one help me.
try this..
Create a class like "CollectionModelManager.h and .m"
//For Save CollectionModel
+ (void)saveCollectionModel:(CollectionModel *) collectionModel
{
NSData *encodedData = [NSKeyedArchiver archivedDataWithRootObject:collectionModel];
NSUserDefaults *userDef = [NSUserDefaults standardUserDefaults];
[userDef setObject:encodedData forKey:kCollectionModelKey]; //here kCollectionModelKey is a static string
[userDef synchronize];
}
//For Get CollectionModel
+ (CollectionModel *)getCollectionModel
{
NSUserDefaults *userDef = [NSUserDefaults standardUserDefaults];
CollectionModel *collectionModel = nil;
NSData *encodedData = [userDef objectForKey:kCollectionModelKey]; //Same static string use here kCollectionModelKey
if (encodedData != nil) {
collectionModel = [NSKeyedUnarchiver unarchiveObjectWithData:encodedData];
}
return collectionModel;
}
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 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.
In the following code, the properties are never set correctly in the sharedInstance method, and I can't figure out why. It seems like I have the correct values though before I save it with the archiver.
#import "User.h"
static User *sharedInstance = nil;
#define NAME #"name"
#define USER_ID #"id"
#define ACCOUNT_ID #"account_id"
#define USER_NAME #"username"
#define ADMIN #"admin"
#define CURRENT_USER #"current_user"
#implementation KSUser
+ (id)sharedInstance
{
#synchronized(self) {
if (sharedInstance == nil) {
NSData *userData = [[NSUserDefaults standardUserDefaults] objectForKey:CURRENT_USER];
if (userData) {
sharedInstance = [NSKeyedUnarchiver unarchiveObjectWithData:userData];
}
else {
sharedInstance = [[super alloc] init];
}
}
}
return sharedInstance;
}
- (void)populateFromJSON:(NSDictionary *)json
{
sharedInstance.name = json[NAME];
sharedInstance.accountId = json[ACCOUNT_ID];
sharedInstance.userId = json[USER_ID];
sharedInstance.userName = json[USER_NAME];
sharedInstance.admin = [json[ADMIN] boolValue];
sharedInstance.loggedIn = YES;
NSLog(#"values are: name: %#, %#, %#, %#", sharedInstance.name, sharedInstance.accountId, sharedInstance.userId, sharedInstance.userName);
}
- (void)logout
{
sharedInstance.name = nil;
sharedInstance.accountId = nil;
sharedInstance.userId = nil;
sharedInstance.userName = nil;
sharedInstance.admin = NO;
sharedInstance.loggedIn = NO;
[self saveState];
}
- (void)saveState
{
NSLog(#"values are: name: %#, %#, %#, %#", sharedInstance.name, sharedInstance.accountId, sharedInstance.userId, sharedInstance.userName);
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:sharedInstance];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:CURRENT_USER];
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:sharedInstance.userId forKey:USER_ID];
[aCoder encodeObject:sharedInstance.accountId forKey:ACCOUNT_ID];
[aCoder encodeObject:sharedInstance.name forKey:NAME];
[aCoder encodeObject:sharedInstance.userName forKey:USER_NAME];
[aCoder encodeBool:sharedInstance.admin forKey:ADMIN];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super init]) {
sharedInstance.userId = [aDecoder decodeObjectForKey:USER_ID];
sharedInstance.accountId = [aDecoder decodeObjectForKey:ACCOUNT_ID];
sharedInstance.name = [aDecoder decodeObjectForKey:NAME];
sharedInstance.userName = [aDecoder decodeObjectForKey:USER_NAME];
sharedInstance.admin = [aDecoder decodeBoolForKey:ADMIN];
}
return self;
}
#end
Any help would be greatly appreciated.
It is because your global variable sharedinstance in method - (id)initWithCoder:(NSCoder *)aDecoder is always nil
In initWithCoder: don't refer to the shared instance but rather self instead. At the time it's executing sharedInstance is nil.
Also, you only call saveState after logging out, so it only saves out the nil values.