UserDefaults - not saving consistently when using Xcode Simulator - ios

I am learning iOS development and I have stumbled across an issue where UserDefaults doesn't seem to be consistently saving correctly.
When I open the app, it checks to see if a UserDefaults property exists, if so, it does something. Now, if I save the UserDefaults property and 'synchronize', then press stop on the simulator and then press 'play' again to re-open it, sometimes the UserDefaults value is nil.
I have yet to test this on a physical device, but has anyone had the same issue - if so, is it expected?

Maybe UserDefaults work fine but when you save some data to UserDefaults and rebuild or run use Command + R if already running simulator
then sometimes nothing changed (saved data disappear)
so everytime when this situation(have to save some data to UserDefaults , CoreData or Realm) I stop and exit simulator first and rebuild or run Xcode
it works fine to me

Related

UserDefaults in IOS 10 is sometimes showing old value

I am testing something on my app where I save the current used theme as a string: "Light" vs "Dark".
I just realized that at some point when I keep switching from light to dark (reading and writing the string to UserDefaults) I sometimes get on old value:
Light -> Dark -> Light -> Dark -> Light -> Light
I have added logs to where I read and write the value and its doing it correctly. This is how I test:
1. Start app
2. Change theme (save string to UserDefaults)
3. Stop app
4. Start app (read string from UserDefaults)
I saw some other posts which says there is a problem with UserDefaults in IOS 10 and the solution is to restart device or mac.
I get this, but doesn't this mean the issue will appear on the user device as well ?
Has anyone encounter this kind of issue ? Any solution ?
I am using Xcode 8.2.1 with IOS 10, Swift 3. And I am testing on my real device, not simulator.
Are you sure that you call [[NSUserDefaults standardUserDefaults] synchronize]; after saving Data?

NSUserDefaults objectForKey sometimes nil

I've just noticed that a key that exists in my **NSUserDefaults**
is returning nil quite often. It seems about half the time it's
correct and the other half it's not. I say half the time, but I dont
mean that its flip-flopping, its just 40-50% I see it not work.
When I write the initial value, I call synchronize immediately.
I use this key as the applications
revision I set when a new user signs up.
The following code returns nil:
#define kDBrevision #"revision"
NSString *rev = [[NSUserDefaults standardUserDefaults] objectForKey:kDBrevision];
When I launch the app and just monitor the value (without
writing any NSUserDefaults), the value sometimes is valid
with no modifications to the NSUserDefaults at all.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSString *r = [[NSUserDefaults standardUserDefaults] objectForKey:kDBrevision];
NSLog(#"revision %#", r);
_exit(1);
I have no idea why this is happening. Im running iOS 10 on my
device connected to Xcode 8.2.1. Anyone have any ideas?
Thanks
EDIT:
I started talking to apple about fixing this and found out that if you have
file protection complete turned on, it can be the cause of this issue
showing up from time to time, however apple told me that my particular
case (which is the only one I was sure of at this time) is a bug.
The case is when you use Xcode to launch the app on the device it should
not fail like this and it occasionally does. No idea when or if it will
be fixed. Instead I moved my critical strings from the defaults to
the keychain instead.
This appears to be an Xcode 8 and/or iOS 10 bug. I ran into it myself and conclusively narrowed the case to UserDefaults intermittently returning nil when in fact there was data for the key. Specifically, I set the data once, then removed the set logic and executed only the get logic, repeatedly, and sometimes got values, sometimes got nil.
I changed my Run scheme to use Release configuration and ran the app on device without the debugger attached and the issue disappeared, that is, the get logic produced the correct value every time I executed it (~30 times).
Here is some more discussion:
iOS 10 with XCode 8 GM caused NSUserDefaults to intermittently not work
iOS 10, NSUserDefaults Does Not Work
https://forums.developer.apple.com/thread/48700
https://forums.developer.apple.com/message/143155#143155
try below code in didFinishLaunchingWithOptions
Swift 3.0
UserDefaults.standard.synchronize()
Obective - C
[[NSUserDefaults standardUserDefaults] synchronize];

iOS 10 with XCode 8 GM caused NSUserDefaults to intermittently not work

NOTE: I have seen many other posts on Stack Overflow about NSUserDefaults being renamed to UserDefaults in Swift or not working on simulator until a restart. This is not a duplicate by anyway. Many of the questions SO is tagging against is from 4 years ago. My question is specific to iOS 10 from this year as this has always worked in older versions. I have mentioned in my question already that my question is not a duplicate of those questions as those were simulator bugs in swift and my issue is on device objective C bug. Please read the questions before marking as duplicate
My issue is different as I am able to reproduce this on objective C and on physical device itself.
I created a brand new project from scratch for this test. I placed this code in the viewDidLoad of a view controller:
if (![[NSUserDefaults standardUserDefaults] valueForKey:#"checkIfInitialized"]){
NSLog(#"setting checkIfInitialized as not exist");
[[NSUserDefaults standardUserDefaults] setValue:#"test" forKey:#"checkIfInitialized"];
[[NSUserDefaults standardUserDefaults] synchronize];
self.view.backgroundColor=[UIColor redColor];
self.mylabel.text=#"NSUserDefaults was NOT there, try running again";
} else {
NSLog(#"checkIfInitialized exists already");
self.view.backgroundColor=[UIColor blueColor];
self.mylabel.text=#"NSUserDefaults was already there this time, try running again";
}
Now if I run the app about 10 times, few times it finds the checkIfInitialized and sometimes it doesn't. No exact number on how many times it fails as it might work 3 times then fail next 2 times then work 4 times and fail once and so on.
Now something I have noticed (not 100% sure though) that the issue only seems to happen when I am testing connected via Xcode. If I run by launching the app by clicking the app icon on device without Xcode, then it seems to work fine but I can't be 100% sure.
I noticed this error occur sometimes:
[User Defaults] Failed to write value for key checkIfInitialized in CFPrefsPlistSource<0x1700f7200> (Domain: com.xxxx.appname, User: kCFPreferencesCurrentUser, ByHost: No, Container: (null)): Path not accessible, switching to read-only
I have this very simple project on my dropbox if you want to test it out.
I would suggest testing about 10-15 times to reproduce this issue.
https://www.dropbox.com/s/j7vbgl6e15s57ix/nsuserdefaultbug.zip?dl=0
This works completely fine on iOS 9 so definitely something to do with iOS 10.
EDIT
Bug logged: 28287988
Response from apple DTS team:
First off, you should first determine whether standardUserDefaults or
valueForKey is failing. My guess is that “standardUserDefaults” is
returning NULL and, if that’s the case, then that’s something you
should be guarding against generally. Notably, standardUserDefaults
will return NULL if the preference file is encrypted in the
environment the app is currently running in (for example, preferences
is set to “NSFileProtectionComplete” and the app is running in the
background). That shouldn’t be an issue for standard foreground-only
apps, but it’s something to be aware of anyway.
It’s very likely that Xcode is actually inducing the problem here.
Xcode vastly complicates the app launching environment in a way that’s
VERY different than a standard app launch. My guess is that this is
basically being triggered by Xcode’s timing inducing an an expected
situation during the app launch, but if you want a more formal test of
that try setting a single breakpoint in applicationDidFinishLaunching
and continuing in the debugger as soon as you hit it. My guess is
just adding that disrupts the timing enough to stop the problem from
happening. Sort of. It’s iOS 10 only in the sense that iOS 9 will
never print that log message, but that’s because the log message was
added in iOS 10. The code itself is similar enough to iOS 9.3 that I
suspect exactly the same behavior is (at least in theory) possible in
iOS 9.
Yes, this is definitely a reproducible bug.
It happens with the GM release of Xcode 8 and iOS 10.
It is not the linked question referring to Swift.
It is not the linked question referring to beta versions of the Simulator.
The bug happens on devices and on the Simulator. It is intermittent: saving will work six times and then fail. Unlike you, I did not get the "failed to write key" message.
The bug also occurs when operating directly on the device without Xcode. This is in fact how I discovered it.
You should report a bug to Apple, especially since you have a short program that will reproduce it. I will do the same.
One key difference: In my case the failure is in writing the default. The previously written value remains in NSUserDefaults. Sometimes one key is successfully written while another is unchanged.
A similarly very intelligent DTS response from my own support request. Basically, killing using Xcode is more murderous than anything that would naturally happen on the device (even the double-Home-click-and-upswipe method) and since everything is abruptly crashed when Xcode halts it, the lazy writing of NSUserDefaults can fail, or be only half completed.
And indeed, pure on-device testing of the app, without Xcode involved, shows that everything does get correctly written to NSUserDefaults when the app is terminated.
I have closed my own bug report.

Realm stops processing my code without crashes the app

For some reason when I call let realm = Realm() it stops processing (doesn't crash) my code. My app is still running, but the screen keep frozen.
When I try to open default.realm using realm browser app, it shows me:
Again, it doesn't crash my app. It is a kind of pause.
No braekepoints
I've already tried: cocoapods and xcode install
I've already tried run on simulator and device (iPad)
App already deleted
Project cleaned up
Derived data cleaned up

CKQueryOperation iOS 9 only work plugged in

I am using CloudKit in my iOS 9 app and I have a CKQueryOperation that work fine and performs as expected when plugged into the Mac. As soon as the query is run when it is not plugged in to the Mac, it does not finish. There is no error message, the activity indicator I added never stops and the results are never displayed.
If I start the operation when unplugged, then plug it in it completes too.
The code was copied and adapted from an app built in Xcode 6.4 and works as expected in that app.
Any ideas why this could be happening? Could it be anything to do with app thinning in iOS 9?
Thank you
ANSWER
Adding this fixed the issue:
queryOperation.qualityOfService = NSQualityOfService.UserInitiated
The default QoS changed in iOS 9. If the query operation is user initiated, then it needs to be marked as such.
See CKOperation.h and the QualityOfService property.

Resources