NSUserDefault with App Group is not working in iOS 8 Beta3 - ios

I have to save Boolean Value to NSUserDefault in my App with custom keyboard extension and share with App Group.
My Code is worked in iOS 8 Beta1.
self.defaults = [NSUserDefaults standardUserDefaults];
if([self.defaults boolForKey:#"BlackKey"])
{
NSLog(#"Black");
}
else
{
NSLog(#"White");
}
But Not in iOS 8 Beta3. When i retrieve Boolean value from NSUserDefault , it's return nothing and i can't load from custom keyboard extension.
I have also tried with initWithSuiteName in NSUserDefault. Am i only one for that problem or bugs of iOS 8 Beta3?

A few probable solutions are:
Your app group is not setup correctly, or you are not using the correct group identifier with initWithSuiteName:
You have not enabled network access for your keyboard. This document states the following when you have network access disabled for your keyboard (default behavior):
No shared container with containing app
It's a bug.

Try using
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"BlackKey"];
when you save and read it by using this code:
if([[NSUserDefaults standardUserDefaults] boolForKey:#"BlackKey"]) {
NSLog(#"Black");
}
else
{
NSLog(#"White");
}
or if you already done so, then it can be bug, and Xcode 6 beta versions also have other bugs so it's safer to try in Xcode 5 or older.

Related

Storing bool in shared NSUserDefaults always returns false

I've set up App Groups for my app so that I can access the same NSUserDefaults in the app and in my keyboard extension. I have a problem though. I can successfully write a boolean to the defaults but when I access it in the keyboard, it always returns false. I know for a fact the id it's using for the key and the suite name are the exact same. I verified App Groups is indeed enabled for both the app and the keyboard and the suite name matches the app group identifier exactly. What could cause it to always be false when accessing from within the keyboard?
In the app:
NSUserDefaults *SharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.com.myname.sharedPrefsName"];
[SharedDefaults setBool:YES forKey:#"com.myname.appname.dataName"];
[SharedDefaults synchronize];
I did confirm it did successfully get set to YES the next time I launch the app:
NSUserDefaults *SharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.com.myname.sharedPrefsName"];
BOOL dataIsYes = [SharedDefaults boolForKey:#"com.myname.appname.dataName"]; //YES
Yet in the keyboard it is always false:
let sharedprefs = NSUserDefaults(suiteName: "group.com.myname.sharedPrefsName")!
let data = sharedprefs.boolForKey("com.myname.appname.dataName") //false
I found the answer in the App Extension Programming guide.
By default, a keyboard has no network access and cannot share a container with its containing app. To enable these things, set the value of the RequestsOpenAccess Boolean key in the Info.plist file to YES.
To fix the issue I had to change the RequestsOpenAccess field to YES in the keyboard's Info.plist > NSExtension > NSExtensionAttributes > RequestOpenAccess. Then remove the keyboard in Settings, delete the app, run it again, and add the keyboard again. Then be sure to tap on the keyboard name and then flip the switch to Allow Full Access.
If users don't enable Full Access, the extension will still be able to access the shared container (iOS 8.3+ only) but it will not be able to write to it, for security and privacy reasons. In 8.2- you cannot read from it without open access granted.
Don't use alloc/init for NSUserDefaults. It's a singleton. You're supposed to use the class method
standardUserDefaults to get a pointer to the shared user defaults object:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

NSUserDefault can't save & load in Extension in iOS8

I need to save data to NSUserDefault in my iOS Custom Keyboard.
I have successfully created App Groups and entitlement in developer portal and XCode.
Here is how i save my value with UISwitch
- (IBAction)switchAction:(id)sender
{
[self.defaults setBool:self.myKey.on forKey:#"myValue"];
[self.defaults synchronize];
}
and here is how i load value in KeyboardView.
self.defaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.mycompany.customkeyboard"];
if([self.defaults boolForKey:#"myValue"])
{
self.backgroundColor = [UIColor redColor];
}
else
{
self.backgroundColor = [UIColor blackColor];
}
It's doesn't work and doesn't load value.
How can i save and load data?
Initialize your NSUserDefaults object like this in all applications in the app group and they will share the database:
[[NSUserDefaults alloc] initWithSuiteName:#"group identifier"];
Keep in mind everything from the [NSUserDefaults standardUserDefaults] database for each application will not carry over into this database.
The documentation indicates that [NSUserDefaults standardUserDefaults] should return the shared database like the above code does, however it does not. I filed this as a bug (rdar://17164758).
And don't forget to synchronize the database:
[yourDefaults synchronize];
Ok, So I had a look around becuase I had the exact problem. What I did that worked was to add the main app and the extension to a group, Go to main project->Target->Capabilities and create a group (if you don't have one, or make one anyway) like this:
Then, go to the Extension below the target (E), again to Capabilities and add the extension to the group (exactly the same app group as you did for the main target), like this:
Then, once you have done both, in your main app, whenever you want to add something, create a new instance of NSUserDefaults, but for the Suitename equal to the groupname you made earlier. Like this:
NSArray *testing = #[#"first",#"Second",#"Third"];
NSUserDefaults *userd = [[NSUserDefaults alloc]initWithSuiteName:#"The gouprname I made earlier"];//This is exactly the same as the groupname
[userd setObject:testing forKey:#"ExtensionArray"];//set the object you want to share
[userd synchronize]; //It's a good idea to sync, just to be on the safe side.
In your extension's ViewController, use the same group name but to read the user defaults:
NSUserDefaults *sharedD=[[NSUserDefaults alloc]initWithSuiteName:#"Exactly the same groupname that I gave both in the Capabilities and when initialising the userdefault"];
self.testing = [[NSArray alloc]initWithArray:[sharedD arrayForKey:#"ExtensionArray"]];
And Voila! the array is there! I read somewhere that you can even add notification functionality for when the object changes, using a Wormhole class, but I can't find the link to it. I'm sure if you google for Wormhole class, you'll come across it.
I hope I could help, and if you found any more info, please share it with me.
You could load the right data from the containing app, so maybe set a new value in extension app will help.
[self.defaults setObject:newValue forKey:#"thisKeyIsJustUsedForSyn"]
And then load the right data.

IPhone - One time setup screen [duplicate]

This question already has answers here:
Detect when an iOS app is launched for the first time? [closed]
(7 answers)
Closed 9 years ago.
I am developing an iphone app which ask for one time set up details like user name in the first two screens when the user launches the app for the first time. I dont want these 2 screens to come up when I am launching the app once the user has given the details.
I tried getting the info from internet but I was not able to how to find it out. May be I am missing the technical term or wordings involved in doing this. Could any one please help in accomplishing this scenario. Any sample code would be very helpful.
Thanks for your time
Just set a boolean variable to the user-defaults. The user-defaults is a way to save information to the phone, external from the application, that the app can call upon whenever you want.
Inside your app-delegate when it boots, check the user-defaults for a boolean.
To save a boolean to user-defaults:
NSValue *state = ... //Whatever state you want. NSValue allows for booleans.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:obj forKey:#"notFirstTimeRun"];
[defaults synchronize];
To load the boolean from user-defaults:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSValue *state = [defaults objectForKey:#"notFirstTimeRun"];
Do a check:
if ([state boolValue] == true){
//Has ran before, skip your UIViews or whatever
}
else{
//Has not ran before, do your setup or whatever.
}
What you are looking to do is basically store a flag that dictates whether the user has gone through the setup steps. And upon app launch, you check to see if that flag exists with the respective value to denote whether or not to show a setup screen. NSUserDefaults provides a really simple, easy way of persisting state across sessions.
How about setting a bool in AppDidFinishLaunching. You can check that bool everytime the app is launched and show or hide respective screen based on that.
This is how I would do it:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasBeenLaunched"]){
// This is not the first time, therefore open app as usual
} else {
// This is the first time, show special views
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasBeenLaunched"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
}

NSUserDefaults behaviour with app update

I have a method in my iOS app that updates the application when detects when my server has a greater version for my app (a new ipa version). If the user wants to download it, the app updates itself on the iPad.
The thing is that I want to update some entities atributes from the DB when the app opens the new version for the first time, but i'm not sure how to. I can't debug it cause when I download the latest ipa, for XCode the app crashed.
I was thinking about doing something like this in the AppDelegate.m:
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedOnce"])
{
//do the stuff i wanna do
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedOnce"];
[[NSUserDefaults standardUserDefaults] synchronize];
// This is the first launch ever
}
But I don't know if this [[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedOnce"] was set to YES before the update, cause the process should be:
1)Launch the app for the first time ever.
2)The app detects a newer version.
3)Download the same app -> At this point apple "replace" the older version to the newer one.
4)Open the newer version app.
5)Do the stuff i wanna do ONLY for the first time I launch the new version.
You could use an integer stored in NSUserDefaults with a version number hard-coded for each version of the app. If the integer is lower than the hard-coded version, prompt for updates:
NSInteger currentVersion = 3; // increment with each new version
if ([[NSUSerDefaults standardUserDefaults] integerForKey:#"HasLaunchedForVersion"] < currentVersion) {
[[NSUserDefaults standardUserDefaults] setInteger:currentVersion forKey:#"HasLaunchedForVersion"];
[[NSUserDefaults standardUserDefaults] synchronize];
// This is the first launch for this version
} else {
// App hasn't been updated since last launch
}
They will not be removed. And will have old values.
if "HasLaunchedOnce" is used in the old version. use a new one and call it "HasUpdateLaunchedOnce".
Check both values and decide what do you want to do.
if (HasLaunchedOnce exists && HasUpdateLaunchedOnce not exists)
// proceed

Delete keychain items when an app is uninstalled

I am using idandersen's scifihifi-iphone code for keychain and save password using
[SFHFKeychainUtils storeUsername:#"User" andPassword:#"123"
forServiceName:#"TestService" updateExisting:YES error:&error];
When I delete the application from the device, the password remains in the keychain.
I want to remove the password from the keychain when the user deletes the application from the device. How can I do this?
You can take advantage of the fact that NSUserDefaults are cleared by uninstallation of an app. For example:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//Clear keychain on first run in case of reinstallation
if (![[NSUserDefaults standardUserDefaults] objectForKey:#"FirstRun"]) {
// Delete values from keychain here
[[NSUserDefaults standardUserDefaults] setValue:#"1strun" forKey:#"FirstRun"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
//...Other stuff that usually happens in didFinishLaunching
}
This checks for and sets a "FirstRun" key/value in NSUserDefaults on the first run of your app if it's not already set. There's a comment where you should put code to delete values from the keychain. Synchronize can be called to make sure the "FirstRun" key/value is immediately persisted in case the user kills the app manually before the system persists it.
For users looking for a Swift 3.0 version of #amro's answer:
let userDefaults = UserDefaults.standard
if !userDefaults.bool(forKey: "hasRunBefore") {
// Remove Keychain items here
// Update the flag indicator
userDefaults.set(true, forKey: "hasRunBefore")
}
*note that synchronize() function is deprecated
There is no trigger to perform code when the app is deleted from the device. Access to the keychain is dependant on the provisioning profile that is used to sign the application. Therefore no other applications would be able to access this information in the keychain.
It does not help with you aim to remove the password in the keychain when the user deletes application from the device but it should give you some comfort that the password is not accessible (only from a re-install of the original application).
For those looking for a Swift version of #amro's answer:
let userDefaults = NSUserDefaults.standardUserDefaults()
if userDefaults.boolForKey("hasRunBefore") == false {
// remove keychain items here
// update the flag indicator
userDefaults.setBool(true, forKey: "hasRunBefore")
userDefaults.synchronize() // forces the app to update the NSUserDefaults
return
}
C# Xamarin version
const string FIRST_RUN = "hasRunBefore";
var userDefaults = NSUserDefaults.StandardUserDefaults;
if (!userDefaults.BoolForKey(FIRST_RUN))
{
//TODO: remove keychain items
userDefaults.SetBool(true, FIRST_RUN);
userDefaults.Synchronize();
}
... and to clear records from the keychain (TODO comment above)
var securityRecords = new[] { SecKind.GenericPassword,
SecKind.Certificate,
SecKind.Identity,
SecKind.InternetPassword,
SecKind.Key
};
foreach (var recordKind in securityRecords)
{
SecRecord query = new SecRecord(recordKind);
SecKeyChain.Remove(query);
}
Files will be deleted from your app's document directory when the user uninstalls the app. Knowing this, all you have to do is check whether a file exists as the first thing that happens in application:didFinishLaunchingWithOptions:. Afterwards, unconditionally create the file (even if it's just a dummy file).
If the file did not exist at time of check, you know this is the first run since the latest install. If you need to know later in the app, save the boolean result to your app delegate member.
#amro's answer translated to Swift 4.0:
if UserDefaults.standard.object(forKey: "FirstInstall") == nil {
UserDefaults.standard.set(false, forKey: "FirstInstall")
UserDefaults.standard.synchronize()
}
This seems to be the default behavior on iOS 10.3 based on behavior people have been witnessing in beta #2. Haven't found any official documentation about this yet so please comment if you have.
Just add an app setting bundle and implement a toggle to reset the keychain on app restart or something based on the value selected through settings (available through userDefaults)

Resources