One method to add/remove/edit dictionaries from memory - ios

Right now, if i have a list , NSMutableArry of NSDictionaries, and i would like to edit / remove/ add object to, and save to the user defaults . (obj-c/swift)
So i would have a function for each operation , for example to add one to the list :
-(void)addMedicineToArray:(NSDictionary*)medicine
{
NSMutableArray *medicines=[[NSMutableArray alloc] init];
medicines=[[[NSUserDefaults standardUserDefaults] objectForKey:#"Medicines"] mutableCopy];
if(!medicines)
medicines=[[NSMutableArray alloc] init];
[medicines addObject:medicine];
[[NSUserDefaults standardUserDefaults] setObject:medicines forKey:#"Medicines"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
Then another method to remove, and another one to edit. I am pretty sure this is not that clever solution, although i coudn't think of a simple solution to do that in one clear method.
is there some better implementation of this instead of writing 3/4 methods ?

//
// MYCustomViewControllerClass.m
// Fro StackOverFlow
//
// Created by Naresh Reddy M on 16/03/16.
// Copyright © 2016 Naresh Reddy M. All rights reserved.
//
#import "MYCustomViewControllerClass.h"
typedef NS_ENUM(NSInteger, EditingType)
{
kEditingTypeDeleteObj = 1,
kEditingTypeUpdateObj = 2,
kEditingTypeAddObj = 3,
// if needed,Add few More as per your requirement .
};
#implementation MYCustomViewControllerClass
- (void)viewDidLoad
{
[super viewDidLoad];
// Forming Medicine Data Dict & Array
NSMutableArray <NSDictionary *>*medicinesArray;
if([[[NSUserDefaults standardUserDefaults] objectForKey:#"MYMeds"] mutableCopy])
medicinesArray = [[[NSUserDefaults standardUserDefaults] objectForKey:#"MYMeds"] mutableCopy];
else
{
medicinesArray = [NSMutableArray new];
for(int i = 0; i < 10 ; ++i)
{
NSMutableDictionary *medicineDetailsDict = [NSMutableDictionary new];
[medicineDetailsDict setValue:[NSString stringWithFormat:#"Medicine : %d",1+i] forKey:[NSString stringWithFormat:#"Name"]];
[medicineDetailsDict setValue:[NSString stringWithFormat:#"D%d",1+i] forKey:[NSString stringWithFormat:#"DrugID"]]; // Please do maintain a UNIQUE ID for Your Drug
// You can have other info about drug with appropriate keys
[medicinesArray addObject:medicineDetailsDict];
}
}
// 1 - Deleting your medicine with DrugID D1 from aray & saving updated medicines Array into userdefaults
// Name will be changed for Medicine With DrugID - D1 from #"Medicine : 1" to #"Updated Data for Name Key"
NSMutableDictionary *medicine1 = [medicinesArray[0] mutableCopy];
[medicine1 setValue:#"Updated Data for Name Key" forKey:#"Name"]; // Im updating its name here
[self updateMedicine:medicine1 inToArray:medicinesArray withUpdateType:kEditingTypeUpdateObj];
// Name of Medicine With D1 Id Has been Updated
// 2 - Adding New Medicince To your Array & saving updated Array In user defaults
NSMutableDictionary *myNewMed = [NSMutableDictionary new];
[myNewMed setValue:#"D150" forKey:#"DrugID"];
[myNewMed setValue:#"New Named Drug" forKey:[NSString stringWithFormat:#"Name"]];
NSLog(#"%#",[medicinesArray description]); // D150 will be Added
[self updateMedicine:myNewMed inToArray:medicinesArray withUpdateType:kEditingTypeAddObj];
NSLog(#"%#",[medicinesArray description]); // D150 Has been Added
// 3 - Deleting A Medicine From DB
// D150 Will be Deleted
[self updateMedicine:myNewMed inToArray:medicinesArray withUpdateType:kEditingTypeDeleteObj];
medicinesArray = [[[NSUserDefaults standardUserDefaults] objectForKey:#"MYMeds"] mutableCopy];
NSLog(#"%#",[medicinesArray description]); // D150 Was deleted
}
-(void)updateMedicine:(NSMutableDictionary *)updatedMedicine inToArray:(NSMutableArray <NSDictionary *>*)arrayOfMedicines withUpdateType:(EditingType)editType
{
switch (editType)
{
case kEditingTypeDeleteObj:
{
NSString *idOfRecivedDrug = updatedMedicine[#"DrugID"];
for(NSDictionary *med in arrayOfMedicines)
{
if([med[#"DrugID"] isEqualToString:idOfRecivedDrug])
{
NSInteger index = [arrayOfMedicines indexOfObject:med];
[arrayOfMedicines removeObjectAtIndex:index];
[[NSUserDefaults standardUserDefaults] setObject:arrayOfMedicines forKey:#"MYMeds"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}
}
break;
case kEditingTypeAddObj:
{
NSString *idOfRecivedDrug = updatedMedicine[#"DrugID"];
bool bIsDrugFound = false;
for(NSDictionary *med in arrayOfMedicines)
{
if([med[#"DrugID"] isEqualToString:idOfRecivedDrug])
{
bIsDrugFound = true;
break;
}
}
if(bIsDrugFound)
NSLog(#"Seems you are trying to add drug to existing UNIQUE ID,Try Updating Other than adding it as a new");
else
{
[arrayOfMedicines addObject:updatedMedicine];
[[NSUserDefaults standardUserDefaults] setObject:arrayOfMedicines forKey:#"MYMeds"];
[[NSUserDefaults standardUserDefaults] synchronize]; }
}
break;
case kEditingTypeUpdateObj:
{
NSString *idOfRecivedDrug = updatedMedicine[#"DrugID"];
for(NSDictionary *med in arrayOfMedicines)
{
if([med[#"DrugID"] isEqualToString:idOfRecivedDrug])
{
// Found Your Drug now Need to edit it's data
NSInteger index = [arrayOfMedicines indexOfObject:med];
[arrayOfMedicines replaceObjectAtIndex:index withObject:updatedMedicine];
[[NSUserDefaults standardUserDefaults] setObject:arrayOfMedicines forKey:#"MYMeds"];
[[NSUserDefaults standardUserDefaults] synchronize];
break;
}
else
NSLog(#"Your medicine was new,probably need to add it into the dictionary");
}
}
break;
default:
break;
}
}
#end
Happy To Help :)
Have Fun

whenever you save anything to NSUserDefaults then it get saved as Immutable i.e. whenever you fetch data from NSUserDefault it will be Immutable. So you need first to downcast it to Mutable object and then you can perform operations on that object. Each class whether its NSMutableArray or NSMutableDictionary have methods defined to add, remove & update the records.
To update any existing data you need first to check its availability in the data source & then perform further operation required. You can even perform all tasks in a single function by having a Enum structure define for each operation.

Related

call different method with same string in different value

I have two methods in my .m file .And i want to access by different values but in my Facebook variable gives nil value but if i use only one line and remove second line of object for key then it work fine for one method .
How i do this which work for my both methods
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[[NSUserDefaults standardUserDefaults] synchronize];
user=[defaults objectForKey:#"userid"];
facebook=[defaults objectForKey:#"FACEBOOKprofile"];
facebook =[defaults objectForKey:#"VLCC"];
if ([facebook isEqualToString:#"VLCCFACEBOOK"])
{
[self FacebookRecord];
}
else if([facebook isEqualToString:#"VLCC"])
{
[self VlccRecord];
}
assume that
Choice-1
// for accessing the both condition in same time
Initially Store the UserDefault value based on your method.
if you are access with facebook , on that time store the string like
[[NSUserDefaults standardUserDefaults] setObject:"VLCCFACEBOOK" forKey:#"FACEBOOKprofile"];
[[NSUserDefaults standardUserDefaults] synchronize];
if you are access with VLCC , on that time store the string like
[[NSUserDefaults standardUserDefaults] setObject:"VLCC" forKey:#"VLCCprofile"];
[[NSUserDefaults standardUserDefaults] synchronize];
and retrieve the both and check like
if ([[[NSUserDefaults standardUserDefaults]
objectForKey:#"FACEBOOKprofile"]isEqualToString:#"VLCCFACEBOOK"])
{
[self FacebookRecord];
}
if([[[NSUserDefaults standardUserDefaults]
objectForKey:#"VLCCprofile"] isEqualToString:#"VLCC"])
{
[self VlccRecord];
}
Choice-2
// for accessing single condition on single time
if you are access with facebook , on that time store the string like
[[NSUserDefaults standardUserDefaults] setObject:"VLCCFACEBOOK" forKey:#"FACEBOOKprofile"];
[[NSUserDefaults standardUserDefaults] synchronize];
if you are access with VLCC , on that time store the string like
[[NSUserDefaults standardUserDefaults] setObject:"VLCC" forKey:#"FACEBOOKprofile"];
[[NSUserDefaults standardUserDefaults] synchronize];
and retrieve the both and check like
if ([[[NSUserDefaults standardUserDefaults]
objectForKey:#"FACEBOOKprofile"]isEqualToString:#"VLCCFACEBOOK"])
{
[self FacebookRecord];
}
else if([[[NSUserDefaults standardUserDefaults]
objectForKey:#"FACEBOOKprofile"] isEqualToString:#"VLCC"])
{
[self VlccRecord];
}
In your code, the [self VlccRecord] method will always get called because you are overwriting the facebook variable.
facebook=[defaults objectForKey:#"FACEBOOKprofile"];
facebook =[defaults objectForKey:#"VLCC"];
If you are looking to append the two strings : Then use the stringbyAppendingString method.
int a = 10;
a = 20;
print >> a; //20
int a = 10;
print1 >> a; //10
a = 20;
print2 >> a; //20
Here,
a = facebook;
print1 = [self FacebookRecord];
print2 = [self VlccRecord];
I believe you'll understand.

Saving Users Scores

Ola, I am trying to find a way to save user scores so when it is closed from the background it saves their scores and when open again it displays that score once again. By the way the thing Im saving is a tap counter so when they tap it has to add onto their previous score.
Any suggestions?
Save:
int numberOfTaps = 42; // some number of taps
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
[standardUserDefaults setValue:[NSNumber numberWithInt:numberOfTaps] forKey:#"numberOfTaps"];
Retrieve:
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
int savedTaps = [[standardUserDefaults objectForKey:#"numberOfTaps"] intValue];
Updating For Further Information
Just to make it extremely easy:
Copy these methods somewhere in your .m
- (void) saveNumberOfTaps:(int)numberOfTaps {
[[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithInt:numberOfTaps] forKey:#"numberOfTaps"];
NSLog(#"Saved numberOfTaps: %i", numberOfTaps);
}
- (int) getNumberOfTaps {
if ([[[NSUserDefaults standardUserDefaults] objectForKey:#"numberOfTaps"] intValue]) {
int numberOfTaps = [[[NSUserDefaults standardUserDefaults] objectForKey:#"numberOfTaps"] intValue];
NSLog(#"Getting numberOfTaps: %i", numberOfTaps);
return numberOfTaps;
}
else {
NSLog(#"No taps saved yet");
return 0;
}
}
Then whenever you want to use these values, you can use the following:
// Save Taps
[self saveNumberOfTaps:14];
// Get Taps
int taps = [self getNumberOfTaps];
NSLog(#"taps retrieved: %i", taps);
It sounds like you're just starting out, so try using NSUserDefaults. Once you need something more advanced, look into Core Data.

iOS - Retrieving and saving scores of a game

I developed a small game , , so I manage something like this , when app lunches for the first time , it save the very first record , then after that it loads and compares the new records with the the first time record . but the problem is when user hit the record it should be saved the new record but it doesn't !!! and saves the first record again ! here is my code :
- (void)manageScores {
NSLog(#"managed");
NSString *tempScore = [NSString stringWithFormat:#"%#",scoreLabel.text];
GO.scoreNumber = [tempScore integerValue];
GO.bestScoreNumber = [tempScore integerValue];
GO.scores.text = [NSString stringWithFormat:#"score:%d",GO.scoreNumber];
GO.bestScore.text = [NSString stringWithFormat:#"best:%d",GO.bestScoreNumber];
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedOnce"])
{
// app already launched
NSLog(#"app already launched");
[GO loadBestScore];
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedOnce"];
[[NSUserDefaults standardUserDefaults] synchronize];
// This is the first launch ever
NSLog(#"first time lunch ever");
[GO saveBestScore];
}
if (GO.scoreNumber > GO.bestScoreNumber) {
//**************THIS METHOD DOESN'T WORK WHEN USER HIT THE RECORD*********/////
[GO saveBestScore];
//**************//
GO.scores.text = [NSString stringWithFormat:#"score:%d",GO.scoreNumber];
GO.bestScore.text = [NSString stringWithFormat:#"best:%d",GO.scoreNumber];
GO.shareScore.text = [NSString stringWithFormat:#"score:%d",GO.bestScoreNumber];
NSLog(#"new record");
}
if (GO.scoreNumber < GO.bestScoreNumber) {
GO.scores.text = [NSString stringWithFormat:#"score:%d",GO.scoreNumber];
GO.bestScore.text = [NSString stringWithFormat:#"best:%d",GO.bestScoreNumber];
GO.shareScore.text = [NSString stringWithFormat:#"score:%d",GO.bestScoreNumber];
[GO loadBestScore];
NSLog(#"NO NEW RECORD");
}
}
saving and loading scores (methods form GameOver.h/m (GO) )
- (void)saveBestScore {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setInteger:bestScoreNumber forKey:#"highScore"];
[defaults synchronize];
NSLog(#"BEST SCORE SAVED:%i",bestScoreNumber);
}
- (void)loadBestScore {
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
bestScoreNumber = (int)[prefs integerForKey:#"highScore"];
}
Looks like you should save scoreNumber rather than bestScoreNumber
Reason:
The following code wants to save the best score because GO.ScoreNumber > Go.BestScoreNumber.
if (GO.scoreNumber > GO.bestScoreNumber) {
//**************THIS METHOD DOESN'T WORK WHEN USER HIT THE RECORD*********/////
[GO saveBestScore];
//**************//
GO.scores.text = [NSString stringWithFormat:#"score:%d",GO.scoreNumber];
GO.bestScore.text = [NSString stringWithFormat:#"best:%d",GO.scoreNumber];
GO.shareScore.text = [NSString stringWithFormat:#"score:%d",GO.bestScoreNumber];
NSLog(#"new record");
}
But in saveBestScore, it stores bestScoreNumber, which is the previous highest score number.
[defaults setInteger:bestScoreNumber forKey:#"highScore"];
- (void)saveBestScore
This method doesn't take an argument, but it should.
If you're keeping scores as int, you should modify it to look like this:
- (void)saveBestScore:(int)bestScore
Then, on first launch, call saveBestScore with an argument of 0, just to get it initialize. In fact, if you write saveBestScore method correctly, you don't really need to check whether the app has already launched ever.
- (void)saveBestScore:(int)bestScore {
if (bestScore > [[[NSUserDefaults standardUserDefaults]
objectForKey:#"BestScore"] intValue]); {
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber
numberWithInt:bestScore] forKey:#"BestScore"];
}
- (int)loadBestScore {
return [[[NSUserDefaults standardUserDefaults] objectForKey:#"BestScore]
intValue];
}

Class Object is always null

This is the method when my app complete the downloading with contact which is coming from server. In for loop I am using AIContactParsarModal class and using dictionary I am setting the objects, but every time object value is null.
Thanks for the help and any help will be appreciable. Here is my code.
- (void)donewithContact {
self.allPersonDetailsDict = [[NSMutableDictionary alloc]initWithCapacity:1];
NSMutableArray *allKeysArray = [[NSMutableArray alloc] init];
NSString *prefix;
NSString *searchContactString = [[NSUserDefaults standardUserDefaults] objectForKey:#"AdvanceSearch"];
if ([searchContactString isEqualToString:#"SpecialSearch"]) {
[self.allPersonDetailsDict removeAllObjects];
}
if (getContactArray.count == 0 ){
[noContactLabel setHidden:NO];
} else {
for (AIContactParsarModal *contact in getContactArray) {
NSLog(#"contact LAST NAMES ARE %#",contact.lastName);
prefix = [[contact.lastName substringWithRange:NSMakeRange(0, 1)] uppercaseString];
if ([allKeysArray containsObject:prefix]) {
NSMutableArray *conatctsWithPrefix = [_allPersonDetailsDict objectForKey:prefix];
[conatctsWithPrefix addObject:contact];
} else {
[allKeysArray addObject:prefix];
NSMutableArray *conatctsWithPrefix = [[NSMutableArray alloc] initWithObjects:contact,nil];
[self.allPersonDetailsDict setObject:conatctsWithPrefix forKey:prefix];
NSLog(#"conatctsWithPrefix %#",conatctsWithPrefix);
}
}
[noContactLabel setHidden:YES];
}
NSLog(#"All Names First characters are:%#", allKeysArray);
[self.contactTableView reloadData];
[HUD hide:YES];
}
May be you are getting null value in contact by which you are getting null in conatctsWithPrefix
first print values stored in contact.

Method Called only After App Update

I have a Scenario , where i have to call a Method only once , whenever a User Will Update the Application or make a Fresh Install.
SO my Question is there is any particular Method to be called in App Delegate , After We update the Application , so if someone can tell me a another way to Achieve this , that would be really appreciated.
I would set a value in NSUserDefaults after you have called the function one time. See the accepted answer here: iOS : Call a method just one time
If you need to run after an update, you should include an always increasing number, or pull the build number, and check against the saved value. This will allow you to know if the app has been updated.
I am writing following function which uniquely checks if user has rated the current version, [self nowRate] is the function, please change this to whatever action you want to perform uniquely per version
-(void)rateApp {
NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleShortVersionString"];
NSMutableDictionary*appRatedForVersion = [[NSMutableDictionary alloc] init];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([[[NSUserDefaults standardUserDefaults] dictionaryRepresentation].allKeys containsObject:#"AppRateDictionaryData"]){
NSData *versionRateData = [defaults objectForKey:#"AppRateDictionaryData"];
appRatedForVersion = [NSKeyedUnarchiver unarchiveObjectWithData:versionRateData];
if ([appRatedForVersion objectForKey:version] != nil) {
NSNumber *isAppRatedForCurrentVersion = [appRatedForVersion objectForKey:version];
BOOL hasBeenRated = [isAppRatedForCurrentVersion boolValue];
if (hasBeenRated) {
//Action already performed in this version do whatever you like
} else {
[self nowRate];
}
} else {
[appRatedForVersion setObject:[NSNumber numberWithBool:YES] forKey:version];
NSData *versionRateData1 = [NSKeyedArchiver archivedDataWithRootObject:appRatedForVersion];
[defaults setObject:versionRateData1 forKey:#"AppRateDictionaryData"];
[defaults synchronize];
[self nowRate];
}
} else {
[appRatedForVersion setObject:[NSNumber numberWithBool:YES] forKey:version];
NSData *versionRateData = [NSKeyedArchiver archivedDataWithRootObject:appRatedForVersion];
[defaults setObject:versionRateData forKey:#"AppRateDictionaryData"];
[defaults synchronize];
[self nowRate];
}
}

Resources