iOS Game Center: Creating Lifetime Achievements - iphone

I'm trying to add a Lifetime achievement to my iOS app. That is, how many times the user performed the single action since he played the game for the first time. In other words, how many kills he got since... ever. I'm using the code based on the one provided by Apple:
.h file:
int64_t lifetimeScore;
IBOutlet UILabel *lifetimeScoreLabel;
.m file:
- (IBAction) increaseScore {
self.lifetimeScore = self.lifetimeScore + 1;
lifetimeScoreLabel.text = [NSString stringWithFormat: #"%ld", self.lifetimeScore];
// Saving the Score:
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
[defaults setInteger:lifetimeScore forKey: #"Score"];
[defaults synchronize];
[self checkAchievements]; }
- (void)viewDidLoad {
[super viewDidLoad];
// Loading the Lifetime Score:
self.lifetimeScore = [[NSUserDefaults standardUserDefaults] integerForKey: #"Score"];
lifetimeScoreLabel.text = [NSString stringWithFormat: #"%ld", self.lifetimeScore];
(...)
}
I works great, no warnings, but each time I close the app, deletes it from the Multitask bar and open the app again, the lifetimeScore goes back to zero. I'm trying to save the int_64 using NSUserDefaults but so far I can't make it work... any ideas?
EDIT: The code was fixed and now it's working 100% in case anyone wants to use it. The complete source code can found here: http://mobile.tutsplus.com/tutorials/iphone/ios-sdk-game-center-achievements-and-leaderboards-part-2/

#"Store" should be #"Score", just so I can get the points... :)

Related

Multiple Images for iOS app's Background

I have seen different apps that the image will change each time the app is opened on the view background. How is this accomplished?
It sounds like what you are seeing is the cached screenshot the iOS system is making of your app just before it puts it into the background.
This is handled automatically, and you do have the opportunity to intercept this.
Check out this answer, you can put an image over your app just as it's entering the background, this will be cached and used to relaunch the app.
add background_0.png, background_1.png, background_2.png, background_3.png etc to your project.
#define max_image_number 3
Add the following to your viewDidLoad method:
-(void)viewDidLoad {
[super viewDidLoad];
[self updatedBackgroundImage];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateBackgroundImage) name:UIApplicationWillEnterForegroundNotification object:nil];
}
-(void) updateBackgroundImage{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (![defaults integerForKey:#"imageNumber"]) {
[defaults setInteger:0 forKey:#"imageNumber"];
}
int i = [defaults integerForKey:#"imageNumber"];
myUIimageView.image = [UIImage imageNamed:[NSString stringWithFormat:#"background_%d.png",i]];
i++;
if (i > max_image_number) {
[defaults setInteger:0 forKey:#"imageNumber"];
}else{
[defaults setInteger:i forKey:#"imageNumber"];
}
...
}
The above will update the image when the viewController is first loaded and also when it returns from running in the background. I assume this is what was missing for you :).
Remember to stop listening for the notification in the viewDidUnload method and you should be set.
Looking at the screenshots in the App Store of the application you mention, it looks like there's just a UIImageView at the back of the view hierarchy for the main menu. You can change this by assigning a UIImage object to its image property.
Suppose you want to change one or more view's background images, depending on the application launch.
Save an int x = 0 into NSUserDefaults. On each application launch increment it by 1. When you are to present a view, check that int and set a background image like this:
switch(x%3){// %3 just to make it a bit random
case 0://set this image;
break;
case 1://set that image
break;
//and so on
}

How to: Get order of tabs, save into NSUserDefaults then retrieve

I have a default UITabBarApplication with default views and tabs... etc. I have eight tabs that have the customizable option, so users can reorder tabs.
How do I get the order of the tabs, save that into NSUserDefaults and retrieve it when the app loads back up. I'd like to see some code examples.
Here is where I'm at so far:
- (void)applicationWillTerminate:(UIApplication *)application {
/*
Called when the application is about to terminate.
See also applicationDidEnterBackground:.
*/
NSMutableArray *vcArray = [NSMutableArray arrayWithCapacity:8];
NSArray *savedViews = tabBarController.viewControllers;
for (UIViewController *theVC in savedViews){
[vcArray addObject:theVC.title];
}
[[NSUserDefaults standardUserDefaults] setObject:vcArray forKey:#"tabLayout"];
}
- (void)tabBar:(UITabBar *)tabBar didEndCustomizingItems:(NSArray *)items changed:(BOOL)changed {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSArray *tabLayout = [defaults arrayForKey:#"tabLayout"];
NSMutableArray *orderedLayout = [NSMutableArray arrayWithCapacity:8];
NSArray *defaultOrder = tabBarController.viewControllers;
for (int i =0; i < 8; i++){
for (UIViewController *theVC in defaultOrder) {
if ([theVC.title isEqualToString:[tabLayout objectAtIndex:i]]) {
[orderedLayout addObject:theVC];
}
}
}
tabBarController.viewControllers = orderedLayout;
}
This code doesn't seem to work in the simulator. I reorder the tabs and hit done then stop the app, but when I hit build and run again the app reverts to default. Why isn't the code above working?
Thanks.
One comment:
NSUserDefaults don't always work with the simulator. If you're doing a new build each time, then of course everything gets reset.
Two suggestions:
put an NSLog in the latter method to make sure that it's being called
build the app to a device, move the tabs around, close the app (completely -- not just in background), open the app again, behold what happens.

How to run a code for only once?

I'm working on an iPhone app, and I'm wondering if I could run some code segment for only once (in other words: an initialization code, that I want it to be executed only at the very first run).
Here's my code, that I execute it at didFinishLaunchingwithOptions method:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// Add the tab bar controller's view to the window and display.
[self.window addSubview:tabBarController.view];
[self.tabBarController setSelectedIndex:2];
[self.window makeKeyAndVisible];
[self createPlist1];
[self createPlist2];
[self createPlist3];
return YES;
}
I want the last three messages to be executed only at the very first run. I thought I could use the UserDefaults and set a key after these messages executes (at the first run) and check for the value of that key at each run, but I'm feeling that there's a better idea -which I don't know.
Thanks in advance.
Using a setting (via NSUserDefaults) is how it's normally done. For added benefit, give the setting the meaning of "last run version"; this way, you'll get a chance to run code not only once per app lifetime, but also once per version upgrade.
That said, your run-once code has persistent side effects, right? Those plists go somewhere probably. So you can check if they exist before creating them. Use the result of the run-once code as a trigger for running it again.
EDIT:
NSUserDefaults *Def = [NSUserDefaults standardUserDefaults];
NSString *Ver = [Def stringForKey:#"Version"];
NSString *CurVer = [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString*)kCFBundleVersionKey];
if(Ver == nil || [Ver compare:CurVer] != 0)
{
if(Ver == nil)
{
//Run once per lifetime code
}
//Run once-per-upgrade code, if any
[Def setObject:CurVer forKey:#"Version"];
}
A much simpler possible solution ->
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:#"FirstTimeBool"]==nil)
{
[defaults setObject:#"YES" forKey:#"FirstTimeBool"];
... //Code to be executed only once until user deletes the app!
...
this is what I used:
static dispatch_once_t once;
dispatch_once(&once, ^ {
// run once code goes here
});
I think you're on the right track with the User Defaults, something like:
-(BOOL)isInitialRun
{
NSNumber *bRun = [[NSUserDefaults standardUserDefaults] valueForKey:#"initialRun"];
if (!bRun) { return YES; }
return [bRun boolValue];
}
-(void)setIsInitialRun:(BOOL)value
{
[[NSUserDefaults standardUserDefaults] setBool:value forKey:#"initialRun"];
}
Then in your app delegate:
if ([self isInitialRun])
{
[self createPlist1];
[self createPlist2];
[self createPlist3];
[self setIsInitialRun:NO];
}
To my knowledge, the way you propose is the only option. Save a key to NSUserDefaults after you ran it for the first time and check for the existence of said key.
You could however, also check in each of your functions (the createPlist1 - 3 functions) run a check if the PList is already there. Would be a bit cleaner.
One thing I would add to #Seva Alekseyev answer:
After you make any changes (i.e. [Def setObject:CurVer forKey:#"Version"];) you should call [Def synchronize]
I had a problem where changes made to NSUserDefaults using setObject were not getting saved, until I used synchronize.

How can I use registerDefaults: properly, to load my default settings?

I've been told that I need to use the registerDefaults: to load the NSUserDefaults if the user has never changed the app settings.
In my AppDelegate I am using the following code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// Load default defaults
[[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Defaults" ofType:#"plist"]]];
[[NSUserDefaults standardUserDefaults] synchronize];
// Add the view controller's view to the window and display.
[window addSubview:viewController.view];
[window makeKeyAndVisible];
return YES;
}
I copied the Root.plist of my settings bundle to the ressources folder of my project and renamed it Defaults.plist
Now in the viewDidLoad method of my view controller i'm using the following code to load the settings:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ((([[defaults objectForKey:kToggleSwitch] isEqualToString:#"Enabled"]) ? YES : NO)) {
//Do Stuff
//BTW i'm using strings for the True/False the values of the ON/OFF positions of the UISwitch
}
Even with doing all this, my default settings stored in the Root.plist of my settings bundle don't get loaded. The only way they load is if the user actually goes into the iPhone settings and simply views my app's settings page.
Clearly i'm doing something wrong here can someone help me out. Btw i'm running it using iOS 4.1
when I made my app, i never knew of any defaults to be registered, so I made a poor man's defaults:
I have a bool variable: haveConfig, which is checked when the settings are loaded:
haveConfig = [prefs boolForKey:#"haveConfig"];
if (!haveConfig)
{
return;
}
/* Load settings here */
....
First time the "haveConfig" will be false, so I don't load them. When the user first changes one of the settings, they are stored together with the "haveConfig" variable:
haveConfig = true;
[prefs setBool:haveConfig forKey:#"haveConfig"];
...
I know it's not perfect, but it works :-)

Registering UIRemoteNotificationTypes

Will this be done on every launch of the app? Since it is in the applicationDidFinishLaunching method. Is there a way to do this once and for all?
Thanks!
Hi you can take advantage of preferences..
here is the code to determine application is running for first time or not:(check this in applicationDidFinishLaunching method.)
NSUserDefaults *defaults =[NSUserDefaults standardUserDefaults];
if([defaults objectForKey:#"Remote_Notification_Registered"] == nil)
{
// This is first run ...
// Do your only one time initialization
[defaults setValue:#"1" forKey:#"Remote_Notification_Registered"];
}
else
{
// Not first run
}
Hope this will help.