iOS: How to Create a "User Preference" Feature - ios

I have an application that is based on a UINavigationController; I wish to add a "Setting" page where the user will have the ability to set some features like Language and some other preferences.
Currently the UIViewController where I wish to have the Setting fields in is 2 levels under the RootViewController (i.e. there is a "main view" >> you click on a button and enter another UIViewController and form there you should be able to enter the Setting UIViewController).
I'm not clear about how I'm supposed to save this data and how to call it upon application load.
I read some blogs about NSUserDefaults and about Singleton but I'm not clear how should I use them.
Where should I create the data attributs that will later on maintain the user preferences? Should I create them on the AppDelegate or on the MySettingsViewController (the UIViewController that I'm creating)?
Should I use a Singleton attribute, and if so, where should it be created?
When you say "Singleton", do you actually mean creating a Static attribute?
Is there another way to communicate between 2 controllers that are not directly connected one to the other (I can transfer data from the "bottom" ViewController to the RootViewController passing it via the UIViewController in the middle, but it seems weird and ineffective)?
Any direction / tutorial will be appreciated!

Definitely use NSUserDefaults. It's great, and Apple recommends it.
To set a setting:
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"Setting 1"];
You can also store other things, such as text, numbers, etc. Much more than a simple boolean.
To check the setting:
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"Setting 1"]) {
//ok, do the thing here
}

I'd use http://inappsettingskit.com/ rather than roll my own. I've used it in almost every application I work on and it handles app settings perfectly for just this sort of scenario.

Related

Is it fine to access NSUserDefaults/UserDefaults frequently?

I have a login view controller in which the user enters their preferences like whether or not he wants to activate certain UI features.
I store these as variables whose getters and setters directly access UserDefaults, here is an example of one of these:
class Preferences {
static var likesSpaghetti : Bool {
set (likesSpaghetti) {
UserDefaults.standard.set(likesSpaghetti, forKey: "likesSpaghetti")
}
get {
return UserDefaults.standard.bool(forKey: "likesSpaghetti")
}
}
}
So that whenever I want to set any of these I simply write something like this:
Preferences.likesSpaghetti = false
Now, my question is: Can I set these variables every time the user flicks the on/off switch or should I keep the preference represented as a local variable and then only set:
Preferences.likesSpaghetti = spaghettiSwitch.isOn
when the user segue's away from the loginViewController? Is every access of UserDefault instant and quick? or is it laggy and should be used mercifully?
Edit after closing this question: So I learned to not prematurely optimize, and that it is probably ok within the scope of a few dozen elements. So I should be fine. I'm going to just update every time the user modifies anything so that my code is a lot easier to read and maintain.
Thanks everyone!
Your code is just fine. Don't worry about such optimizations until you actually encounter an issue. Trust that UserDefaults is implemented smartly (because it is). There is nothing "laggy" about setting something as simple as a Bool in UserDefaults.
You also wish to review another one of my answers which is related to this question: When and why should you use NSUserDefaults's synchronize() method?
Actually userDefaults (it's originally a plist file) is used for this purpose which is storing app settings and that light-wight content creating a variable may consum memory if you have to configure many of them , besides not reflecting the new setting change directly to defaults made by user , may cause un-expectable old settings to happen at the time of change such as a localized alert or part of code (such as push notification callback) that check the same setting where the user thinks it's already reflected
Adding to both #rmaddy #Sh_Khan, if you think about the security aspect of it, NSUserDafault is exactly for the details which is app specific such as settings, preferences or some configurations which are not security sensitive while things like passwords, usernames and sensitive data are not recommended to store in UserDefaults. You should use services like keychain which is encrypted for such data.

Sending a parameter (or get the key) with inAppSettingsKit when initialising custom controller

I'm trying to initialise a custom viewcontroller and send a parameter with it but can't figure it out.
To sketch an idea:
I want to have different settings per sport (walking, running, ...) and save those in core data on the custom viewcontroller.
To know to which sport the settings belong to I need to know what sport the user tapped to get to that screen. I was thinking about doing this by sending a parameter, using a custom init for each sport or by getting the key. All of which I can't figure out.
I've looked into this issue and tried to do it like this: (item 4)
But that doesn't work, according to the IASK docs I think I also need to specify a file, but what file?
The selector must have two arguments: an NSString argument for the file name in the Settings bundle and the IASKSpecifier.
I've tried setting a File field and tried different values but without success.
I'm seriously lost here, if it's possible to get the key of the item that initialised the controller ("running" for item 4 for example) that would also be good.
I've tried so many different things I'm losing my mind, I'm obviously doing something very wrong but I have no idea what it is.
You don't need to specify an IASKSpecifier key in your Settings.plist. The way this works is you implement
SettingsPerSportViewController.m:
- (id) myinit:(NSString*)file specifier:(IASKSpecifier*)specifier {
if ((self = [super init])) {
// setup your VC
if ([specifier.key isEqualToString:#"tennis"]) {
// custom stuff for tennis
}
}
return self;
}
However, this means you build the settings view controllers for your sports on your own and don't use IASK for that.
If all sports share the same settings and you want to use IASK to generate the settings view controller, you could implement a custom settings storage and use the same Sport.plist for all sports. Your custom settings storage could then load from and save directly to Core Data. A sport property would then route the load and save operations to the right Core Data context or table.
At the root level you could have a simple static view controller that sets up and pushes an IASKAppSettingsViewController for each sport with the settingsStore property set to your custom storage subclass and the sport property set according to the selected sport.

Saving Preference for iOS 8 custom keyboard

I can't find any tutorial or guide to show me how to save preferences with my Custom iOS 8 keyboard.
I have different skins and the user can cycle to all the skins. However if the user dismisses the keyboard then loads it back up, it will load the initial skin.
I know other keyboards have found a way to load Skins and remember which one was selected.
I also have 2 different layouts for my keyboard
QWERTY and DVORAK
I would like to also save that preference so user can change their selection when in my app.
Keyboard is done in Swift/Obj-C (not sure if that matters)
1. Create an app group for your host app and your custom keyboard
Select your host app's target, go to Capabilities, scroll to App Groups and add one by clicking on "+" sign. Type the name of your group.
Do the same with your keyboard's target but now simply add a group by ticking the recently added app group.
2. Now you can use NSUserDefaults to store & share data with your host app
Store data:
_userDefaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.app-group-name"];
[_userDefaults setObject:#"theme-dark" forKey:#"KeyboardTheme"]; // save data
Restore data:
_userDefaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.app-group-name"];
_theme = [_userDefaults objectForKey:#"KeyboardTheme"];
if ([theme isEqualToString:#"theme-dark"])
{
...
}
- Use constants & NS_ENUM instead of comparing strings.
- Full access has to be activated.
Apple Resources: head for “Sharing Data with Your Containing App“
I know I need to use NSUserDefaults but not sure how to implement it.

How can I implement a "first time user registration" workflow in my Cocoa Touch app?

I'm implementing a "new user registration" in my app
The first time the user logs in, I'd like to have them complete a 4-step registration process to create their username, set their profile photo, etc.. Each step would be a different screen.
All this needs to be done just once. I was wondering -
What is the best setup for these 4 screens? Should they be a UINavigationController so that they are implemented as a "stack"? i.e. the user can go back to the previous screens?
How do I preserve the state of the registration? I'd like to know what step the user was on in case they leave halfway, or in general know whether I should display the registration (i.e. if this is their first time). Is there a way I can store the number of steps completed, so that if numStepsCompleted == 0 then I know they haven't started and if numStepsCompleted < 4 I know they have started but not finished?
In general, if there are any code examples online that implement something like this, it would be great to look at.
Thanks!
I would present a UINavigationController modally from you root view controller. One the user has finished you can do 2 things:
Save in NSUserDefaults the fact that the user has already complete the registration process. If the user delete the app, this flag will be removed with it.
Save personal information such username and password in the keychain, they will persist event after the remove of the application, that can be useful for silent login process and they are ciphered.
For the first point to can do something like that
+ (BOOL)isFirstTime{
static BOOL flag = NO;
static BOOL result;
if(!flag){
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"hasBeenLaunchedOnce"])
{
result = NO;
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"hasBeenLaunchedOnce"];
[[NSUserDefaults standardUserDefaults] synchronize];
result = YES;
}
flag = YES;
}
return result;
}
If this is the very first launch, show registration/login process, if not go along by taking username and password from keychain.
Save a value in NSUserDefaults to know which is the last completed step done by the user. As tou said, make a UINavigation to setup the four registration steps. When the app opens read the user defaults to know if the registration is finished.
There are a couple of different ways to implements this:
1. Use a UINavigationController
2. Use a UIPageViewController (which I like better)
3. Use a UITableView to contain all the information in one screen
To keep track of the registration info you should implement a RegistraionInfo class (or struct in Swift). Since there should only be one instance of it, you can create a singleton and access it from anywhere - RegistrationInfo.sharedInstance()
You can also create an instance in the first ViewController and pass it to the next one on prepareForSegue:.
The last part is to identify if the user already completed the Sign Up process. If you're using a Username/Password model then you should keep that info in the keychain and fetch it on launch.
Otherwise you can keep a value in NSUserDefaults.

Is it ok to read a value stored in NSUserDefaults in multiple views

Is it ok to read a value stored in NSUserDefaults from multiple views or you only read it once and then you use another method to pass the data to different part of your app?
In other words I want to know if I’m doing it right, what I’m currently doing in an app I’m working on is basically saving a couple of NSIntegers and NSStrings (only two or three of each) in NSUserDefaults and then I’m reading those values in different parts of my app (different views) but I was wondering if this is a common practice or should I be doing something different like, read the value somewhere in the app and then try to use a different method to pass that data to other views. I want to learn best programming practices, that’s all.
What is the most common practice when using NSUserDefaults values in multiple parts of your app?
FYI,
I’m familiar with multiple ways to pass data between view controllers such as, delegation, prepareForSegue etc.
Thanks a lot.
I would also recommend, to read it multiple times and do not introduce another layer to hold the data.
The most important aspect is imo the actuality of the data, which might be changed inbetween different invocations.
It is ok to read and even write values to NSUserDefault in multiple places, but it is a better practice to have a global mechanism (like a singleton pattern) to read and write to UserDefaults. this way you'll be guaranteed to have fully synchronized values. all you need to do is to create a new class and add a few Class methods to read and write values from NSUserDefaults.
It is OK, when you call [NSUserDefaults standardUserDefaults] it will return the same object whether you spread the calls everywhere in the app or you encapsulate the access in a class.
I prefer the later as it allows you to have more readable code (and other benefits):
BOOL hasX = [TLPSettings hasPreferenceX];
if (hasX) {
[TLPSettings setY:YES];
}
vs
BOOL hasX = [[NSUserDefaults standardUserDefaults] boolForKey:#"hasX"];
if (hasX) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"hasY"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
You can change the preferences keys easily (without defining consts for them), change the validation or logic of a preference without messing with it everywhere, debug its usage or rogue values easily, store all or part of them in a different place, etc.
TLP is your three letter prefix.

Resources