UIDocument Recover Unsaved Changes On App Crash/Force Quit - swift

From what I understand, the UIDocument class can track unsaved changes to a file and even locks the file so it cannot be checked out by more than one person. But what happens if the user force quits the app without saving or the app crashes? How would I go about recovering the unsaved changes to a UIDocument so that when the app re-launches it reopens the UIDocument with the most recent unsaved changes? Do I need to make duplicate copies of each file before it is changed and alter the temporary duplicate until the user saves the changes? Or does Apple provide a simpler implementation? I have also considered encoding and storing the Data file contents and the undoManager of each UIDocument instance periodically as a cache. Would that work?

The UIDocument and UIManagedDocument do automagic change tracking (calling a function where you can return true if the document has changed) and saving the changes to disk by respecting other system constraints (for example: if another process is trying to read the file).
The way Apple does saving is in a very safe way, if you don't override the base class methods. When the save operation is triggered, Apple saves to a temp file and if the save is successfully a quick rename and delete of the original file is done (IIRC the rename/delete is atomic, or near atomic). You can assume that the save operation doesn't leave a corrupt file in the file system for 99.99% of all cases.
Apple triggers save operations in the background at specific points (like: time based, app switched to background, before other process tries to accesses the file, ...), but I couldn't find any clear statement what happens when the application is force quit.
That said, logic and common sense tells me, that if you force quit an application, the current document state can't be saved. Even implementing a "quick save" manually for a force-quit may not be technically feasible. A periodic background save operation (like UIDocument already does) may be the best strategy.
About saving the state of the undo manager: This would be the same technical problem as with saving the UIDocument. There is no event or anything else that tells the application it's about to be force-quit.
You should read the Apple Documentation. It's very long but it explains the process in more detail. My advice to you would be, to implement the strategy that Apple imposes. These strategies are sound, and work for many, many applications in the Apple ecosystem and for their users. On top of that, you have a reduced implementation cost and automagic improvements (when Apple updates their implementation).

Related

Why is loadFromContents called after an autosave of my UIDocument?

I have an iOS app that implements a custom subclass of UIDocument that encapsulates a file wrapper on a package of data, archived objects and images. I initially converted the implementation to UIDocument for iCloud support but it has been too unreliable and so I am pulling out the iCloud portion and just using a UIDocument with local storage.
When the user modifies the data I send updateChangeCount: to the UIDocument instance and it periodically autosaves in the background. I only close the document when the application goes to the background. I've discovered that after an autosave which calls my contentsForType method of UIDocument a loadFromContents is also called. I don't understand this logically and it is also causing problems with my interface.
I thought loadFromContents would only be called when opening the document or if an iCloud document was modified on another device. So when my document autosaves in the background it also re-loads which causes an update of the UI which can sometimes discard new work in progress that hasn't been saved. I could ignore the re-loading in the UI but I'd still have some problems with my model data being different than the UI objects.
So what am I doing wrong to cause this behavior? I would like to have a save not initiate a loadFromContents or understand how to handle that behavior.
Thanks!
I discovered that this behavior was somehow due to some #synchronized calls I had wrapped contentsForType and the writeContents: methods with on initial implementation to prevent crashes when closing the document with unsaved changes. I was getting two near simultaneous calls to contentsForType that caused bad access errors.
I found a cleaner way to prevent these crashes by setting updateChangeCount:UIDocumentChangeCleared before closing the document.

How to prevent NSManagedObjectContext from corrupting the database when a thread gets killed while saving?

In this question, octy wrote:
BTW, if you save on a background
thread, you also need to consider what
happens when your app is terminated
while a save operation is in progress.
Background threads get killed right
away, whereas the watchdog waits 5
seconds for the main thread to finish
up.
Now I've spent all day implementing NSOperation and creating NSManagedObjectContext instances directly inside the NSOperation subclasses so every NSOperation owns it's own non-shared MOC. But now this is very bad news since a scenario like this, which likely happens all the time, would corrupt the Core Data database. I mean it can't start to write a half byte of something in the sqlite3 file and then just stop right away.
And then there's another problem: In my NSOperations I also do File I/O with NSFileManager.
So what can I do about this? Must I keep track of all running NSOperations and NSOperationQueues in my app und take care of them quickly in the App Delegate when the app gets terminated, so that I can tell the NSOperations to SFF (Save F*****g Fast) or cancel all operations, grab their MOC's and "hard-save" them immediately? What's best practice to solve this problem?
And why am I hearing about this the first time in my career? I mean none of the NSOperationQueue and Core Data mentioning books even talks about this but it seems it's a random app killer that forces the user to re-install (and possibly lose tons of data) if we don't take explicit care of this.
The main power of MOC is that represents an abstract storage that is independent from the format. It stores all it's objects in the memory and uses the mechanism of transactions to commit any changes. So when you're inserting/deleting/editing some objects that in MOC they are only changed in memory and not in persistent store (whether it is SQLite database, XML file or whatever). The changes are only committed when you call save: method.
As for NSOperation and file handling: if you want to stop some operation, you should call cancel for that. From docs:
This method does not force your
operation code to stop. Instead, it
updates the object’s internal flags to
reflect the change in state. If the
operation has already finished
executing, this method has no effect.
Canceling an operation that is
currently in an operation queue, but
not yet executing, makes it possible
to remove the operation from the queue
sooner than usual.
It means that if there is some IO operation running when you're canceling an operation operation would wait until it is finished.
Also despite the Core Data transaction mechanism you should implement your own one for any data that is not managed by it (if needed).

difference between save: and processPendingChanges: in CoreData

I have an NSManagedObjectContext, i make a couple of changes to the model and then... to "commit" the transactions, what's the difference between doing:
[context save:&error];
and
[context processPendingChanges];
It seems they both do the same thing.
In a nutshell, processPendingChanges changes the state of the current object graph. save will save the current object graph to disk.
Calling save will call processPendingChanges automatically.
If you think of a text file in a word processor, save is analogous to saving the document to disk.
processPendingChanges is analogous to telling the word processor to update it's internal state of the document after an edit, but without saving to disk. This usually triggers updates to the UI such as updating a displayed word or line count, doing any necessary formatting, etc...
In my experience, for the iPhone, you rarely need processPendingChanges. I believe it is mostly intended for Mac OS X and handling advanced or complicated undo management or updating UI bindings.
For the iPhone, this is usually done to trigger NSFetchedResultsControllers to update table views. Even then, this is somewhat rare. If you aren't sure just stick with save
For more info, go study the difference between NSManagedObjectContextDidSaveNotification and NSManagedObjectContextObjectsDidChangeNotification in the docs.

Saving painting app data to cope with interruptions on iPhone?

I'm making an iPhone bitmap painting app. I want to support about five 1024x768 layers (~15Mb of data). My problem is that I do not know what strategy to use for saving the user's document to cope with my app being interrupted.
My document file format at the moment is to save each layer as a .png file and then save a short text file that contains the layer metadata. My problem is that, if the app is interrupted by something like a phone call, I'm unlikely going to have enough time for my app to be able to save all the data to disk as saving all the .png files can take ~10 seconds. What options do I have?
I've considered adding an autosave feature that would be called every five minutes. In the worst case, the user will lose a few minutes of work if the app fails to save on interruption (which isn't ideal).
An idea I've considered is to keep track of which layers have changed since the last autosave and only update the layer files that need to be updated. This means that, when interrupted, my app might only need to save one layer in the typical case. However, the worse case is still having to save several layers.
I'm not sure what to do. On a practical note, I've noticed many popular iPhone painting apps with good reviews will lose the current document progress if interrupted with a phone call. I'm beginning to doubt there is a way to solve this particular problem and that I might just have to go with something less than ideal.
The IOS4 SDK provides support for long-running background tasks, which would be the perfect place to save your layers. From the documentation:
You can use task completion to ensure that important but potentially long-running operations do not end abruptly when the user leaves the application. For example, you might use this technique to save user data to disk or finish downloading an important file from a network server.
Any time before it is suspended, an application can call the beginBackgroundTaskWithExpirationHandler: method to ask the system for extra time to complete some long-running task in the background. If the request is granted, and if the application goes into the background while the task is in progress, the system lets the application run for an additional amount of time instead of suspending it. (The backgroundTimeRemaining property of the UIApplication object contains the amount of time the application has to run.)
I'm not sure if this is feasible (you don't state how the user interacts with the layers, or indeed if this interaction is transparent from their perspective), but as a suggestion why not simply save the "active" layer out (via a background thread) when the user switches layers, as you'd then only need to save a single layer when your app is backgrounded.

iPhone Core Data application will terminate save database required?

I have an application that allows you to edit some percentages, however it will only let you commit those changes if the percentages add up to 100. However because the Core Data template includes the save code in the application will terminate. If the user changed something and then exited the application, the item would be of course saved even though it did not add to a 100%.
Therefore I simply decided to comment out the save in the application will terminate. I know the other option would be to use another context for the edit and then merge the changes or setting my context values until the actual save point. However I do not see any harm in commenting out this line, since I save whatever I want in my application when the user clicks the save button, so my question is: is the save on the application will terminate mandatory? what possible consequences could this have?. It is important to note that the application continues to work just fine after commenting this lines (which is what I expected).
Thank you in advance.
-Oscar
You can save whenever you like.
However, you will never know when the app will terminate. Unlike applications on more conventional platforms e.g desktops, the iPhoneOS will terminate your app (from the apps perspective) at random. The only warning you will get will be the applicationWillTerminate message sent to the app delegate. If you don't handle saves there then it is highly likely that at some point, your users will lose data.
I think you should reconsider your design. It sounds like you're putting calculation into the managedobjects that could (1) be handled elsewhere in code or (2) be handled by transient properties. You shouldn't have a condition in which the managedobject can't be saved at the drop of hat. Doing so makes your datamodel utterly dependent on external code for its internal integrity. This causes problem with maintenance, portability and upgrading.
Its not mandatory to save on application will terminate. You can save when ever you feel appropriate for the context of the app.