How to implement NSUserDefaults to save preferences - ios

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];
}

Related

User not logged out from Dropbox and Google drive when deleting app

I am using Dropbox and Google Drive integration in my iOS app. I can fetch files from both drives and view listing in tableview. However, when I delete the app on my iPhone without logout from these drives, it still shows logged in when I install new app. How to logout user when I delete the app or remove session?
For Dropbox i am using ObjectiveDropboxOfficial apiV2 and for Google Drive i am using GoogleAPIClientForREST, GTMSessionFetcher etc libraries.
My code:
[DBClientsManager setupWithAppKey:#"my-key"];
[DBClientsManager authorizeFromController:[UIApplication sharedApplication]
controller:self openURL:^(NSURL *url) {
[[UIApplication sharedApplication] openURL:url];
}];
//AppDelegate
if ([DBClientsManager handleRedirectURL:url])
{
if (DBClientsManager.authorizedClient || DBClientsManager.authorizedTeamClient) {
// NSLog(#"App linked successfully!");
// At this point you can start making API calls
NSNotification *notification = [NSNotification notificationWithName:#"DropboxLoggedIn" object:nil];
[[NSNotificationCenter defaultCenter] postNotification:notification];
}
return YES;
}
If these services are designed this way I assume they save credentials in keychain which persists data and your application is already logged in when reinstalled or keychain is anyhow transfered.
If this is not your desired effect I can only assume you will need to log out from these services manually. This means you will need to track these logins and logouts and then when the app starts simply log out from all services which have not been tracked as logged in by you.
It is an ugly thing to do but it is a solution:
When a service is logged in save a value in user defaults
- (void)serviceDidLogin:(ServiceType)type {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:[self keyForServiceType: type]];
}
Then when it is logged out you need to clear it
- (void)serviceDidLogout:(ServiceType)type {
[[NSUserDefaults standardUserDefaults] removeObjectForKey:[self keyForServiceType: type]];
}
Then when app starts you need to log out from all of the services that you have no recording of being logged into:
- (void)logOutFromAllUnusedService {
for(int i=0; i<ServiceTypeCount; i++) {
ServiceType type = i;
if([[NSUserDefaults standardUserDefaults] boolForKey:[self keyForServiceType: type]] == NO) {
[self logoutFromService:type];
}
}
}
No matter how you do this but my situation assumes ServiceType is an enum like so:
typedef enum {
// Never assign values to enums
ServiceTypeDropbox,
ServiceTypeGoogleDrive,
ServiceTypeCount, // Always last of the valid
// Move deprecated services here
ServiceTypeDeprecated1 // TODO: remove at some point
} ServiceType;

- (void)applicationWillEnterForeground:

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.

How to check if user visit my iOS app for the first time?

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];
}

Use didFinishLaunchingWithOptions to save preferences

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];
}

Open different view on initial run

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;
}

Resources