While developing with Firebase, I manually added a data record in the console but forgot one entry, which caused the app to crash. I've corrected the problem in the console but, because I was using Firebase's data persistence, the original data error persists, causing the crash again. If I switch the persistence off, everything is fine but the cached store isn't being updated. Has anyone had this problem and found a way to solve it?
I too have faced this issue, and have lost sleep over the thought of users being trapped in an endless crash-on-start-up loop.
As suggested, the opportunity for the issue to arise is created in the time window between the start up of an app and the subsequent arrival of cache updates from the Firebase server. If data is read from the cache during this time window, and then, if the data happens to be missing an expected value, and then, if the app uses the data in a way that assumes the data will not be nil, the app crashes. If the app crashes before the cache updates, the cache will never have the chance to update, and the user will be trapped in an endless loop (absent wiping the app's data from the device's memory).
Thus far, I've dealt with the issue by more strenuously guarding against the possibility of nil values in code that is called during start up. If nil is checked and inconveniently found, then, depending upon the situation, either (1) if possible and if it will not lead to further data corruption, the app substitutes an appropriate value in place of nil, or otherwise (2) the app goes into a wait mode for a couple of seconds, and then initiates a fresh read from the problem node, and then reattempts the start-up routine.
Perhaps the moral of the story is never assume that a value is non-nil or otherwise within an expected range. Either validate the value upon receipt or check the value at the time of intended use or both, and then handle errors accordingly.
The cache should update automatically. Since this happens in a background thread without, while it invokes your code on the main thread, I'd expect it to update the disk cache even if you application code crashes.
But if that doesn't happen, the fastest way to get back in a good state is to clear the app data for you application or to completely uninstall/reinstall.
Related
CKFetchDatabaseChangesOperation has recordZoneWithIDWasPurgedBlock and recordZoneWithIDWasDeletedBlock blocks. My assumption is that when the user deletes all iCloud data associated to an app the purge block is called, and when I manually delete a zone then the delete block is called.
However in my experience of a few weeks with CloudKit neither have ever been called, I have tried many methods to trigger either block but it never happens. Instead these scenarios seem to be handled through errors in the CKFetchRecordZoneChangesOperation I later make. I get a CKError.Code.changeTokenExpired if the zone has been deleted and a CKError.Code.userDeletedZone if the user deletes iCloud data themselves.
Handling these scenarios through errors is fine and manageable for me but leaves me wondering when are these blocks ever called? Should I be handling them the same way I handle the errors for deleted/purged zones?
A bug of my own doing caused the custom zone to always be created at app launch which would obviously never trigger delete blocks AND invalidate all change tokens.
I have the most frustrating type of advice for those having a similar issue: check your code! Make sure you're not creating the custom zone before you fetch database changes.
I have been debugging an app for a while and am ready to upload it to app store. However, I still get occasional crashes that are hard to duplicate or debug--for example when pushing buttons in weird sequences. If I write these sequences down, I can debug but inevitably it happens when I haven't written it down and these can be difficult to replicate.
I know I need to just call it a day and leave future bug fixes for next release. I have removed all the abort() statements I had in testing. However, occasionally rather than getting a crash that lets you just close the app and reopen, I get one that makes it impossible to open the app without reinstalling. For example, this just happened, with a
'NSGenericException', reason: '*** Collection <__NSCFSet: 0x174a41b90> was mutated while being enumerated.'
This resulted from switching VCs during a background sync to the cloud.
I can live with crashes where you just need to reopen but not with ones that render the app unusable. Are there any guidelines for types of crashes to help you focus on the really fatal ones?
Or is there anything you can do to keep crashes from bricking app?
You should just fix this problem. Since you have this crashlog with that error message, you know which method can raise the problem, so you've got a good head start on fixing it, even if the problem manifests itself inconsistently and/or in a variety of ways.
But the occasional crash may not seem like too much of an inconvenience to you, but it is the quickest way to 1-star reviews and unhappy customers. I suspect you will quickly regret distributing an app with known, easily reproduced crashes.
But, a few observations:
It sounds like your background update process is mutating your model objects used by the main thread.
If possible, I'd suggest being careful to simply do not change any of your model objects in the background thread, but rather populate a local variable and when you're ready to update the UI accordingly, dispatch both the model update and UI refresh to the main thread.
If you cannot do this for some reason, you have to synchronize all interaction of model updates with some mechanism such as locks, GCD serial queue, reader-writer model, etc. This is slightly more complicated approach, but can be done.
I would advise temporarily editing your target's "scheme" and turn on the thread sanitizer:
It may possibly help you identify and more easily reproduce these sorts of problems. And the more easily you can reproduce the problem, the more easily you will be able to fix the issue.
You say:
Or is there anything you can do to keep crashes from bricking app?
It sounds like the "save" operation is somehow leaving the results in persistent storage in an internally inconsistent manner. Any one of the following would help, though I'd suggest you do all three if possible):
At the risk of repeating myself, fix the crash (it's always better to eliminate the source of the problem than try to program around manifestations of the problem);
Depending upon how you're saving your results, you may be able to employ an atomic save operation, so that if it fails half way, it won't leave it in an inconsistent state; we can't advise how you should do that without seeing code snippet illustrating how you're saving the results, but it's often an option;
Make sure that, if the "load" process that reads the persistent storage can fail, that it does so gracefully, rather than crashing; see if you can get it in this state where the app is failing during start-up, and then carefully debug what's going on in the start-up process that is causing the app to fail with this particular set of data in persistent storage. In the "devices" section, there is an option to download the data associated with an app, so you can carefully diagnose what's going on.
So I've had a look at the apple documentation on the NSUserDefaults's synchronize() method. See below for reference:
https://developer.apple.com/reference/foundation/userdefaults/1414005-synchronize
The page currently reads:
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.
However, what I still don't understand is when should this method be called? For example, should it be called every time the user changes the app's settings? Or should I just trust that the background api is going to handle that? And does the leaving of the view immediately after a settings change in memory result in that change being lost?
Also, when might a failure to call synchronize() result in user settings not getting changed correctly?
Furthermore, what is the cost (performance, memory or otherwise) of calling this method? I know it involves reading and writing from/to the disk but does that really take that much effort on phones?
There seems to be so much confusion about user defaults. Think of it this way. It's essentially the same as you having a global dictionary available throughout your app. If you add/edit/remove a key/value to the global dictionary, that change is immediately visible anywhere in your code. Since this dictionary is in memory, all would be lost when your app terminates if it wasn't persisted to a file. NSUserDefaults automatically persists the dictionary to a file every once in a while.
The only reason there is a synchronize method is so your app can tell NSUserDefaults to persist the dictionary "now" instead of waiting for the automatic saving that will eventually happen.
And the only reason you ever need to do that is because your app might be terminated (or crash) before the next automatic save.
In my own apps, the only place I call synchronize is in the applicationDidEnterBackground delegate method. This is to ensure the latest unsaved changes are persisted in case the app is terminated while in the background.
I think much of the confusion comes from debugging an app during development. It's not uncommon during development that you kill the app with the "stop" button in the debugger. And many times this happens before the most recent NSUserDefaults changes have been persisted. So I've developed the habit of putting my app in the background by pressing the Home button before killing the app in the debugger whenever I want to make sure the latest updates are persisted.
Given the above summary, let's review your questions:
should it be called every time the user changes the app's settings?
No. As described above, any change is automatically available immediately.
Or should I just trust that the background api is going to handle that?
Yes, trust the automatic persistence with the exception of calling synchronize when your app enters the background.
And does the leaving of the view immediately after a settings change in memory result in that change being lost?
This has no effect. Once you add/edit/delete a key/value in NSUserDefaults, the change is made.
Also, when might a failure to call synchronize() result in user settings not getting changed correctly?
The only time a change can be lost is if your app is terminated before the latest changes have been persisted. Calling synchronize when your app enters the background solves most of these issues. The only remaining possible problem is if your app crashes. Any unsaved changes that have not yet been persisted will be lost. Fix your app so it doesn't crash.
Furthermore, what is the cost (performance, memory or otherwise) of calling this method? I know it involves reading and writing from/to the disk but does that really take that much effort on phones?
The automatic persistence is done in the background and it simply writes a dictionary to a plist file. It's very fast unless you are not following recommendations. It will be slower if you are misusing NSUserDefaults to store large amounts of data.
Apple's documentation for synchronize() has been updated and now reads:
Waits for any pending asynchronous updates to the defaults database and returns; this method is unnecessary and shouldn't be used.
UPDATE
As anticipated, it has been deprecated as mentioned in Apple Doc
synchronize()
Waits for any pending asynchronous updates to the defaults database and returns; this method is unnecessary and shouldn't be used.
Original Answer
-synchronize is deprecated and will be marked with the NS_DEPRECATED macro in a future release.
-synchronize blocks the calling thread until all in-progress set operations have completed. This is no longer necessary. Replacements for previous uses of -synchronize depend on what the intent of calling synchronize was. If you synchronized…
— …before reading in order to fetch updated values: remove the synchronize call
— …after writing in order to notify another program to read: the other program can use KVO to observe the default without needing to notify
— …before exiting in a non-app (command line tool, agent, or daemon) process: call CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication)
— …for any other reason: remove the synchronize call
As far i know, synchronize is used to sync the data immediately but iOS can handle that in smart way. So you dont need to call it everytime. If you call it everytime then it will turn to performance issue.
Check apple documentation:
Official Link
Since I've started developing my Blackberry app, the biggest problems I've encountered all had to do with SQLite Databases.
Right now I'm putting my app through a stress test, and when problems pop up I address them by printing out statuses to the console and taking care of things line by line. Right now (after mashing buttons on my app) I received a "Database is locked" error and I'm not sure what to do.
It seems that once the database is locked it's locked for good until it is unlocked........ my question is how can I unlock it?? First of all, how can I check to see if it's locked??
I'm sure our users won't be mashing buttons like I did, but you never know. I want to account for every possible scenario.
Thanks
EDIT: This is what happens in my application..... When I launch it starts a thread, this thread performs a cleanup on one of my tables based on how old certain pieces of data are (uses DELETE). The thread then continues to get a USER object from my DB (read only), it then uses this USER object as a parameter to call a web service. The data retrieved from the web service is INSERTED into my database. (It's a little more complex than that as a few read/write operations are performed at this time. After that, the thread fires a callback method to update my UI.
This all works fine. I can exit the app WHILE the thread is running and relaunch and a flag will prevent it from starting a new instance of the same thread (unless the other one is done of course).
Now my problem: My app's home screen is a list of buttons, when the user clicks one of these buttons another, more detailed list is loaded (this requires a READ ONLY call to the database). When I launch the app (firing the web service calling thread) and then click a button on the main screen right away, the table gets locked. (Not always, sometimes it takes 4 or 5 tries, sometimes more, sometimes less). But if I keep doing this it WILL eventually lock making it impossible to make any calls to my DB, hence no more UI (which depends on the DB).
The DB call that populates the UI on the second screen is READ ONLY, can't I have as many of these as I need?? What causes the DB to lock?? What's the difference between a DB lock and File System error (12)??
I seemed to have fixed the problem. I was under the impression that if a read/write connection was open then a read-only connection could be created safely.
This doesn't seem to be the case. If I have a read/write connection open then no other connections can open until that one is finished.
I basically created one read/write connection, set a flag to identify it as open, and during my read connection use the same Database object if the flag is open, or create a read only if it's closed.
So far so good.
Sqlite does not support concurrent modification. In practice on BlackBerry, this means you can only open the database from one part of the code at a time. To maintain this one-at-a-time access, you need to close the database when you are done with it, as #AnkitRox points out.
However you also need to guard against concurrent access. Even if your code properly closes the database, it is possible for two different threads to access the database. In that case, you will need one to wait. In Java-ME this is typically accomplished through the 'synchronized' keyword, and using the same lock object for all database access.
Check properly that, you are opening and closing database before and after execution of query respectively.
Because if Database is going to open without closing it properly, then it gives errors.
This question io3->ios4 upgrade said to support applicationWillResignActive. While implementing this call, I also implemented applicationDidEnterBackground and applicationWillEnterForeground. However, I found that my app would crash. After some debugging on the simulator I determined that I needed to reinitialize a key data structure in applicationWillEnterForeground. So my question is how would I have known that from reading the documentation? (In fact, I may be doing the wrong thing and just so happened to get it working again.) Is there an exact description of what to do when these methods are called?
Thanks.
The only things you should do when supporting multitasking, is saving the state of your app when it enters the background, and reload it when it becomes active. (If you generate a new template in Xcode, you'll see this.)
Saving state means writing any user preferences or data to disk. Reloading the state involves reading saved preferences and data, recreating any in memory data structures that might need it (like in your example you gave).
In most circumstances, there's little else you need to do. The only thing that would crash your app that is unique to multitasking would be trying to run code in the background for longer than the allotted amount of time, (which is 10 minutes.) Otherwise, it sounds like you have got other problems with your code.