Issue in NSUserDefaults while retrieving data, value change from "0" to "1" - objective-c

I am beginner in Objective C, while performing my Assignment I tried to save the object in NSUserDefaults
Object Created Successfully and after reopening of Object, the value changes of BOOL instance variable , from 0 to 1 of all objects.
Protocol's Method, which evaluates on Task Creation..
-(void)didAddTask:(Task *)task{
[self.taskObjects addObject:task];
NSMutableArray *arrayOfTaskObjects = [[[NSUserDefaults standardUserDefaults]arrayForKey:TASK_OBJECTS_KEY] mutableCopy];
NSLog(#"%#",arrayOfTaskObjects);
if (!arrayOfTaskObjects) {
arrayOfTaskObjects = [[NSMutableArray alloc]init];
}
[arrayOfTaskObjects addObject:[self taskObjectAsAPropertyList:task]];
[[NSUserDefaults standardUserDefaults] setObject:arrayOfTaskObjects forKey:TASK_OBJECTS_KEY];
[[NSUserDefaults standardUserDefaults] synchronize];
[self dismissViewControllerAnimated:YES completion:nil];
[self.tableView reloadData];
}
The method to create Property List:
- (NSDictionary *)taskObjectAsAPropertyList:(Task *)taskObject{
NSDictionary *myDictionary = #{TASK_TITLE:taskObject.taskTitle,
TASK_DESCRIPTION:taskObject.taskDescription,
TASK_DATE:taskObject.taskDate,
TASK_COMPLETION:#(taskObject.isTaskCompleted)};
NSLog(#"Task Completion: %#", myDictionary[TASK_COMPLETION]);
return myDictionary;
}
EDIT:
Code of Task Class:
#implementation Task
-(id)init{
self = [self initWithData:nil];
return self;
}
-(id)initWithData:(NSDictionary*)data{
self = [super init];
if (self) {
self.taskTitle = data[TASK_TITLE];
self.taskDescription = data[TASK_DESCRIPTION];
self.taskDate = data[TASK_DATE];
self.isTaskCompleted = data[TASK_COMPLETION];
}
return self;
}
#end
Edit 2:
NSMutableArray * demoArray = [[[NSUserDefaults standardUserDefaults]arrayForKey:TASK_OBJECTS_KEY]mutableCopy];
NSLog(#"Retrievd Array From NSUserDefaults : %#", demoArray);
for(NSDictionary *dic in demoArray){
Task *myTask = [[Task alloc]init];
myTask.taskTitle = dic[TASK_TITLE];
if (dic[TASK_COMPLETION]==0) {
myTask.isTaskCompleted = NO;
}
else{
myTask.isTaskCompleted = YES;
}
//myTask.isTaskCompleted = dic[TASK_COMPLETION];
myTask.taskDescription = dic[TASK_DESCRIPTION];
myTask.taskDate = dic[TASK_DATE];
[self.taskObjects addObject:myTask];
}

Make Sure that the Data you're going to save in NSUserDefaults, is the same that you entered, by Using NSLog statement.

Related

Remove object from an array stored in a singleton

Im working with a singleton to store some data, her's the implementation
static ApplicationData *sharedData = nil;
#implementation ApplicationData
#synthesize list;
+ (id)sharedData
{
static dispatch_once_t dis;
dispatch_once(&dis, ^{
if (sharedData == nil) sharedData = [[self alloc] init];
});
return sharedData;
}
- (id)init
{
if (self = [super init])
{
list = [[NSMutableArray alloc]init];
}
return self;
}
if list have less than 3 (2<) object i the app crash with "index 0 beyond bounds for empty array"
// NSMutableArray *anArray = [[NSMutableArray alloc]initWithObjects:#"", nil];
while ([[[ApplicationData sharedData]list] lastObject] != nil)
{
File *file = [[[ApplicationData sharedData]list] lastObject];
BOOL isDir;
if (![[NSFileManager defaultManager] fileExistsAtPath:file.filePath isDirectory:&isDir])
{
NSMutableDictionary *tmpDic = [NSMutableDictionary dictionaryWithObjects:[NSArray arrayWithObjects:file.fileName,file.filePath,logEnteryErrorfileNotFoundDisplayName,[formatter stringFromDate:[NSDate date]], nil] forKeys:[NSArray arrayWithObjects:logShredFileName,logShredFilePath,logShredStatue,logShredDate, nil]];
[logArray addObject:tmpDic];
errorOccured = YES;
[[[ApplicationData sharedData]list] removeLastObject];
continue;
}
... other code
}
if i use the anArray that work perfectly.
what is the problem ?
That's totally weird, you've probably did something else to achieve this. Why don't you use - (void)removeAllObjects?
Maybe you remove objects in the while cycle the last line, ie:
while ([[[ApplicationData sharedData]list] count] != 0)
{
// remove object from list
// ...
[[[ApplicationData sharedData]list] removeLastObject];
}
And just a note, you don't need to check if (sharedData == nil) in sharedData as far as it's guaranteed to be executed only once. (unless you do something outside to your static variable, but that's not how it's supposed to be done I believe)

Message wont connect to method

I'm kind of new to this, so it might be a beginners mistake. The problem is that when i send a message to a method it doesn't connect.
Here's the calling method:
-(BOOL) login:(LoginInfo *) info{
NSString *url = [NSString stringWithFormat:#"%#/%#?name=%#&password=%#", FAMORABLE_API_URL,FAMORABLE_API_ACTION_LOGIN, info.username, info.password];
NSDictionary* json = [self getJson:url];
NSString *token = [json objectForKey:#"Token"];
NSLog(#"results: %#", token);
LoginInfo *loginResult = [LoginInfo alloc];
loginResult.token = token;
//TODO
NSLog(#"test 1 %#", loginResult.token);
[clientService saveLoginInfo:loginResult];
return YES;
}
On the line above the last you can see I'm sending to saveLoginInfo in clientService which is declared in ClientService.h which is imported in this file.
-(void) saveLoginInfo:(LoginInfo *)info{
NSLog(#"test 2 %#", info);
NSLog(#"test 3%#", info.token);
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:info.token forKey:KEY_TOKEN];
NSString *string = [defaults stringForKey:KEY_TOKEN];
NSLog(#"test 4%#", string);
// save it
[defaults synchronize];
if (!(info.token)){
self.currentUser = nil;
self.isLoggedOn=false;
}else{
self.currentUser = info;
self.isLoggedOn=true;
}
}
This is the method being called. I've put out a bunch of logs, mostly as a safe if i did one wrong, but none of them are being executed...
Do you have any ideas of what might be wrong?
Thanks in advance
Tom
Is clientService in the line [clientService saveLoginInfo:loginResult]; an instance variable in your class?
If so, make sure that you have instantiated the object somewhere before you call it. (Either in your class's init method, or possibly viewDidLoad if it's a UIViewController.)
E.g.
- (void)viewDidLoad {
[super viewDidLoad];
clientService = [[MyClientService alloc] init];
}
- (id)init {
self = [super init];
if (self) {
clientService = [[MyClientService alloc] init];
}
return self;
}
(Make sure you also release the instance in the dealloc method.

NSUserDefaults with Custom object that has an array of custom objects

I have an object that I convert into NSData using an NSKeyedArchiver and then store it into NSUserDefaults. Everything gets saved correctly except for the elements of an array that the object has. All the objects in the array have conform to the NSCoder protocols (or whatever theyre called- ex. self.property = [decoder decodeObjectForKey:#"key"] and [encoder encodeObjectForKey:#"key"])
When I save the object, the elements of the array remain in the array, but their properties themselves do not get saved. i DO call the sycnrhonize method, so that is not the issue.
NOTE that all other times i save & load it is correct, it just does not save the elements of an array that belongs to an object. Do i have to save that separately?
The "current status" NSNumber is not being saved. Objective and target are being saved
import "Level.h"
#implementation Level
#synthesize objective = _objective;
#synthesize isComplete = _isComplete;
#synthesize goldReward = _goldReward;
#synthesize xpReward = _xpReward;
#synthesize missionID = _missionID;
#synthesize currentStatus = _currentStatus;
#synthesize targetName = _targetName;
#synthesize owner = _owner;
-(void)dealloc{
[super dealloc];
}
-(id)initWithMissionID:(int)number{
if (self = [super init]) {
self.currentStatus = 0;
self.isComplete = NO;
self.missionID = [NSNumber numberWithInt:number];
[self setUpMisson];
}
return self;
}
-(void)setUpMisson{
if ([self.missionID intValue] == 0) {
self.xpReward = [NSNumber numberWithInt:100];
self.goldReward = [NSNumber numberWithInt:100];
self.objective = [NSNumber numberWithInt:3];
self.targetName = #"Swordsman";
CCLOG(#"Gotta kill some swordsmen!");
}
}
-(void)encodeWithCoder:(NSCoder *)encoder{
[encoder encodeObject:self.objective forKey:#"objective"];
[encoder encodeObject:self.isComplete forKey:#"isComplete"];
[encoder encodeObject:self.goldReward forKey:#"goldReward"];
[encoder encodeObject:self.xpReward forKey:#"xpReward"];
[encoder encodeObject:self.missionID forKey:#"missionID"];
[encoder encodeObject:self.currentStatus forKey:#"currentStatus"];
[encoder encodeObject:self.targetName forKey:#"targetName"];
[encoder encodeObject:self.owner forKey:#"owner"];
CCLOG(#"SAVING LEVEL");
}
-(id)initWithCoder:(NSCoder *)decoder{
if (self = [super init]) {
self.objective = [[decoder decodeObjectForKey:#"objective"]retain];
self.isComplete = [[decoder decodeObjectForKey:#"isComplete"]retain];
self.goldReward = [[decoder decodeObjectForKey:#"goldReward"]retain];
self.xpReward = [[decoder decodeObjectForKey:#"xpReward"]retain];
self.missionID = [[decoder decodeObjectForKey:#"missionID"]retain];
self.targetName = [[decoder decodeObjectForKey:#"targetName"]retain];
self.owner = [[decoder decodeObjectForKey:#"owner"]retain];
CCLOG(#"LOADING LEVEL");
}
return self;
}
-(void)updateStatusForKill:(AI *)killedTarget{
CCLOG(#"WE KILLED: %# and OUR GOAL IS: %#",killedTarget.name,self.targetName);
if ([killedTarget.name isEqualToString:self.targetName]) {
[self setCurrentStatus:[NSNumber numberWithInt:[self.currentStatus intValue]+1]];
CCLOG(#"Current Status: %i Objective: %i", [self.currentStatus intValue],[self.objective intValue]);
if ([self.currentStatus intValue] == [self.objective intValue]) {
[self completeMission];
}
}
}
-(void)completeMission{
[self.owner setCoins:[NSNumber numberWithInt:[[self.owner coins]intValue] + [self.goldReward intValue]]];
[self.owner setXp:[NSNumber numberWithInt:[[self.owner xp]intValue] + [self.xpReward intValue]]];
CCLOG(#"complete");
[[self.owner missionList]removeObject:self];
}
#end
EDIT: The "owner" refers back to the object being saved. I think this is where the problem is, so I'm removing that and testing again.
EDIT: and that did nothing!
What you describe should "just work." In the encodeWithCoder method of your custom object, you would just encode the array object, and that should cause the array and it's contents to be encoded.
However, if any of the objects in the array do not support NSCoding, that will fail. My guess is that something in your array (or it's children or grandchildren) does not support NSCoding.
I ran into this problem when trying to save an array of Accounts that contain property values and another custom object. I couldn't save my data with your proposed solution because I was arbitrarily adding accounts to an array, and it wouldn't make sense to come up with unique identifiers for dynamically added accounts. I ended up nesting the NSCoding protocol:
In my AccountManager class:
- (void) saveAllAccounts {
[self deleteAllAccounts]; //Just removes whatever was previously stored there
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:accountArray] forKey:saveAccountsArrayKey];
[[NSUserDefaults standardUserDefaults] synchronize];
}
In my Account class:
- (void) encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:username forKey:#"username"];
[aCoder encodeObject:token forKey:#"token"];
[aCoder encodeObject:specialID forKey:#"special ID"];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:deviceArray];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"device array"];
}
- (id) initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self != nil) {
username = [[aDecoder decodeObjectForKey:#"username"] retain];
token = [[aDecoder decodeObjectForKey:#"token"] retain];
ecoID = [[aDecoder decodeObjectForKey:#"eco ID"] retain];
NSData *deviceArrayData = [[NSUserDefaults standardUserDefaults] objectForKey:#"device array"];
if (deviceArrayData != nil) {
NSArray *savedArray = [NSKeyedUnarchiver unarchiveObjectWithData: deviceArrayData];
if (savedArray != nil)
deviceArray = [[NSMutableArray alloc] initWithArray:savedArray];
}
}
return self;
}
In my AccountDevice class:
- (void) encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:pairingPassword forKey:#"pairing password"];
[aCoder encodeObject:devicePin forKey:#"device pin"];
}
- (id) initWithCoder:(NSCoder *)aDecoder {
self = [super init];
if (self != nil) {
password = [[aDecoder decodeObjectForKey:#"password"] retain];
pin = [[aDecoder decodeObjectForKey:#"pin"] retain];
}
return self;
}
It might be a little buggy because I haven't finished testing it, but my preliminary tests were successful, and I think the concept is there.
It looks like you're using -encodeObject:forKey: and -decodeObjectForKey: even on values that aren't objects. For example, in your -initWithMissionID: you've got:
self.isComplete = NO;
which makes me think that complete is a BOOL property, but your -encodeObject:forKey: says:
[encoder encodeObject:self.isComplete forKey:#"isComplete"];
I think you probably want to call -encodeBool:forKey: instead, like this:
[encoder encodeBool:self.isComplete forKey:#"isComplete"];
On the other hand, I'd really expect some sort of warning if the problem were that simple. Do you get any warnings? It's harder to infer the types of your other properties, but look at each of your properties for the same kind of problem.
found a workaround using a Unique-Id system for each mission and saving the progress for each mission separately into NSUserDefaults which are just then loaded again. Not ideal, but it works. Thanks for everyone's help!

cancel button on search bar does not cancel correctly

I have a search bar, i can search now, but when I enter a text to search, and click the cancel button. It does not give me back my first stage, meaning full of the items in the table.
For example: I search the item with word: a, it gives me all the a items, yes, it is right now, but when i hit the cancel button, i want the programme gives me all the items exist, not just a items.
Here is the code: please help me out. Thank you so much.
- (void)searchBarCancelButtonClicked:(UISearchBar *)aSearchBar
{
searchBar.text = #"";
[searchBar resignFirstResponder];
letUserSelectRow = YES;
searching = NO;
self.tableView.scrollEnabled = YES;
NSLog(#"what text after cancel now: %#", searchBar.text);
[self.tableView reloadData];
}
- (NSMutableArray *) searchTableView {
NSString *searchText = searchBar.text;
NSLog(#"search text: %#", searchText);
NSMutableArray *resultArray = [[NSMutableArray alloc] init];
NSMutableArray *tempArr = [[NSMutableArray alloc] init];
for (NSDictionary *dTemp in arrayData)
{
NSString *tempStr = [dTemp objectForKey:#"url"];
NSLog(#"sTemp string: %#",[ NSString stringWithFormat:#"%#", tempStr]);
NSRange titleResultsRange = [tempStr rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (titleResultsRange.length > 0)
{
NSLog(#"1 count :%d", [resultArray count]);
[resultArray addObject:dTemp];
NSLog(#"2 count :%d", [resultArray count]);
[tempArr addObject:resultArray];
[resultArray release];
resultArray = [NSMutableArray new];
}
}
if (resultArray != nil) {
[resultArray release];
}
return tempArr;
}
- (void)searchBar:(UISearchBar *)aSearchBar textDidChange:(NSString *)searchText
{
NSLog(#"what text after cancel now: %#", searchBar.text);
if([searchText length] > 0) {
[sortedArray removeAllObjects];
searching = YES;
letUserSelectRow = YES;
self.tableView.scrollEnabled = YES;
NSMutableArray *searchArray = [self searchTableView];
sortedArray = [[NSMutableArray alloc] initWithArray:searchArray copyItems:YES];
for (int i = 0; i<[sortedArray count]; i++) {
NSLog(#"this is the search array: %#", [[sortedArray objectAtIndex:i] class]);
}
NSLog(#"sorted array: %d", [sortedArray count]);
}
else {
searching = NO;
letUserSelectRow = NO;
self.tableView.scrollEnabled = NO;
}
[self.tableView reloadData];
}
You don't need to override any of UISearchBar methods to accomplish this. The new way of doing this relies on the UISearchDisplay controller instead (specifically on shouldReloadTableForSearchString).
Declare your view controller to conform to UISearchDisplayDelegate protocol, and keep two instance variables: your model as NSArray (all data) and a filtered array as NSMutableArray (a subset of your data). The code you presently have in "searchTableView" would filter the content of the model and place it into the filtered NSMutableArray. Then you would override the following UITableView methods: -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section and -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath. In each, before returning, make a comparison to determine whether your tableView argument is equal to self.searchDisplayController.searchResultsTableView. If it is, the user is looking at the filtered list and your should use the content of the filtered NSMutableArray to create the view, otherwise, the user is looking at the whole data set and you should use the content of the NSArray that holds your model. Take a look at the following Apple code for a simple example of what I described:
http://developer.apple.com/library/ios/#samplecode/TableSearch/Introduction/Intro.html

NSMutableArray won't add NSDictionary

I updated to cocos2d from 0.99.4 to 0.99.5. While it was on the older version I had the high score list working and I was able to add the NSDictionary to NSMutableArray with no problems.
Now that I've updated it won't add the NSDictionary variable scoreDetails to my NSMutableArray scoreList. Here's my code:
StatsManager.h
#interface StatsManager : NSObject {
NSMutableArray *scoreList;
NSUserDefaults *saveHighScore;
NSMutableArray *printableScoreList;
//NSMutableArray *scoreListTestOne;
float highScoreHelloWorld;
}
StatsManager.m
-(void)setHighScore:(float)highScore nameStrings:(NSString*)nameString {
NSNumber *newHighScore = [NSNumber numberWithFloat:highScore];
NSLog(#"%# highScore", newHighScore);
NSDictionary *scoreDetails = [NSDictionary dictionaryWithObjectsAndKeys:nameString, #"name", newHighScore, #"score", nil];
NSLog(#"%#", scoreDetails);
//NSMutableArray *testTwo = [[NSMutableArray alloc] init];
[scoreList addObject:scoreDetails];
NSLog(#"scoreList %#", scoreList);
//[scoreListTestOne addObject:scoreDetails];
//NSLog(#"scoreListTestOne %#", scoreListTestOne);
//sort
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:#"score" ascending:NO];
[scoreList sortUsingDescriptors:[NSArray arrayWithObject:sort]];
printableScoreList = scoreList;
NSLog(#"printableScoreList %#", printableScoreList);
//NSLog(#"scoreListTestOne %#", scoreListTestOne);
}
The line in question is
[scoreList addObject:scoreDetails];
I created a local NSMutableArray variable in the setHighScore function and tried adding the scoreDetails to that and it worked. but why doesn't it work like I've coded it above anymore?
I alloc init my scoreList here:
#implementation StatsManager
static StatsManager *_sharedStatsManager = nil;
-(id)init {
scoreList = [[NSMutableArray alloc] init];
//playerNames = [[NSMutableArray alloc] init];
//playerScores = [[NSMutableArray alloc] init];
printableScoreList = [[NSMutableArray alloc] init];
//listOfScoresTest = [[NSMutableDictionary alloc] initWithCapacity:5];
/*if ([scoreList count] == 0) {
for (int i = 0; i < 5; i++) {
[scoreList addObject:[NSNumber numberWithFloat:0.00]];
}
}*/
return [super init];
}
I should also mention that I created a new projectB and transferred my files/images from my old projectA to the new one because the old one wouldn't compile anymore because of some duplicate error. But i "cleaned all targets" again and it worked but that also has the same problem as my new projectB
Do you initialize scoreList ivar in init or so forth?
- (id)init
{
/* snip */
scoreList = [[NSMutableArray alloc] init];
return self;
}
- (void)dealloc
{
[scoreList release];
[super dealloc];
}
Ok Georg I reconsidered your suggestion about overwriting it later.
and it had something to do with my NSUserdefaults. I commented them out and now it add's the objects to my NSMutableArray. I'm pretty new to NSUserdefaults so I don't know exactly how to use it atm lol
-(void)save
{
//make another array to save the scores.
saveHighScore = [NSUserDefaults standardUserDefaults];
//[saveHighScore setObject:scoreListNew forKey:#"DodgerAppBeta"];
[saveHighScore synchronize];
//[[NSUserDefaults standardUserDefaults] setObject:scoreListTestOne forKey:#"DodgerBeta"];
//[[NSUserDefaults standardUserDefaults] synchronize];
}
-(void)load
{
saveHighScore = [NSUserDefaults standardUserDefaults];
//scoreListNew = [[saveHighScore objectForKey:#"DodgerAppBeta"] mutableCopy];
printableScoreList = [[saveHighScore objectForKey:#"DodgerAppBeta"] mutableCopy];
//NSLog(#"scoreListTestOne %#", scoreListTestOne);
//[printableScoreList addObject:[[NSUserDefaults standardUserDefaults] objectForKey:#"Dodger"]];
NSLog(#"PSL %#", printableScoreList);
}