Custom objects in array stored in a plist - nsarray

I've got some custom class objects that i store in an array, this array is then stored in a dictionary with a key. This is then saved and will be loaded when the app is loaded/ reloaded.
Now the issues is that i can't seem to get the custom objects back out, when i load the array it comes back as empty. I must be doing something wrong with either writing or loading the data but i can't figure out what exactly.
Here's the code i have so far:
CustomClass.H file:
#import <Foundation/Foundation.h>
#interface PersonClass : NSObject <NSCoding>
#property (nonatomic,strong) NSString *personName;
#property (nonatomic,strong) NSString *personNo;
#property (nonatomic,strong) NSString *personNotes;
#property (nonatomic) BOOL switchPersonality;
#property (nonatomic) BOOL switchChemistry;
#property (nonatomic) BOOL switchLooks;
#property (nonatomic) BOOL switchHumour;
#property (nonatomic) int personRating;
#property (nonatomic,strong) NSNumber *personRecord;
- (id)initWithName:(NSString *)iPersonName PersonNo:(NSString *)iPersonNo PersonNotes:(NSString *)iPersonNotes SwitchChemistry:(BOOL *)iSwitchChemistry SwitchHumour:(BOOL *)iSwitchHumour SwitchLooks:(BOOL *)iSwitchLooks SwitchPersonality:(BOOL *)iSwitchPersonality PersonRating:(int *)iPersonRating PersonRecord:(NSNumber *)iPersonRecord;
#end
Custom Class.M file:
- (void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:personName forKey:#"pName"];
[aCoder encodeObject:personNo forKey:#"pNo"];
[aCoder encodeObject:personNotes forKey:#"pNotes"];
[aCoder encodeInt:personRating forKey:#"pRating"];
[aCoder encodeObject:personRecord forKey:#"pRecord"];
[aCoder encodeBool:switchChemistry forKey:#"sChemistry"];
[aCoder encodeBool:switchHumour forKey:#"sHumour"];
[aCoder encodeBool:switchLooks forKey:#"sLooks"];
[aCoder encodeBool:switchPersonality forKey:#"sPersonality"];
}
-(id)initWithCoder:(NSCoder *)aDecoder{
if (self = [super init]){
self.personName = [aDecoder decodeObjectForKey:#"pName"];
self.personNo = [aDecoder decodeObjectForKey:#"pNo"];
self.personNotes = [aDecoder decodeObjectForKey:#"pNotes"];
self.personRating = [aDecoder decodeIntForKey:#"pRating"];
self.personRecord = [aDecoder decodeObjectForKey:#"pRecord"];
self.switchChemistry = [aDecoder decodeBoolForKey:#"sChemistry"];
self.switchHumour = [aDecoder decodeBoolForKey:#"sHumour"];
self.switchLooks = [aDecoder decodeBoolForKey:#"sLooks"];
self.switchPersonality = [aDecoder decodeBoolForKey:#"sPersonality"];
}
return self;
}
The save Method:
- (void)saveData{
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to our Data/plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"data.plist"];
NSLog(#"PList Path %#",plistPath);
//new array
NSMutableArray *newEntries = [[NSMutableArray alloc]init];
NSLog(#"NEW ENTRIES BEFORE %#",newEntries);
for (PersonClass *person in peopleEntries) {
//encode the object
[NSKeyedArchiver archivedDataWithRootObject:person];
// add the object to the entries
[newEntries addObject:person];
}
NSLog(#"NEW ENTRIES AFTER %#",newEntries);
[newEntries writeToFile:plistPath atomically:YES];
// create dictionary with arrays and their corresponding keys
NSDictionary *plistDict = [NSDictionary dictionaryWithObjects: [NSMutableArray arrayWithObjects:newEntries, recordId, nil] forKeys:[NSMutableArray arrayWithObjects: #"peopleEntries",#"recordId", nil]];
NSLog(#"DICTIONARY IS IN SAVE %#",plistDict);
NSString *error = nil;
// check if plistData exists
if(plistDict)
{
// write plistData to our Data.plist file
[plistDict writeToFile:plistPath atomically:YES];
}
else
{
NSLog(#"Error in saveData: %#", error);
}
NSLog(#"Save RUN");
}
The load Method:
- (void)loadData{
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to our Data/plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"data.plist"];
NSLog(#"PList Path %#",plistPath);
// check to see if data.plist exists in documents
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath])
{
// return without loading
NSLog(#"RETURNING");
return;
}
// get saved dictionary
NSDictionary *dictionaryTemp = [[NSDictionary alloc]initWithContentsOfFile:plistPath];
NSLog(#"DICTIONARY IS LOAD %#",dictionaryTemp);
if (!dictionaryTemp)
{
NSLog(#"Error reading plist:");
}
// temporary array
NSMutableArray *holderOne = [dictionaryTemp objectForKey:#"peopleEntries"];
// array to be populated
NSMutableArray *holderTwo = [[NSMutableArray alloc]init];
NSLog(#"HOLDER ONE IS BEFORE %#",holderOne);
NSLog(#"HOLDER TWO IS BEFORE %#",holderTwo);
// go through the array and pull out person classes
for (NSData *person in holderOne) {
// temp array
NSData *entry = [holderOne objectAtIndex:person]; // check the object at index might be an issue???
NSLog(#"ENTRY IS %#",entry);
//deencode the object
[NSKeyedUnarchiver unarchiveObjectWithData:entry];
// add the object to the entries
[holderTwo addObject:entry];
}
NSLog(#"HOLDER ONE IS AFTER %#",holderOne);
NSLog(#"HOLDER TWO IS AFTER %#",holderTwo);
// assign values
peopleEntries = [NSMutableArray arrayWithArray:holderTwo];
NSLog(#"DICTIONARY IS AFTER ADDING %#",dictionaryTemp);
recordId = [NSNumber numberWithInt:[[dictionaryTemp objectForKey:#"recordId"]integerValue]];
NSLog(#"recordId is %#",recordId);
NSLog(#"LOAD RUN");
}

OK i finally discovered the issue, i was extending my class from the NSObject subclass which does not implement the NSCoder protocol so i was encoding and decoding my object in the incorrect way. It should have been done in the following way:
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
self.personName = [decoder decodeObjectForKey:#"pName"];
self.personNo = [decoder decodeObjectForKey:#"pNo"];
self.personNotes = [decoder decodeObjectForKey:#"pNotes"];
self.personRating = [decoder decodeObjectForKey:#"pRating"];
self.switchChemistry = [decoder decodeBoolForKey:#"sChemistry"];
self.switchHumour = [decoder decodeBoolForKey:#"sHumour"];
self.switchLooks = [decoder decodeBoolForKey:#"sLooks"];
self.switchPersonality = [decoder decodeBoolForKey:#"sPersonality"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:personName forKey:#"pName"];
[encoder encodeObject:personNo forKey:#"pNo"];
[encoder encodeObject:personNotes forKey:#"pNotes"];
[encoder encodeObject:personRating forKey:#"pRating"];
[encoder encodeBool:switchChemistry forKey:#"sChemistry"];
[encoder encodeBool:switchHumour forKey:#"sHumour"];
[encoder encodeBool:switchLooks forKey:#"sLooks"];
[encoder encodeBool:switchPersonality forKey:#"sPersonality"];
}
These objects were then added into an array and i was looping through each object in the array and decoding. This was not correct you can simply encode or decode the entire array with te objects as so:
Decode:
NSData *peopleData;
peopleEntries = [NSKeyedUnarchiver unarchiveObjectWithData:peopleData];
Encode:
NSMutableArray *peopleEntries;
NSData *personData = [NSKeyedArchiver archivedDataWithRootObject:[[GlobalData sharedGlobalData]peopleEntries]];

Related

ARC Objective-C delegation through a C++ abstract layer

I'm programming simple cross platform C++ layer (dylib) that gets implemented by Objective-C. The parts that are platform specific are included through platform macros.
The NSUserNotificationCenter requires the delegation pattern for handling specific actions, clicking on the notification for example. The issue I'm facing is that as soon as I execute send, the notification is sent but the instance unloads right after that. Thus the onclick notification action never gets called (didActivateNotification) instead it crashes for a bad pointer. How can I make this work?
Note:
SomeObjectiveC.mm is a class located in my Application. AppleUserNotificationManager.m is initialized by NotificationManager+apple.mm and both are located in my Dylib.
SomeObjectiveC.mm
Notification *notification = new Notification;
notification->setTitle("Foo bar notification");
notification->setMessage("Hello world!");
NotificationManager *notificationManager = new NotificationManager;
notificationManager->send(notification);
NotificationManager+apple.mm
#include "notificationManager+apple.hpp"
bool NotificationManager::send(Notification *notification)
{
AppleUserNotificationManager *notificationManager = [[AppleUserNotificationManager alloc] init];
NSString *title = [NSString stringWithUTF8String:notification->getTitle().c_str()];
NSString *message = [NSString stringWithUTF8String:notification->getMessage().c_str()];
if (notification->getSoundName().empty()) {
[notificationManager sendWithTitle:title andMessage:message];
}
NSString *soundName = [NSString stringWithUTF8String:notification->getSoundName().c_str()];
[notificationManager sendWithTitle:title andMessage:message andSound: soundName];
return true;
}
AppleUserNotificationManager.m
#import "AppleUserNotificationManager.h"
#implementation AppleUserNotificationManager
#synthesize userNotification;
- (id)init
{
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate: self];
userNotification = [[NSUserNotification alloc] init];
self = [super init];
return self;
}
/**
* #param NSUserNotificationCenter center
* #param NSUserNotification notification
*
* #return bool
*/
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification{
return YES;
}
/**
* #param NSUserNotificationCenter center
* #param NSUserNotification notification
*/
- (void) userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification
{
NSString *notificationText = [notification informativeText];
NSString *urlRegEx = #"(http|https)://((\\w)*|([0-9]*)|([-|_])*)+([\\.|/]((\\w)*|([0-9]*)|([-|_])*))+";
NSPredicate *urlTest = [NSPredicate predicateWithFormat:#"SELF MATCHES %#", urlRegEx];
if ([urlTest evaluateWithObject:notificationText]) {
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:notificationText]];
}
}
/**
* #param NSString title
* #param NSString message
* #param NSString soundName
*/
- (void)sendWithTitle:(NSString *)title andMessage:(NSString *)message andSound:(NSString *)soundName{
userNotification.title = title;
userNotification.informativeText = message;
userNotification.soundName = soundName;
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification: userNotification];
}
#end
Used self pointer before allocated?
Could below change fix the problem?
- (id)init
{
self = [super init];
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate: self];
userNotification = [[NSUserNotification alloc] init];
return self;
}

Getting Crash with AWSS3PutObjectRequest

I am using the AWS SDK like this way
Properties declaration :
#property (nonatomic, strong)AWSStaticCredentialsProvider *credentialsProvider;
#property (nonatomic, strong)AWSServiceConfiguration * configuration;
#property (nonatomic, strong)AWSS3 *s3;
#property (nonatomic, strong) AWSS3PutObjectRequest *putrequest;
And Implementation like this way
AccessKeys * accessKeys = [ECSGlobals sharedInstance].accessKeys;
self.credentialsProvider = [AWSStaticCredentialsProvider credentialsWithAccessKey:accessKeys.accessKeyId secretKey:accessKeys.secretAccessKey];
self.configuration = [AWSServiceConfiguration configurationWithRegion:AWSRegionUSEast1 credentialsProvider:self.credentialsProvider];
self.s3 = [[AWSS3 alloc] initWithConfiguration:self.configuration];
self.putrequest = [AWSS3PutObjectRequest new];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *savedImagePath = [documentsDirectory stringByAppendingPathComponent:imageName];
NSData *imageData = UIImageJPEGRepresentation(image, 0.2);
[imageData writeToFile:savedImagePath atomically:NO];
long long fileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:savedImagePath error:nil][NSFileSize] longLongValue];
self.putrequest.bucket = bucketName;
self.putrequest.key = imageName;
self.putrequest.contentType = #"image/jpeg";
self.putrequest.body = [NSURL fileURLWithPath:savedImagePath];
self.putrequest.contentLength = [NSNumber numberWithLongLong:fileSize];
[[self.s3 putObject:self.putrequest] continueWithBlock:^id(BFTask *task) {
NSLog(#"Amazon error : %#", [task error]);
return nil;
}];
But I am getting crash
[CFError retain]: message sent to deallocated instance 0x7f2daff0
I have checked all properties are strong. Not sure why I am getting this crash.
I found the solution. We are getting this issue as some how saved image path become nil before coming in use. So there is no image data sending to AWS.
After fixing this, images are uploading successfully.

Unit test core data and a method with NSExpressionDescription

How do I make a unit test for the following piece code??
NSExpressionDescription *expressionDescription = [[NSExpressionDescription alloc] init];
NSString *sumOfTotalPaidBySelf = #"sumOfTotalPaidBySelf";
expressionDescription.name = sumOfTotalPaidBySelf;
expressionDescription.expression = [NSExpression expressionForKeyPath:#"#sum.moneyInMainCurrency"];
expressionDescription.expressionResultType = NSDoubleAttributeType;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"MCPayment"];
fetchRequest.resultType = NSDictionaryResultType;
fetchRequest.predicate = [NSPredicate predicateWithFormat:#"payingPerson = %#", self];
fetchRequest.propertiesToFetch = #[expressionDescription];
NSError *error = nil;
NSArray *fetchResult = [[self managedObjectContext] executeFetchRequest:fetchRequest error:&error];
if (error) {
NSLog(#"%#: error fetching: %#", self, error);
return nil;
}
return [[fetchResult firstObject] objectForKey:sumOfTotalPaidBySelf];
What I've learned so far is, that this code should work when querying a database that already contains data, which is stored on disk. However for the unit tests I use a persistentStoreType of NSInMemoryStoreType, which gets pre populated with some data during setup and that data still resides in the managedObjectContext.
This is the unit test I have.
MCSharedBill *tonightsBill = [MCSharedBill addSharedBillToContext:_context];
MCPerson *fred = [tonightsBill addPerson];
fred.firstName = #"Fred";
MCPerson *anna = [tonightsBill addPerson];
anna.firstName = #"Anna";
MCPayment *drinks = [tonightsBill addPayment];
drinks.payingPerson = fred;
drinks.money = #(10);
drinks.descriptionOfPayment = #"coffee";
NSNumber *totalPaidByFred = fred.totalSumPaid;
// NSError *error = nil;
// [_context save:&error];
// XCTAssertFalse(error);
XCTAssertEqualWithAccuracy(#(10).doubleValue, totalPaidByFred.doubleValue, 0.001);
Any help would be appreciated.
Cheers.

How do I get element from NSArray of NSDictionary

This is NSLog of my NSArray
[{"id":16,"venueId":16,"street":"171 - 3401 Dufferin St","city":"Toronto","zipcode":"M6A 2T9","province":"ON","country":"Canada"}]
NSDictionary *dict = [myarray objectAtIndex:i]
//myarray is your array of dictionary
//if the array has just one element like in your example, i will be 0
NSNumber *venueId = [dict objectForKey:#"venueId"];
finally I found solution
NSError *error;
NSArray* jsonArray = [NSJSONSerialization JSONObjectWithData:[[venue objectForKey:#"address"] dataUsingEncoding:NSUTF8StringEncoding] options:0 error:&error] ;
NSDictionary *dict = [jsonArray objectAtIndex:0];
//myarray is your array of dictionary
NSString *street = [dict objectForKey:#"street"];
NSLog(#"street: %#", street);
Note: I used below code to check data type to make sure it returns valid kind of class.
if ([[venue objectForKey:#"address"] isKindOfClass:[NSArray class]]) {
NSLog(#"%#", #"It is NSArray");
} else if ([[venue objectForKey:#"address"] isKindOfClass:[NSString class]]) {
NSLog(#"%#", #"It is NSString");
}

How to read a plist data and get int from it?

Currently using cocos2d. I have a plist data name myplist.plist. Inside the plist are all integers.. How do i read the data and the int in it?
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *dictionaryPath = [path stringbyAppendingPathComponent:#"myplist.plist"];
NSDictionary *integerDictionary = [[NSDictionary alloc] initWithContentsOfFile:dictionaryPath];
int myInteger1 = [[integerDictionary objectForKey:#"integer1"] intValue];
int myInteger2 = [[integerDictionary objectForKey:#"integer2"] intValue];
// etc etc