I need a way to initialize a lot of objects only the 1st time my app is installed. So I thought I use this:
-(id) init {
self = [super init];
if(self) {
NSUserDefaults *myUD = [NSUserDefaults standardUserDefaults];
[myUD setObject:#"5" forKey:#"Extra"]; // 5 giving 4 extra diaries
[myUD setObject:#"0" forKey:#"CatchingUp"]; // it's a live diary
[myUD setObject:#"888" forKey:#"SendingSuccess"];
[myUD synchronize];
}
return self;
}
I obviously have many more than theses to initialize but it's just to give you the idea of what I did.
I deleted the app from my phone in order to test, but the init wasn't performed. I put it before ViewDidLoad.
What am I doing wrong please or is there a better way to do it ?
Thank you very much in advance.
If you just want to set up some values at the first time your app runs,
Put this code in app delegate and use this method in didFinishLaunchingWithOptions method,
or you can put this code in your view controller and use this method in viewDidLoad method:)
- (void)setDefaultInfoValue {
if (![[NSUserDefaults standardUserDefaults] boolForKey:#"everLaunched"]) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"everLaunched"];
//put you code here
NSUserDefaults *myUD = [NSUserDefaults standardUserDefaults];
[myUD setObject:#"5" forKey:#"Extra"]; // 5 giving 4 extra diaries
[myUD setObject:#"0" forKey:#"CatchingUp"]; // it's a live diary
[myUD setObject:#"888" forKey:#"SendingSuccess"];
[myUD synchronize];
}
}
At the moment you code will run whenever you create an instance of the class that contains the code - this is likely not what you want (and isn't what you describe).
Instead of setObject:forKey:, you should use registerDefaults: which installs the specified values only if values don't already exist and doesn't save the values to disk. This could be done in the app delegate, or separately in each class that uses different keys from user defaults.
The immediate problem you observe is probably that you aren't creating an instance of the class which contains your code.
Related
In my watch extension code I am trying to prevent the display of a second interface controller in certain scenarios. My solution requires looking through the list of active view controllers.
Does anyone know how to get a list of all active interface controllers? You can do this in iOS using self.navigationController.viewControllers but I could find no navigation controller in WatchKit.
I have read through the Apple Watch Programming Guide a number of times but they don't address this. Since you can push & pop interface controllers, there must be a list kept by the OS but I suspect there is no programming access to that list.
You could try to work around it by making your own list, and save it in NSUserDefaults for example:
You should clear it on the init of your initial ViewController:
- (instancetype)init {
self = [super init];
// get user defaults
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// empty the array at application launch
[defaults setObject:[[NSMutableArray alloc] init] forKey:#"activeVCs"];
[defaults synchronize];
return self;
}
And then in every other ViewController, you add and remove to this list on activate/deactivate of the controllers:
- (void)willActivate {
// get list of VCs
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *listOfVCs = [defaults objectForKey:#"activeVCs"];
[listOfVCs addObject:#"VC1"];
// save array and sync
[defaults setObject:listOfVCs forKey:#"activeVCs"];
[defaults synchronize];
[super willActivate];
}
- (void)didDeactivate {
// get list of VCs
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *listOfVCs = [defaults objectForKey:#"activeVCs"];
// remove VC if exists
if ([listOfVCs containsObject:#"VC1"])
[listOfVCs removeObject:#"VC1"];
// save array and sync
[defaults setObject:listOfVCs forKey:#"activeVCs"];
[defaults synchronize];
[super didDeactivate];
}
Because you can pass any valid context and access it in awakeWithContext:, I've used the technique of bundling a reference to the presenting view controller along with additional context in my WatchKit app. Not only does this allow you to build-up a list of presented interface controllers, but it also allows you to do things like assign delegates, inform controllers of interesting events, etc. I've published my JBInterfaceController project on GitHub: https://github.com/mikeswanson/JBInterfaceController
I need to share an NSMutabaleArray between multiple ViewControllers, so I created a singleton. But when I re-launch my app, the array seems to clear itself which causes problems within my app. This is my singleton:
.h:
#import <Foundation/Foundation.h>
#interface playlistArray : NSObject {
NSMutableArray *playlistSongsArray;
}
#property (nonatomic, retain) NSMutableArray *playlistSongsArray;
+ (id)sharedArray;
#end
.m
#import "playlistArray.h"
#implementation playlistArray
#synthesize playlistSongsArray;
#pragma mark Singleton Methods
+ (id)sharedArray {
static playlistArray *sharedArray = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedArray = [[self alloc] init];
});
return sharedArray;
}
- (id)init {
if (self = [super init]) {
self.playlistSongsArray = [[NSMutableArray alloc] init];
}
return self;
}
- (void)dealloc {
// Should never be called, but just here for clarity really.
}
#end
I thought about storing it in NSUserDefaults - is this the correct route? If so, how would I do this?
Any help would be much appreciated, thanks!
I am not sure creating a separate singleton class for just passing an array to different view controllers is a great idea from code structure point of view. Much more cleaner approach would be to pass playlistSongsArray directly whenever you create a new instance of a view controller that may need it.
However, if you still want to use singleton in your implementation for some reason, I'd change playlistArray class to something like PlaylistManager (notice that the common convention is to capitalise first letters of each word in class names) — by doing this, you don't constraint yourself with array-only implementation and you can use it to share other playlist information between your view controllers.
As for persistency between app launches, it really depends on what kind of data you store in your array. For example, you can use NSUserDefaults if your array stores relatively small number of NSStrings (or other <NSCoding>-compliant classes). Other most common options are NSKeyed​Archiver and Core Data. You can learn more about data persistency on iOS from Apple documentation or great online tutorials like this one on NSHipster.
I thought about storing it in NSUserDefaults - is this the correct route?
NSUserDefaults is certainly an option you have.
You can add the following two methods into your class and call it when appropriate (i.e., savePlayList when you modify the array;loadPlaylist` in the singleton init method):
- (void)savePlaylist {
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:self.playlistSongsArray forKey:#"playlistSongs"];
[defaults synchronize];
}
- (void)loadPlaylist {
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
self.playlistSongsArray = [[defaults arrayForKey:#"playlistSongs"] mutableCopy];
}
Have a look at this post for a discussion about different approaches you have.
You get singleton array blank when you relaunch the app because all memory allocated to the array is removed. These singleton classes are allocated once during the lifecycle of the app.
So the best answer to fetch the data every time you open the app is to save the data into the disk. To do so there are few ways
Database using Sqlite
plist
NSUSerDefaults
You can go for userdefaults or plist if you want to save the app.
Below is the example of saving the array into userdefault
Saving the array into userdefaults:
// Get the standardUserDefaults object, store your UITableView data array against a key, synchronize the defaults
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:array forKey:#"singletonArray"];
[userDefaults synchronize];
Retrieving the array:
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSArray *singletonArray = [userDefaults objectForKey:#"singletonArray"];
Hope it will help you. Happy Coding :)
I'm creating a simple double value, saving it as NSUserDefault and trying to recover it...but it doesn't.
- (IBAction)try:(id)sender {
double value = 42.00;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setDouble:value forKey:#"kDoubleKey"];
// NSLog(#"loading %f",myDouble);
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSUserDefaults *fetchDefaults = [NSUserDefaults standardUserDefaults];
double intValue = [fetchDefaults doubleForKey:#"kDoubleKey"];
NSLog(#"douvle retrieve %f",intValue);
}
Do not forget to synchronise whenever you save something to the defaults:
put this at the end of your try method
[[NSUserDefaults standardUserDefaults]synchronize];
Here is what apple says about this: Because this method is automatically invoked at periodic intervals, use this method only if you cannot wait for the automatic synchronization (for example, if your application is about to exit) or if you want to update the user defaults to what is on disk even though you have not made any changes.
I have created a Prefs Controller class, which dose what it says controlls my prefs values. I have two specific values in my prefs both strings one that's called installSelected and the other called finishSelected, I setting them as a string that's either T or F...
When the app first starts I create the new prefs and the values are set to F automatically as those are their default values in the plist bundle. Then later in the app I overwrite installSelected to T. When I restart the application it returns the correct value as T. Then I write over it again using F. Again I restart but when I read the values this time it still shows T when it should be F.
I have debugged this and I am just not sure why it's not saving the value.
This is what my prefController method looks like that is used to write the new values:
- (void) writeBool:(NSString *)name MyBool:(NSString *)myboolString {
prefs = [NSUserDefaults standardUserDefaults];
if ([name isEqualToString:#"InstallsSelected"]) {
[prefs setObject:myboolString forKey:name];
}
else if ([name isEqualToString:#"FinishSelected"]) {
[prefs setObject:myboolString forKey:name];
}
[prefs synchronize];
}
I call the above method like this
[prefsController writeBool:#"InstallsSelected" MyBool:#"F"];
It just makes no sense that it's not working as I am able to change it from F to T but not back if needed and none of the code is different. What might be causing this problem?
Why are you using strings instead of bool values?
You have to set your boolean by using:
// Notice setBool
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"pref"];
and call synchronize after it:
[[NSUserDefaults standardUserDefaults] synchronize];
To retrieve the value, you call:
// Notice boolForKey
if(![[NSUserDefaults standardUserDefaults] boolForKey:#"pref"]) {
// False
} else {
// True
}
To register default prefs, use:
NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], #"pref1",
[NSNumber numberWithBool:YES], #"pref2",
nil];
NSUserDefaults * prefs = [NSUserDefaults standardUserDefaults];
[prefs registerDefaults:appDefaults];
Are you on the simulator or a real device? I have had serious problems getting the sync right in the Simulator. It turns out that if you just do a rebuild from xCode, the user preferences are not saved. They are saved if you bring the app to the background in the Simulator (press cmd-shift-H).
Also it helps to NSLog as much as possible to see what is written in the prefs.
I need to register default preference values, which are then used by the interface immediately upon launching to set checkbox states, etc.
I register the defaults in the app delegate:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSLog(#"Did finish launching");
NSDictionary *defaultPrefs = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:#"isEnabled"];
[[NSUserDefaults standardUserDefaults] registerDefaults:defaultPrefs];
}
then, in an object that's created via the MainMenu.xib file, I want to use the default values:
- (void)awakeFromNib
{
NSLog(#"Awake from nib");
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[self.checkbox setState:([defaults boolForKey:#"isEnabled"] ? NSOnState : NSOffState)];
}
But these are called in the wrong order!
> Awake from nib
> Did finish launching
// Preference == `nil` or `0`, not the default value I set.
Thus the default prefs are not set when I want to use them.
Where should I registerDefaults: so that the default prefs can be used to set up the .xib file interface objects?
Am I using the wrong delegate methods? applicationDidFinishLaunching / awakeFromNib ? What are the alternatives?
I normally register defaults in the +initialize class method of the controller object in which they'll be used. +initialize is one of the earliest methods that is called in the lifetime of an object, so it assures that they are registered as early as possible.
+ (void)initialize {
NSDictionary *defaultPrefs = [NSDictionary dictionaryWithObject:
[NSNumber numberWithBool:YES] forKey:#"isEnabled"];
[[NSUserDefaults standardUserDefaults] registerDefaults:defaultPrefs];
}
Register the defaults in the init method of your app delegate class. This will ensure they are registered first.