I am pasring json data from a url and taking one of teh values to NSUserdefaults to use it it application view .
Example user will enter an unique code given to him and this code will be appended to the requested URL , accoridng to the requested code the json value will be changed .
At present when i enter code it is fetching and saving NSuserdefaults and passing it to the label filed in view . But when i enter new code and fecth new data it is not updating in the view . If i restart the application and enter new code then it is showing new Value . Can somebody help me . here is the code
NSString *jsonUrlString = [[NSString alloc] initWithFormat:#"http://mywebsite.com/json/code.php?user=%#",_opcodeField.text];
NSURL *url = [NSURL URLWithString:[jsonUrlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:url];
[_indicator startAnimating];
_indicator.hidesWhenStopped = YES;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
//-- JSON Parsing
NSMutableArray *result = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:nil];
NSLog(#"settings = %#",result);
for (NSDictionary *dic in result)
{
[[NSUserDefaults standardUserDefaults] setObject:[dic objectForKey:#"footer"] forKey:#"op_footer"];
[[NSUserDefaults standardUserDefaults] synchronize];
[_indicator performSelectorOnMainThread:#selector(stopAnimating) withObject:nil waitUntilDone:YES];
}
View code is:
(void)viewDidLoad {
[super viewDidLoad];
_Footer.text = [[NSUserDefaults standardUserDefaults] objectForKey:#"op_footer"];
How can I show new values without restarting application . Is there any code to be added in view ? I can see json is fetching correctly newvalues when user entered new code
Thank you.
Are you sure viewDidLoad is the best place to add this code?.
Try to add your code in some method which is called more often. For debugging I usually add some button just to call a NSLog to see what happens.
Try this if you want to load the text all the time you come to the viewcontroller.
- (void)viewWillAppear:(BOOL)animated {
_Footer.text = [[NSUserDefaults standardUserDefaults] objectForKey:#"op_footer"];
}
Or you can try this if you want to load it to a label after getting the new code.
- (void)updateLabel {
_Footer.text = [[NSUserDefaults standardUserDefaults] objectForKey:#"op_footer"];
}
Hope this helps.. :)
Why not move this line...
_Footer.text = [[NSUserDefaults standardUserDefaults] objectForKey:#"op_footer"];
to here (unless of course this is in a different class)...
NSMutableArray *result = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableContainers error:nil];
NSLog(#"settings = %#",result);
for (NSDictionary *dic in result)
{
[[NSUserDefaults standardUserDefaults] setObject:[dic objectForKey:#"footer"] forKey:#"op_footer"];
[[NSUserDefaults standardUserDefaults] synchronize];
[_indicator performSelectorOnMainThread:#selector(stopAnimating) withObject:nil waitUntilDone:YES];
}
//Update it straight away
_Footer.text = [[NSUserDefaults standardUserDefaults] objectForKey:#"op_footer"];
Also bit confused why you are looping through dictionaries here? Is there more than one dictionary? If there is the last value will always be the one that gets stored in UserDefaults.
You are also stopping the animation in the loop as well?
The user defaults plist can take time to save and update. It doesn't happen instantly. Try triggering the call after a pause of some kind. Test with a button.
Edit: you can add an observer to the default so that you know it has updated - Cocoa - Notification on NSUserDefaults value change?
Related
I'm having trouble with my In-App Purchases. Upon leaving the MasterViewController (where the IAP items are listed and bought) I come to the ViewController which in the viewDidLoad it checks for purchased items, but I also have a viewWillAppear checking for purchased items and unlocking those purchased items right when the ViewController is loaded.
The problem is, every time the ViewController is reloaded or refreshed, left and then gone back to, the viewWillAppear checks for purchased items and unlocks the items again, which in my case the purchased items come from javascript files, which happens to load the JS multiple times, cluttering the app with the same IAP item over and over again.
For instance, if you purchased 'Bookmarks', every time you reload the ViewController, you'll get another 'Bookmarks' tool, over and over again.
How do I get it so that the IAP is loaded only one time?
My viewWillAppear looks like this:
- (void)viewWillAppear:(BOOL)animated {
// Check To See If The Uniques IAP Has Been Paid For
if (![[[NSUserDefaults standardUserDefaults] objectForKey:SHOW_UNIQUES_KEY] isEqualToString: #"YES"]){ // NEW CODE
// Code to show bookmarks
} else {
NSString *jsString = [NSString stringWithContentsOfURL:[[NSBundle mainBundle] URLForResource:#"Uniques" withExtension:#"js"] encoding:NSUTF8StringEncoding error:nil];
[viewWeb stringByEvaluatingJavaScriptFromString:jsString]; }
NSLog(#"Shows uniques?: %#", [[NSUserDefaults standardUserDefaults] objectForKey:SHOW_UNIQUES_KEY]);
// Check To See If The PL Numbers IAP Has Been Paid For
if (![[[NSUserDefaults standardUserDefaults] objectForKey:SHOW_PORTAL_LEVEL_KEY] isEqualToString: #"YES"]){ // NEW CODE
// Code to show bookmarks
} else {
NSString *jsString = [NSString stringWithContentsOfURL:[[NSBundle mainBundle] URLForResource:#"PortalLevel" withExtension:#"js"] encoding:NSUTF8StringEncoding error:nil];
[viewWeb stringByEvaluatingJavaScriptFromString:jsString]; }
NSLog(#"Shows portal levels?: %#", [[NSUserDefaults standardUserDefaults] objectForKey:SHOW_PORTAL_LEVEL_KEY]);
// Check To See If The PN IAP Has Been Paid For
if (![[[NSUserDefaults standardUserDefaults] objectForKey:SHOW_PORTALS_KEY] isEqualToString: #"YES"]){ // NEW CODE
// Code to show bookmarks
} else {
NSString *jsString = [NSString stringWithContentsOfURL:[[NSBundle mainBundle] URLForResource:#"PortalNames" withExtension:#"js"] encoding:NSUTF8StringEncoding error:nil];
[viewWeb stringByEvaluatingJavaScriptFromString:jsString]; }
NSLog(#"Shows portal names?: %#", [[NSUserDefaults standardUserDefaults] objectForKey:SHOW_PORTALS_KEY]);
// Check To See If The Bookmarks IAP Has Been Paid For
if (![[[NSUserDefaults standardUserDefaults] objectForKey:SHOW_BOOKMARKS_KEY] isEqualToString: #"YES"]){ // NEW CODE
// Code to show bookmarks
} else {
NSString *jsString = [NSString stringWithContentsOfURL:[[NSBundle mainBundle] URLForResource:#"bookmarks" withExtension:#"js"] encoding:NSUTF8StringEncoding error:nil];
[viewWeb stringByEvaluatingJavaScriptFromString:jsString]; }
NSLog(#"Shows bookmarks?: %#", [[NSUserDefaults standardUserDefaults] objectForKey:SHOW_BOOKMARKS_KEY]);
// Check To See If The Poke A Bowl Ad Has Been Paid For
if (![[[NSUserDefaults standardUserDefaults] objectForKey:SHOW_ADS_KEY] isEqualToString: #"No"]){ // NEW CODE
// Code to show ads
_PokeABowlAd.hidden = NO;
} else {
_PokeABowlAd.hidden = YES;
}
NSLog(#"Shows ads?: %#", [[NSUserDefaults standardUserDefaults] objectForKey:SHOW_ADS_KEY]);
}
I think what you should do is make it so that each one is called at most once. You should use NSUserDefaults to accomplish this. Do something like this:
Put this in your AppDelegate.m:
//One new key for each IAP - put at the top of the AppDelegate.m and VC.m
#define IAP_FIRST_USED #"IAP 1 Used"
...
#define IAP_LAST_USED #"IAP X Used"
and
// In applicationDidFinishLaunchingWithOptions, should be the first method listed
[[NSUserDefaults standardUserDefaults] setObject: #"No" forKey: IAP_FIRST_USED];
...
[[NSUserDefaults standardUserDefaults] setObject: #"No" forKey: IAP_LAST_USED];
[[NSUserDefaults standardUserDefaults] synchronize];
And then in the VC.m modify viewWillAppear to be like this (this is an example of what just the first IAP should look like):
// Check To See If The PL Numbers IAP Has Been Paid For
if (![[[NSUserDefaults standardUserDefaults] objectForKey:SHOW_PORTAL_LEVEL_KEY] isEqualToString: #"YES"]){ // NEW CODE
// NEW CODE BEGINS HERE
if ([[[NSUserDefaults standardUserDefaults] objectForKey:IAP_FIRST_USED] isEqualToString: #"No"]){
[[NSUserDefaults standardUserDefaults] setObject: #"Yes" forKey: IAP_FIRST_USED];
[[NSUserDefaults standardUserDefaults] synchronize];
// Code to show bookmarks
// End of code to show bookmarks
// NEW CODE ENDS HERE
}
} else {
NSString *jsString = [NSString stringWithContentsOfURL:[[NSBundle mainBundle] URLForResource:#"PortalLevel" withExtension:#"js"] encoding:NSUTF8StringEncoding error:nil];
[viewWeb stringByEvaluatingJavaScriptFromString:jsString];
}
Also remember to include the define statements in VC.m
Today I encountered a rather confusing Problem:
When my app starts I'm downloading some things and after parsing the responses I write the data to the NSUserDefaults.
Now this was working fine until I recently discovered that sometimes my entire UI freezes and wont unfreeze until I relaunch the app. When I hit pause programm execution in the debugger I get:
The documentation says that NSUserDefaults is threadsafe and from what I can see no other threads are trying to access the user defaults...
Any Ideas on how to resolve/further debug the issue would be greatly appreciated!
Thanks in advance!
Code that's causing the problem:
This is called after I receive a response the webservice:
+ (void)updateWithData:(NSData *)data
{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSError *error;
NSArray *decodedData;
if (data) {
decodedData = [NSPropertyListSerialization propertyListWithData:data
options:NSPropertyListImmutable
format:NULL
error:&error];
}
if (!error && decodedData) {
[userDefaults setObject:decodedData forKey:kUserDataUserDefaultsKey];
}
else{
NSLog(#"%#", error.localizedDescription);
}
}
Updated with new code sample:
You should convert the decodedData into archived data in order to store it in NSUserDefaults:
if (!error && decodedData) {
NSData *archivedData = [NSKeyedArchiver archivedDataWithRootObject: decodedData]];
[userDefaults setObject: archivedData forKey:kUserDataUserDefaultsKey];
}
And retrieve the data with this:
NSArray *storedData = [NSKeyedUnarchiver unarchiveObjectWithData:[[NSUserDefaults standardUserDefaults] objectForKey:kUserDataUserDefaultsKey]];
changesOperation.fetchRecordChangesCompletionBlock = ^(CKServerChangeToken *serverChangeToken, NSData *clientChangeTokenData, NSError *operationError){
//encode and save token
NSData *encodedServerChangeToken = [NSKeyedArchiver archivedDataWithRootObject:serverChangeToken];
[[NSUserDefaults standardUserDefaults] setObject:encodedServerChangeToken forKey:fetchToken];
[[NSUserDefaults standardUserDefaults] synchronize];
//handle more - **this causes a retain cycle**
if(changesOperation.moreComing){
}
};
Hi just wondering in the fetchRecordChangesCompletionBlock, the docs say:
If the server is unable to deliver all of the changed results with this operation object, it sets this property to YES before executing the block in the fetchRecordChangesCompletionBlock property. To fetch the remaining changes, create a new CKFetchRecordChangesOperation object using the change token returned by the server.
In the code above this causes a retain cycle so how should this be handled and when recreating the operation is it possible to use the same completion blocks alreay created?
You should define a weak changesoperation like this
__weak CKFetchNotificationChangesOperation *weakChangesOperation = changesOperation;
changesOperation.fetchRecordChangesCompletionBlock = ^(CKServerChangeToken *serverChangeToken, NSData *clientChangeTokenData, NSError *operationError){
...
if(weakChangesOperation.moreComing){
}
Is it possible to save and load data on Today Extension using NSUserDefaults?
After closing the Notification Center, the widget behaves like an app which is terminated, so any data results lost. How could I solve this issue?
This is my code:
NSUserDefaults *defaults;
- (void)viewDidLoad {
[super viewDidLoad];
defaults = [NSUserDefaults standardUserDefaults];
NSArray *loadStrings = [defaults stringArrayForKey:#"savedStrings"];
if ([loadStrings objectAtIndex:0] != nil) {
[display setText:[NSString stringWithFormat:#"%#", [loadStrings objectAtIndex:0]]];
}
if ([loadStrings objectAtIndex:1] != nil) {
calculatorMemory = [NSString stringWithFormat:#"%#", [loadStrings objectAtIndex:1]].doubleValue;
}
}
- (IBAction)saveData:(id)sender {
NSString *displayString;
NSString *memoryString;
NSArray *saveStrings = [[NSArray alloc] initWithObjects: displayString, memoryString, nil];
defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:saveStrings forKey:#"savedStrings"];
[defaults synchronize];
}
You need to use app group identifier instead of com.*
For instance:
NSUserDefaults *shared = [[NSUserDefaults alloc]initWithSuiteName:#"group.company.appgroup"];
Don't forget to synchronise when you store data
[shared synchronize];
You need to add the App Group stuff detailed under here and then if it actually worked (pretty iffy under beta) it should allow you to share NSUserDefault data like normal between the host and widget.
Edit: Normal NSUserDefaults does not work. Apple has implemented a new method. To use, simply redefine your NSUserDefaults instance like this:
NSUserDefaults *shared = [[NSUserDefaults alloc]initWithSuiteName:#"com.you.app.container"];
For anyone wondering how in the world do you save and get values then look at this code.
In your regular app add this to save whatever you like in your *.m file.
NSUserDefaults *shared = [[NSUserDefaults alloc]initWithSuiteName:#"group.yourcompanyname.TodayExtensionSharingDefaults"];
//save dic
[shared setObject:dictionary2 forKey:#"dicForTodayWidget"];
//save array
[shared setObject:tempArray2 forKey:#"arrayForTodayWidget"];
//save some value
[shared setObject:#"1234" forKey:#"myValForTodayWidget"];
[shared synchronize];
In your today widget under TodayViewController.m in viewDidLoad add this.
NSUserDefaults *shared = [[NSUserDefaults alloc]initWithSuiteName:#"group.yourcompanyname.TodayExtensionSharingDefaults"];
//get dic
NSMutableDictionary *dictionary = [shared objectForKey:#"dicForTodayWidget"];
You first need the App Groups set up for both targets (application and the extension).
Then, use the
NSUserDefaults *shared = [[NSUserDefaults alloc]initWithSuiteName:#"group.company.myapp"];
to obtain the defaults object which you can read from/write to as usual.
If you want to be notified of changes to the defaults, use the NSUserDefaultsDidChangeNotification in your widget (or app).
For a step-by-step tutorial explaining all this, take a look at this blog post.
#edukulele
Today Extension and Main app run on two processes. Today Extension can't receive NSUserDefaultsDidChangeNotifications. I tried use MMWormhole. It is very good.
I am about to release an iPhone app for a small business. The app has a call button which calls the business with an ibaction. I would like to know if i can find out how many times this action occurs so i can get a better idea of how effective the app is, and how to go about it.
Thanks for your time.
You could implement a web-service with a simple method which will increment a counter on a server. And call it asynchronously every time user taps a button. I would do it like this:
#define TAP_COUNT_KEY #"tapcnt"
#define MAKE_URL(X) [NSURL URLWithString:[NSString stringWithFormat:#"http://my_service_address?cnt=%d", X]]
dispatch_queue_t serviceQueue = dispatch_queue_create("Tap counter queue", NULL);
dispatch_async(serviceQueue, ^{
NSUInteger counter = [[NSUserDefaults standardUserDefaults] integerForKey:TAP_COUNT_KEY] + 1;
NSURL *serviceUrl = MAKE_URL(counter);
NSError *error = nil;
[NSString stringWithContentsOfURL:serviceUrl encoding:NSUTF8StringEncoding error:&error];
if (error)
NSLog(#"%#", [error localizedDescription]);
else
counter = 0;
[[NSUserDefaults standardUserDefaults] setInteger:counter forKey:TAP_COUNT_KEY];
});
dispatch_release(serviceQueue);
On IBAction method, you can increment counter by +1. Then you can store it into NSUserDefaults by key-value pair. And every time, increase counter and store it. So it'll maintain counter state until application will be uninstalled.
int counter;
-(IBAction)click:(id)sender
{
NSUserDefaults *_countDefaults = [NSUserDefaults standardUserDefaults];
counter = [[_countDefaults integerForKey:#"Counter"] intValue];
counter++;
[_countDefaults setInteger:counter forKey:#"Counter"];
[_countDefaults synchronize];
}
Hopefully, you'll get idea.
Thanks.
You can increase your button tag value and access it where ever you want,
-(IBAction)btnPanic:(id)sender
{
UIButton *btn=(UIButton *)sender;
btn.tag=btn.tag+1;
}