I read: iPhone: How to Pass Data Between Several Viewcontrollers in a Tabbar App and was wondering what is the difference between
[[UIApplication sharedApplicaton] delegate]
and
extern struct* global
?
Conceptually, I don't see how [[UIApplication sharedApplicaton] delegate] not being a global thing. In fact, that lessen the sense of guilty when using the dirty global struct * now.
I am starting a new project very soon. So, I use this break to ask the question: is there any best-practice code example to illustrate how to share data between two ViewControllers (but not globally)?
Let me put it in an example:
this is a game
there is a NSString *name to store the player's name
there is a NSInteger score to store the player's current score
the GameMainViewController will update and display the score
in the GameSettingViewController, there is a text field to edit name and a button to reset score
the GameMainViewController is responsible for set a default name (if nil), save both name and score when exit, load both (if exists) when start
so
where should I put "name" and "score"?
how can both ViewControllers access and change the values
Thank you!
You can store name and score in NSUserDefaults.
Retrieve an item:
NSString *name = [[NSUserDefaults standardUserDefaults]objectForKey:#"name"];
Setting an item:
[[NSUserDefaults standardUserDefaults]setObject:#"Horace" forKey:#"name"];
Also, if this is data that you want to preserve across launches of the app, you may want to archive it into a plist.
Related
I am creating a names generator app for iPhone and this is what I am trying to do.
Allow the user to save the current generated name by clicking a save button.
In order to do this, here is what is happening:
The current babyname is displayed in a UILabel.
The user presses 'save' and then the label.text value is appended to a NSMutableArray (I don't think this is working in my code correctly).
The NSMutableArray is then saved in NSUserDefaults.
The contents will then be loaded in another view to populate UITableView cells.
My specific question is, am I handling this saving/persistent storage process correctly? Here is the snippet in question:
- (IBAction)saveName:(id)sender {
// save current baby name to array
NSMutableArray *babyNameArray = [[NSMutableArray alloc] init];
[babyNameArray addObject:babyname.text];
// save list of babynames to nsuserdefaults
[[NSUserDefaults standardUserDefaults] setObject:babyNameArray forKey:#"My Key"];
// for testing log names listed in nsuserdefaults
NSLog(#"%#", [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]);
}
Here is a link to the pastebin of the whole file contents (with comments): http://pastebin.com/hQRM9Azh
Every time you add, you're starting again with an empty array ([[NSMutableArray alloc] init]) – instead of starting with the existing items.
Here's some code which adds to the existing entries instead:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *babyNameArray = [[defaults stringArrayForKey:#"My Key"] mutableCopy];
if (!babyNameArray) babyNameArray = [[NSMutableArray alloc] init];
[babyNameArray addObject:babyname.text];
[defaults setObject:babyNameArray forKey:#"My Key"];
[defaults synchronize];
A couple of things before we start addressing the big issue. First, you'll want to have those arrays of names stored somewhere in a file(s), which you'll read either when the application starts, or in ViewWillAppear/ViewWillLoad.
Second, the way you have things working right now, you will only save one name at a time. You alloc & init a new (empty) array every time the user clicks the "Save Name" button. You then add the current name to this (empty) array and set it as the object for key "My Key." Note that your "My Key" object will always have only one element - the most recently saved name.
And lastly, you never actually save your changes to NSUserDefaults. If I recall correctly, after you are done making changes to it, you need to call synchronize - otherwise your changes will be lost as soon as the application closes. Which kind of kills the whole purpose of using data persistance. :)
I have an NSArray called namesArray.
I need to store all the names existing in namesArray using coredata.
How do i achieve this ?
Does we need to create any database like 'names.sqlite' using sqlite manager?
No you don't need to create a names.sqlite using manager. You should go through some of the tutorial found on the net for example: Here or Here.
You basically need to save in the datamodel in valueForKey format.
//Coredata saving
self.theAppDel=[[UIApplication sharedApplication] delegate];
NSManagedObjectContext*context=[self.theAppDel managedObjectContext];
NSManagedObject*object=[NSEntityDescription insertNewObjectForEntityForName:#"Contacts" inManagedObjectContext:context];
NSError*error=nil;
[object setValue:[namesArray objectAtIndex:0] forKey:#"name"]; //for saving first name
1. Creating an instance of your appdelegate .
2. Access your Managed Object Context and Managed Object
3. Assuming you created a entity description with name contacts. With the count of number of items in your array, add each object for a column named name (which i'm assuming you would have created).
This is short example, but you should go through the tutorials and read apple's documentation.
EDIT: As Ondra mentioned my earlier solution would have added only the last element. Use the following for adding: Adding NSMutableArray in CoreData Thanks Ondra Peterka
I think that iNoobs answer would not work - the loop would overwrite the value several times (only last name would be saved). In every case reading the tutorials is good idea.
Maybe the answer you are looking for is here: Saving an NSMutableArray to Core Data
Also ... I know you explicitly said "save to core data", but just in case - you can use also different storage like NSUserDefaults:
[[NSUserDefaults standardUserDefaults] setObject:namesArray forKey:#"namesArray"];
and retrieve it later using:
myArray= [[NSMutableArray alloc] initWithArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"namesArray"]];
Of course NSUserDefaults are ment to be used only for small amount of data.
Good luck ;)
I'm trying to call up a specific setting that is saved in the settings bundle of my iphone app. I want to do an if statement based on what was saved. My code in the implementation file looks like this:
branchMatch = [[NSString alloc] initWithString:[defaults objectForKey:#"branch"]];
The object in the settings file is just the name of the specific branch. I keep getting the SIGABRT error but I'm not sure why.
First, there's no need to initialize a string with another string. Assuming that branchMatch is a NSString*, the following would suffice:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
branchMatch = (NSString*)[defaults objectForKey:#"branch"];
Second, check if objectForKey returns a nil. Your SIGABRT is, most likely, due to a nil there.
EDIT: those values aren't present in the collection until the user opens up Settings and explicitly changes them. It's up to you to provide the sensible value if the setting is not found (is nil). The ones in the settings bundle are not automatically applied; they are only for the Settings app to initialize its UI properly.
Are you checking that the objectForKey call isn't returning nil?
Do this instead :
NSUserDefault* defaults = [NSUserDefauls standardUserDefaults];
NSString* setting = [defaults objectForKey:#"branch"];
if (setting)
// Do what needed if branch is set
else
// Do what needed if branch has never been set
After much hair pulling, I realized that I need to load user defaults from the delegate class instead of the ViewController.
Thanks for all the input
I'm aware of NSUserDefaults for saving/restoring user preferences. What is the equivalent class for an application? For example, the application may have a "last run" field; or it may have a field for a unique identification of the device for use at the application level.
My intention is to keep the application's settings (not user's settings) out of the Settings Application, and not backup those settings in iTunes, Time Machine, {whatever}.
I'm getting a lot of noise for Java and C#, but not much for iOS/iPhone/iPad.
NSUserDefaults can be used for what you're asking.
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"shownPrompt"]) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"shownPrompt"];
// Show your prompt or whatever
}
That's a working code snippet. If the key is false, it sets it to true and shows the prompt. The next time this code runs, the key will already by true, so the prompt won't be shown.
NSUserDefaults is specific to the current app on the current device, and is similar to an NSMutableDictionary in that it's a key-value system, with the difference that instead of instantiating your own, there's a universal shared instance for your whole app, that doesn't get erased when the app exits.
NSUserDefaults is perfect for saving things like whether something has been shown, the date of last run, etc. Read the docs here: https://developer.apple.com/documentation/foundation/userdefaults
Don't be put off by the 'user preferences' part. You can use it to save anything you want (as long as it is or can be converted to an NSObject which implements <NSCoding>, which basically means NSString, NSDictionary, NSArray, NSNumber, UITextField, int, float, bool, etc.).
Just to clarify, stuff you put in NSUserDefaults will not, under any circumstances, automagically turn up in the Settings app. It will be kept completely private and hidden. For something to appear in Settings, you need to add a Settings bundle to your app, and manually add keys to it for each and every value that you want to be visible in the Settings app.
if you can store value by NSUserDefaults, then it is good to store application preferences too.
or add settings.plist on your project and read that (what you are not changing later)
and you can use like.,
+ (NSDictionary*)getBundlePlist:(NSString *)plistName
{
NSString *errorDesc = nil;
NSPropertyListFormat format;
NSString *plistPath = [[NSBundle mainBundle] pathForResource:plistName ofType:#"plist"];
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization
propertyListFromData:plistXML
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:&format errorDescription:&errorDesc];
return temp;
}
+ (id) getPropValue:(NSString *)PropertyName
{ // I am supposing you had add your app preferences on settings.plist.
return [[Property getBundlePlist:#"settings"] objectForKey:PropertyName];
//here Property is my class name, then you can use value by
//NSString *value = [Property getPropValue:#"setting1"];
}
It's hard to tell what you're asking. It sounds like NSUserDefaults is what your looking for, but you claim that you're already aware of it. So what's your problem?
What is the difference between:
[[NSUserDefaults standardUserDefaults] registerDefaults:
[NSDictionary dictionaryWithObjectAndKey:anObject, #"something"]];
And this:
[[NSUserDefaults standardUserDefaults] setObject:anObject forKey:#"something"];
The difference is that the first code-snippet you register defaults that will be used when the user has not made any changes to the "property".
So if you want to provide let's say a "property" with the key name 'Welcome message', you could instead of having the property returning nil insert a default message 'Welcome first-time user' that will be displayed when there have been no changes to the property.
This will simplify your logic because you don't need to write an if test to check if the "property" returns nil and then make another message if this is the case.
NSString *greeting = [[NSUserDefaults standardUserDefaults] stringForKey:#"Greeting"];
if(greeting == nil) {
NSLog(#"Welcome first-time user!");
}
The second code-snippet you posted is for setting the property to another value. You will have different set methods (setString, setObject, setBoolean) to set values depending on your program state in the Userdefaults.
EDIT-----Updates as requested in comment.
The first method is for registering values to defaults, as the name implies. The first time you access the property with some key name the value will be either nil for objects, false for booleans or 0 for numbers. Instead of doing a lot of tests and so on to so if the values is not set in the program, and then do something "default" action such as the example above, you can ship your application with some already predefined values for these keys.
A typical place to put the registerDefaults is in the initializer-method in the appDelegate.
Then somewhere in your program you may want to set the values of these fields then you use the setObject, setString, setBoolean...and for retrieving you use stringForKey, objectForKey...
Think of it as this
The registerDefaults is the constructor where you may supply sensible values for the object, otherwise you get some defaults which I already wrote. Then later if you want to change the object's attributes you do NOT use the "constructor" but the set/get methods.
Long story short,
[[NSUserDefaults standardUserDefaults] setObject:#"Entropy" forKey:#"kName"]
will save "Entropy" to a file named com.example.Demo.plist in Library/Preference folder (where com.example.Demo is your Bundle ID, see IOS Application Security Part 20 – Local Data Storage)
[[NSUserDefaults standardUserDefaults] setObject:#"Mac OS X" forKey:#"kOS"];
NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
#"Windows", #"kOS",
#"Google", #"kSearchEngine", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults];
NSLog(#"%#", [[NSUserDefaults standardUserDefaults] objectForKey:#"kOS"]);
NSLog(#"%#", [[NSUserDefaults standardUserDefaults] objectForKey:#"kSearchEngine"]);
NSLog(#"%#", [[NSUserDefaults standardUserDefaults] objectForKey:#"kBrowser"]);
will print "Mac OS X", "Google", (null)
In fact, registerDefaults
does not save to disk
only sets value for keys that haven't been set ("kOS" is set by setObject:forKey: and "kSearchEngine" is not set)
returns 0 for scalar values, nil for objects if that key is not set by both registerDefaults and setObject:forKey: ("kBrowser" in this case)
And the usage of registerDefaults
Quoted from Preferences and Settings Programming Guide
If these standard default values are not appropriate for your app, you
can register your own default values using the registerDefaults:
method. This method places your custom default values in the
NSRegistrationDomain domain, which causes them to be returned when a
preference is not explicitly set.
Quoted from How to Save Data with NSUserDefaults
Another tip is that you can initialize your NSUserDefaults with a
pre-defined NSDictionary object. So for example you could set a
default value to be “false” or “true” before the user ever had a
chance to interact with your program. In my case, sometimes I create
an array that represents all the levels in my game, and in each array
value I store a boolean to check if a player has finished the level.
To do this I create the data object and then register it with
NSUserDefaults. If a previous value exists for the object, then
nothing happens. Otherwise my blank object gets saved as the “default”
defaults
PS: Ole has a very good article explaining Handling Default Values With NSUserDefaults in detail
Another way of looking at it is this. If you delete the preferences file from ~/Library/Preferences, the defaults that are set by registerDefaults will be the ones that apply to the application until new preferences are set.
In Swift 2.1.1 Xcode 7.2
I added this snippet to application:didFinishLaunchingWithOptions to initialise the tintColorsIndex which is one of parameters user can change in the app.
let defaults = NSUserDefaults.standardUserDefaults()
defaults.registerDefaults([
"tintColorsIndex" : -1,
])
When the app is launched the very first time the tintColorsIndex will be assigned a value of -1 (an Int). If user has changed the color while using the app, their preference won't be overridden at subsequent launches.
User Defaults are grouped in domains...
registerDefaults is used to add defaults to the registration domain..
You can read about the domains in Preferences and Settings Programming Guide.