there is a notification in my view controller that listens for preference changes and then updates the sound settings. When I change the sound settings and then kill the app, the prefs don't persist.
I need to check those prefs and set the sounds settings based on the persisted prefs on app launch
Can I do this from the app delegate, if so, how?
Here is the notification
- (void)preferencesDidChange:(NSNotification *)note
{
NSMutableArray *changedPreferences = note.object;
if ([changedPreferences containsObject:#"localPlayUISounds"]) {
[FHSSound setUISoundsEnabled:PREFS.localPlayUISounds];
}
else if ([changedPreferences containsObject:#"localPlayAlertSounds"]) {
[FHSSound setAlertSoundsEnabled:PREFS.localPlayAlertSounds];
}
else if ([changedPreferences containsObject:#"localEnablesDNDWhenDrivingCar"]) {
[self startMonitoringLocationIfEnabled];
}
}
I would use NSUserDefaults for this.
The system keeps a set of defaults for your app. You just have to add the values when they are set, and load them again in didFinishLaunchingWithOptions.
To save a value, when the user changes it:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:YES forKey:#"playSound"];
[defaults synchronize];
To load it at startup (in the app delegate):
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
bool playSound = [userDefaults boolForKey:#"playSound"];
One extra detail: on the first run of the app those values will return NO, so you want to detect that.
I would do something like this on the app delegate:
int runCount = (int)[userDefaults integerForKey:#"runCount"];
if (!runCount)
{
//first run, save the predefined values
}
else
{
//not first run, load the previously saved values
}
runCount++;
[userDefaults setInteger:runCount forKey:#"runCount"];
Take a look at this answer about saving user preferences.
Once your preferences are persisted, then you can reload them whenever the application starts by providing your own implementation of
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
You might also consider registering a handler for the UIApplicationDidBecomeActiveNotification event if you want to restore the sound settings whenever the application returns to the foreground even if it wasn't killed between activations.
The data was already persisting, I just had to find a way for the app to launch off of the persisted data. Here is what I did
- (void)viewDidLoad
{
[super viewDidLoad];
[self setSoundPreferences];
}
The notification is set to listen for the change in prefs
- (void)preferencesDidChange:(NSNotification *)note
{
NSMutableArray *changedPreferences = note.object;
if ([changedPreferences containsObject:#"localPlayUISounds"]) {
[FHSSound setUISoundsEnabled:PREFS.localPlayUISounds];
}
else if ([changedPreferences containsObject:#"localPlayAlertSounds"]) {
[FHSSound setAlertSoundsEnabled:PREFS.localPlayAlertSounds];
}
and lastly setting the preferences at launch
#pragma mark (launch)
- (void)setSoundPreferences
{
[FHSSound setUISoundsEnabled:PREFS.localPlayUISounds];
[FHSSound setAlertSoundsEnabled:PREFS.localPlayAlertSounds];
}
Related
I'm coding an iOS app in Objective C. I want the terms and conditions to appear when a user opens the app. I'm going to have a "don't show these again" button, which is all figured out using NSUserDefaults. What I can't figure out is how to make the terms and conditions disappear if the user hits "agree" (termsPressed) but NOT "don't show these again" (neverAgainPressed). I can hide them using button.hidden, but then as soon as the user returns to the main screen of the app the terms and conditions appear again, overlaying the main screen just as they did when the app first launched.
I've tried setting an NSUserDefault when "agree" is pressed, then resetting it when -applicationWillTerminate is called, but it appears that applicationWillTerminate is not called reliably when the app closes if it's closed from the background, and hence the user would never see the terms again even if they hadn't hit "don't show these again". Here's my code:
- (void)viewDidLoad {
// Hide all the terms and conditions elements if either "neverAgain" or "termsPressed" is true
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL neverAgain = [defaults boolForKey:#"neverAgain"];
if (neverAgain == TRUE){
_terms.hidden=YES;
_hideTerms.hidden=YES;
_background.hidden=YES;
_dontShow.hidden=YES;
}
BOOL termsPressed = [defaults boolForKey:#"termsPressed"];
if (termsPressed == TRUE){
_terms.hidden=YES;
_hideTerms.hidden=YES;
_background.hidden=YES;
_dontShow.hidden=YES;
}
NSLog(#"View loaded. termsPressed = %i", termsPressed);
NSLog(#"View loaded. neverAgain = %i", neverAgain);
[super viewDidLoad];
}
- (IBAction)neverAgainPressed:(id)sender {
BOOL neverAgain = TRUE;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:neverAgain forKey:#"neverAgain"];
[defaults synchronize];
NSLog(#"Don't show terms again pressed, neverAgain set to TRUE");
}
- (IBAction)termsPressed:(id)sender {
_terms.hidden = YES;
_hideTerms.hidden = YES;
_background.hidden = YES;
_dontShow.hidden = YES;
BOOL termsPressed = TRUE;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:termsPressed forKey:#"termsPressed"];
[defaults synchronize];
NSLog(#"Terms accepted, termsPressed set to TRUE");
}
- (void)applicationWillTerminate:(UIApplication *)application {
BOOL termsPressed = FALSE;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:termsPressed forKey:#"termsPressed"];
[defaults synchronize];
NSLog(#"Application entering background, termsPressed reset to FALSE");
}
#end
If there was a method that was called every time an app quit, or a version of .hidden that lasted the whole time an app was running, I'd be all set. Let me know if you guys have any ideas.
If you want "don't show these again", why reset? You can show at first launch and after some time (30 days for example):
BOOL ranBefore = [[NSUserDefaults standardUserDefaults] boolForKey:#"kRanBefore"];
NSDate *lastDate = [[NSUserDefaults standardUserDefaults] objectForKey:#"kLastCloseDate"];
NSTimeInterval timeDiff = [[NSDate date] timeIntervalSinceDate:lastDate];
int days = timeDiff / 86400;
if ((!ranBefore) || (days > 30)) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"kRanBefore"];
[[NSUserDefaults standardUserDefaults] synchronize];
[[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:#"kLastCloseDate"];
... show something
}
or reser at
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
return YES;
}
maybe someone could assist me.
When my app is launched for the first time a UIAlertController message appears and asks the user if they Need to GoTo the Settings App. using the following code.
dispatch_async(dispatch_get_main_queue(), ^{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"prefs:root=Wi-Fi"]];
});
Upon return to the App the same UIAlertController Message appears, using this code in the ViewDidLoad:
-(void)viewDidLoad {
if (launched == NO) {
launched = YES;
defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:launched forKey:#"boolKey"];
[defaults synchronize];
My code for the UIAlertController.
} else if (launched == YES) {
[self doSomeThing];
}
}
It appears my Bool Value is not being saved when the settings in the info.plist Application does not run in background: is set to YES, but if the Application does not run in background: is set to NO the else statement is executed. However this is not good because my app is suspended and when launched again I need the original message to appear and it does not, the app is restored to its last state.
Any Suggestions is greatly appreciated.
JZ
1.- Saving into NSUserDefaults does not save anything to the info.plist. NSUserDefaults has its own plist that gets wiped out if you remove your application.
If you want to prevent to launch the same alertview:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if([defaults boolForKey:#"boolKey"]) {
defaults = [NSUserDefaults standardUserDefaults];
[defaults setBool:YES forKey:#"boolKey"];
My code for the UIAlertController.
}
else {
//Whatever you need to do if its not first launch
}
Now next time you hit your viewDidLoad, since the boolForKey:#"boolKey" has a YES, you won't hit that code and the alertView won't get presented.
If user is opening application for the first time after downloading it, then I want to show a view controller with tutorial and then go to main application's View Controller.
But if user is already visited app before, then go to Main view controller.
How to implement that check in AppDelegate?
add in your app delegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([[NSUserDefaults standardUserDefaults]objectForKey:#"firsttime"]) {
[[NSUserDefaults standardUserDefaults]setObject:#"YES" forKey:#"firsttime"];
[[NSUserDefaults standardUserDefaults]synchronize];
}
}
If condition will check for the key is available in app or not.
If it is not available then it will save it to YES.
You can do as you want.
I would do that in your main view controller in method viewWillAppear. You can use NSUserDefaults. Here's an example:
Swift:
func isAppAlreadyLaunchedOnce() {
let defaults = NSUserDefaults.standardUserDefaults()
if let isAppAlreadyLaunchedOnce = defaults.stringForKey("isAppAlreadyLaunchedOnce"){
println("App already launched")
return true
} else {
defaults.setBool(true, forKey: "isAppAlreadyLaunchedOnce")
println("App launched first time")
//Show your tutorial.
return false
}
}
Objective-C:
- (void)isAppAlreadyLaunchedOnce {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *isAppAlreadyLaunchedOnce = [defaults stringForKey:#"isAppAlreadyLaunchedOnce"];
if (isAppAlreadyLaunchedOnce != nil) {
NSLog(#"App already launched");
return true;
} else {
NSLog(#"App launched first time");
[defaults setBool:true forKey:#"isAppAlreadyLaunchedOnce"];
//Show your tutorial.
return false;
}
}
And then run this method in your viewWillAppear:
Swift:
func viewWillAppear(animated: Bool) {
isAppAlreadyLaunchedOnce()
}
Objective-C:
- (void)viewWillAppear:(BOOL)animated {
[self isAppAlreadyLaunchedOnce];
}
NSUserDefaults will be cleared when user will uninstall app.
Add a flag in NSUserDefaults. When the user launches the app for the first time, the flag is not set. After checking the status of the flag, if the flag is not set the flag should be set.
you need to use boolForKey and setBool
if (![[NSUserDefaults standardUserDefaults]boolForKey:#"firsttime"]) {
[[NSUserDefaults standardUserDefaults]setBool:YES forKey:#"firsttime"];
[[NSUserDefaults standardUserDefaults]synchronize];
}
My app has a setting that allows me to turn off ui sounds, or turn it on. I use this for the value:
+ (void)setUISoundsEnabled:(BOOL)UISoundsEnabled
{
__UISoundsEnabled = UISoundsEnabled;
}
here is a notification in my view controller that listens for preference changes and then updates the sound settings.
- (void)preferencesDidChange:(NSNotification *)note
{
NSMutableArray *changedPreferences = note.object;
if ([changedPreferences containsObject:#"localPlayUISounds"]) {
[FHSSound setUISoundsEnabled:PREFS.localPlayUISounds];
}
2 questions:
First question: How would go about getting the settings saved in PREFS to match the settings saved in the BOOL at the top.
Second question:
How would I implement NSUserDefaults to save and load this data. To be specific where exactly am I implementing NSUserDefaults to save, and load this data. I'm not familiar with NSUserDefaults so examples would be very helpful
Please let me know if you need any more code or have any more questions
It's very simple. You use NSUserDefaults as-is; there's no need to subclass.
// read the setting when you start the app
flag = [[NSUserDefaults standardUserDefaults] boolForKey:"someKey"]
// set the setting if user can change it inside your app
[[NSUserDefaults standardUserDefaults] setBool:flag forKey:"someKey"]
It's probably best to create a settings plist file so that these settings can be then changed in the Settings app. If you do that, you should also listen to NSUserDefaultsDidChangeNotification in case user changes the settings in the Settings app while yours is in the background.
...
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(defaultsChanged:)
name:NSUserDefaultsDidChangeNotification object:nil];
...
- (void)defaultsChanged:(id)sender {
[[NSUserDefaults standardUserDefaults] synchronize];
// and notify your observers as necessary based on new settings...
}
Apps with advanced settings also often contain a pre-populated plist with "initial" default values, because your standardUserDefaults will contain no values until the user enters something in Settings app:
NSURL *defaultSettingsURL = [[NSBundle mainBundle] URLForResource:#"DefaultSettings" withExtension:#"plist"];
self.bundledDefaults = [NSDictionary dictionaryWithContentsOfURL:defaultSettingsURL];
You can dump bundledDefaults into settings on first run or simply use them as a back-up any time you read from the user defaults.
Here is how I solved it without using NSUserDefaults.
- (void)viewDidLoad
{
[super viewDidLoad];
[self setSoundPreferences];
}
The notification is set to listen for the change in prefs
- (void)preferencesDidChange:(NSNotification *)note
{
NSMutableArray *changedPreferences = note.object;
if ([changedPreferences containsObject:#"localPlayUISounds"]) {
[FHSSound setUISoundsEnabled:PREFS.localPlayUISounds];
}
else if ([changedPreferences containsObject:#"localPlayAlertSounds"]) {
[FHSSound setAlertSoundsEnabled:PREFS.localPlayAlertSounds];
}
and lastly setting the preferences at launch
#pragma mark (launch)
- (void)setSoundPreferences
{
[FHSSound setUISoundsEnabled:PREFS.localPlayUISounds];
[FHSSound setAlertSoundsEnabled:PREFS.localPlayAlertSounds];
}
I'm trying to make my app launch a different view on the first time it is loaded up. I've got this code at the moment which implements that something should happen when the app is first launched. I've got this code but it lacks the code to open Initialviewviewcontroller. I have no idea how to do this so help would be much appreciated
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
BOOL hasRunBefore = [defaults boolForKey:#"FirstRun"];
if (!hasRunBefore) {
[defaults setBool:YES forKey:#"FirstRun"];
[defaults synchronize];
// what goes here??
else
{
NSLog (#"Not the first time this controller has been loaded");
So I should launch a different view controller in the if statement. But what should I put ?
Solution No. 1
I've written a simple snippet for this thing because I use it quite a lot. You can find it here.
Feel free to use it, fork it or modify it!
Solution No. 2
You can do something like this in your AppDelelegate.m
Add this simple method at the bottom:
- (BOOL)hasEverBeenLaunched
{
// A boolean which determines if app has eer been launched
BOOL hasBeenLaunched;
// Testig if application has launched before and if it has to show the home-login screen to login
// to social networks (facebook, Twitter)
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasAlreadyLaunched"]) {
// Setting variable to YES because app has been launched before
hasBeenLaunched = YES;
// NSLog(#"App has been already launched");
} else {
// Setting variable to NO because app hasn't been launched before
hasBeenLaunched = NO;
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasAlreadyLaunched"];
[[NSUserDefaults standardUserDefaults] synchronize];
// NSLog(#"This is the first run ever...");
}
return hasBeenLaunched;
}
After implementation of this method you can use it like that:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Determining Storyboard identifier for first view
NSString *storyboardID = [self hasEverBeenLaunched]? #"MainView" : #"LoginView";
// Setting proper view as a rootViewController
self.window.rootViewController = [self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:storyboardID];
return YES;
}