NSUserDefaults and App Groups in WatchOS 2 - swift

I am trying to use NSUserDefaults to access an object I save in my NSUserDefaults.
I have looked everywhere but I must be missing something. Hoping anyone can help me out.
Heres what I have done.
I use Xcode 7.1
My bundle identifier is "com.mycompany.myapp"
I added a watchkit app (new target)
Bundle identifier for watch target is "com.mycompany.myapp.watchkitapp"
Bundle identifier for extension target is "com.mycompany.myapp.watchkitapp.watchkitextension"
I enabled App groups in all 3 targets.
App Group ID for all 3 targets is: "group.com.mycompany.myapp"
All 3 steps are checkmarked in the App Groups section
I use NSUserDefaults.standardUserDefaults() in all cases but the one where I need the object to be accessible from the watch.
In this case I use:
if let userDefaults = NSUserDefaults.init(suiteName: "group.com.mycompany.myapp")
{
userDefaults.setObject(myDate, forKey: UserDefaults.DateKey.rawValue)
userDefaults.synchronize()
}
I can reload this data using this code:
let mydefaults = NSUserDefaults(suiteName: "group.com.mycompany.myapp")
mydefaults!.synchronize()
let myDict = mydefaults!.dictionaryRepresentation()
for entry in myDict
{
print("ENTRY: \(entry)")
}
Works just fine, but executing the same code in awakeWithContext in my watch extensions InterfaceController just prints a few of the default keys. Not the one I set myself.
I basically just need to access a date stored in the iphone app.
Any idea what I am doing wrong?

You can no longer use app groups sharing with watchOS 2. At least I haven't found any solution yet. It is my understanding that you now have to use WatchConnectivity.
https://forums.developer.apple.com/thread/3927

Related

Xcode 8: UI Testing, Neither element not any descendant has keyboard focus

I've recently started to use UI testing within Xcode. I was able to record the test and play it back and it worked perfectly fine. I recently changed the accessibility labels for textFields to make the test look cleaner by giving it a better name.
However, now when I record the UI test and play it back again to test it, I get an error "Neither element not any descendant has keyboard focus.". I've attempted to uncheck hardware keyboard as mentioned in other posts, but that doesn't work for me.
//test login with username and empty password
let app = XCUIApplication()
let elementsQuery = app.scrollViews.otherElements
let emailtextfieldElement = elementsQuery.otherElements["emailTextField"]
let passwordtextfieldElement = elementsQuery.otherElements["passwordTextField"]
emailtextfieldElement.tap()
passwordtextfieldElement.tap()
emailtextfieldElement.tap()
app.typeText("wayne#test.com")
passwordtextfieldElement.tap()
app.buttons["Login - signInButton"].tap()
Anybody know the fix for this?
Use typeText on emailtextfieldElement instead of app.
let emailtextfieldElement = elementsQuery.otherElements["emailTextField"]
emailtextfieldElement.tap()
emailtextfieldElement.typeText("wayne#test.com")

Return nil when finding path for App Group

I have enabled App Group and created a group called "group.com.classData"
And my code looks like this:
let plistPath3 = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.classData")
print(plistPath3) //returns "nil"
And it returns nil. Why does that happen?
I ran into this error while developing a share extension. It turned out that while I had added the App Group entitlement to the app’s target, I hadn’t added it to the extension’s target. I needed to select the extension from the project/target dropdown list:
Then I clicked on the Capabilities tab and enabled App Groups. The App Group I had created for the application was already in the list; I just needed to check its checkbox. When I ran the app again, containerURL(forSecurityApplicationGroupIdentifier:) returned a non-nil value.
it take my 4 hour to solve it..
in my post pic
clips and clipsKeyBoard must both add
Capability must choose "All" , first time i just chose Release... it's a sad story
enter image description here

Deleted app and reinstalled and user is still logged in? [duplicate]

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)

OSX Today View Access Core Data For Viewing Only

I have an app that stores lots of information in core data and displays it on a NSTableView, i want to have a smaller version of this for my today view extension.
I know how to share User Defaults using app groups, but could someone give me instructions on how to share the core data file with my extension. And also how to use it because as far as i know
let appDel = NSApplication.SharedApplication.delegate as! AppDelegate
and
context = appDel.managedObjectContext
wont work because they are files in the main app and i need those two things to query and view my data. Thanks in advance.

How can I reset the NSUserDefaults data in the iPhone simulator?

I've added NSUserDefaults data retrieval to my app, which is pretty nice. But for testing I would like to reset all the data I added to the defaults database, so that everything is in the state when the user launches the app the first time.
I tried to call:
[NSUserDefaults resetStandardUserDefaults];
but that doesn't do anything. The defaults are still saved and can be retrieved.
You want NSUserDefaults removePersistentDomainForName. This will remove all user defaults for the application:
NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier];
[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];
For more information on the NSUserDefaults class see the Apple docs.
Alternately, if all you are concerned with is data in the iOS Simulator, you can do that via iOS Simulator > Reset Content and Settings.
The easiest way is to remove the app from the simulator-- just like you'd remove it from a real phone, by tapping (clicking) and holding until the icons start vibrating. That removes all app data, and the next time you install from Xcode it's like the first time.
If you have other app data you need to keep, you have a couple of options.
One way would be to have some debug code that calls removeObjectForKey: on each of your defaults keys.
The other is to find the directory where the simulator copy is installed, and remove the file containing the preferences. Use this to find the app:
ls -ld ~/Library/Application\ Support/iPhone\ Simulator/User/Applications/*/*.app
The full path to your app will contain directory whose name is a UUID. In that directory, look in Library/Preferences for the preferences file. Remove that, and user preferences are gone.
You may find out what you have "written" to userdefaults for the app are all inside a file
delete this .plist file works:
user name/Library/Preferences/com.theAppYouAreHandling.plist
In Swift 2.0 the following 1 line will reset all the NSUserDefaults for an app:
NSUserDefaults.standardUserDefaults().removePersistentDomainForName(NSBundle.mainBundle().bundleIdentifier!)
Actually, this may not be suitable in every circumstance, but since I keep no information of value in the simulator, I just Reset Content and Settings from the iPhone menu, from within the simulator itself.
Here's the swift version:
let domainName = NSBundle.mainBundle().bundleIdentifier!
NSUserDefaults.standardUserDefaults().removePersistentDomainForName(domainName)
Til Xcode 6 and iOS 8 Simulator the location of the plist file changed.
I found the *.plist in the following directory:
[1] /Users/SOME-USERNAME/Library/Developer/CoreSimulator/Devices/SOME-DEVICE-ID/data/Library/Preferences/SP.UserDefaultsTest.plist
Deleting the located file from path [1] manually and the NSUserDefaults are gone.
In the simulator top menu:
Simulator -> Reset Content and Settings...
You can use removePersistentDomainForName method available with NSUserDefaults Class.
Syntax :
- (void)removePersistentDomainForName:(NSString *)domainName
Example :
NSString *strAppBundleId = [[NSBundle mainBundle] bundleIdentifier];
[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:strAppBundleId];
If you're doing this in a unittest, you may want to keep the state of your app in the current simulator instead of inadvertently wiping it every time you also run your unittests. One way to do this is to simply hang on to the old values for your app domain in setUp() and then restore them in tearDown():
class MyClass_Tests: XCTestCase {
static let domainName = Bundle.main.bundleIdentifier!
static var oldUserDefaults: [String : Any]?
override class func setUp() {
super.setUp()
// Hang onto this so we don't inadvertently wipe the app's state while running tests.
oldUserDefaults = UserDefaults.standard.persistentDomain(forName: domainName)
}
override class func tearDown() {
// Restore the old values.
UserDefaults.standard.setPersistentDomain(oldUserDefaults!, forName: domainName)
super.tearDown()
}
override func setUp() {
super.setUp()
// Wipe the state for each test.
UserDefaults.standard.removePersistentDomain(forName: MyClass_Tests.domainName)
}
override func tearDown() {
super.tearDown()
}
}
You can find UserDefaults in following path in Finder, delete the .plist
~/Users/<USER>/Library/Developer/CoreSimulator/Devices/<DEVICE_ID>/data/Containers/Data/Application/<APP_ID>/Library/Preferences/<APP_BUNDLE_ID>.plist
Path components to replace:
1. <USER> = MAC user name
2. <DEVICE_ID> = Device/Simulator Identifier, e.g., 999271B8-FAA6-41DE-9864-4111F422ED12
3. <APP_ID> = Application identifier, e.g., 69928AEF-BCD5-413A-B06F-BC4A07080D62
4. <APP_BUNDLE_ID> = Your apps bundle identifier, e.g., com.company.appname.plist