My first app update just went live last night and I've gotten a complaint that the update caused the user-created data (some of it) to disappear. I have been able to reproduce the problem, but can't tell why.
In the Documents directory, I have saved one key file which tells me the "title" of all the user's files and their filenames (full path). Then all the user's files are also in the Documents directory.
When the update occurs, the key file is still there (at least, I think it is because the data shows up in the first screen of the app - apps do fully quit and relaunch after updating, right?), but when the user tries to navigate into the actual files, there is no data and any new data the user types in is never saved.
It's acting exactly as it did in debugging when I was accidentally using an invalid filename (with improper characters in it) - it never saved. But with the update, these files had been saved properly in the old version but are somehow failing in the new version.
This is my first app and my first update and I'm quite at a loss here. I've pulled the app from sale for the moment (didn't know you couldn't simply revert to an old version! yikes!) and would deeply appreciate any ideas as to where/how to look for the problem and how to write a GOOD update that won't lose the data. (So far, all I've found is "save data in the Documents directory", which is what I was already doing.)
I do have the original app saved in its own project and I can go back and work from that again. I copied that whole directory when I started working on the update and I do wonder if that could somehow be the problem? I did change the name of the directory that holds all the XCode files and used the Project>Rename function. Could that have this effect somehow?
The app (both original and update) is running on 4.2 and above, if that matters.
RESOLUTION:
I believe I have figured out this problem. Like I said near the beginning of my question, I was saving the FULL PATH of the user files in my key files. Apparently, the full path is NOT guaranteed to be the same after an update (I'm sure this is documented somewhere, but I hadn't run across it).
So, the user files HAD been transported to the update's new Documents directory, but I was looking for them at the old absolute path, i.e. not in my sandbox.
Fixed by putting a loop in app's didFinishLaunching to pull out the offending file of filepaths (THAT, I had always looked for locally and it was still working) and chop them down to just fileNAMES. The Documents path has to be found and added programmatically whenever file operations are performed.
For what it's worth, it was actually good for me that there is no way to revert to a previous binary in the store because re-installing the original version would not have fixed the user's problem, but I would have believed it had, at least initially. I do wish there was a way to have disallowed UPDATES, but still allowed new purchases (since the problem did not affect new purchases). Probably not a common enough situation to warrant it, though.
Go to Settings > General > [Device] Storage. You might see a list of recommendations for optimizing your device's storage, followed by a list of installed apps and the amount of storage each one uses. Tap an app's name for more information about its storage. Cached data and temporary data might not be counted as usage.
Just to shed some more light on this for internet passer-bys. Yes, the OP answered his own question at the very end. You should never store absolute URLs for files in your documents directory and doing so may cause data loss. This is because when you update an app, by changing its version # in the .plist file, iOS creates a new directory for that app with a different hexadecimal name. Now your absolute URL is referencing the incorrect location and won't return the proper file.
You can see this on your own computer before you even deploy to a device by navigating to
~/Library/Application Support/iPhone Simulator/*ios_version*/Applications/
There you'll see folders with names like:
6AA4B05C-8A38-4469-B7BE-5EA7E9712510 CB43C5F3-D720-49C3-87D4-EBE93FFD428B
Inside those folders will be the standard file system layout for iOS apps. i.e.
Documents Library tmp YourApp.app
By referencing the entire URL, you'll see something like,
~/Library/Application Support/iPhone Simulator/*ios_version*/Applications/6AA4B05C-8A38-4469-B7BE-5EA7E9712510/Documents/MyVeryImportantUserData.txt
However when you update the app the new URL that the app will try will be:
~/Library/Application Support/iPhone Simulator/*ios_version*/Applications/CB43C5F3-D720-49C3-87D4-EBE93FFD428B/Documents/MyVeryImportantUserData.txt
which is empty.
Instead you should always make a dynamic reference by only saving the filepath after you've gotten the documents directory filepath prefix.
/** Returns the path to the application's Documents directory. */ - (NSString *)applicationDocumentsDirectory { return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; }
To the original poster, you could have saved your users lost data by issuing a new update where you modified your code to take the absolute url and trim everything through the documents portion of the string. Then you could prefix the filepath with the proper documents directory url and the app would have found the data.
Both the question poster and the first answer were helpful in clarifying what exactly was going on.
So building on their observations, I came to realize that there was (what appears to be) a renaming of the folders within the path that lead up to the Documents folder. This lead me to a solution where I'm saving only the fileNames of the files and creating a utility class for grabbing the current fullPath string of the fileName that had been saved and is now stored in the current Documents folder after the app updates:
#import "StringUtils.h" @implementation StringUtils + (NSString *)getFullDocumentUrl:(NSString *)fileName { return [NSString stringWithFormat:@"%@/%@",[self applicationDocumentsDirectory],fileName]; } + (NSString *)applicationDocumentsDirectory { return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; } @end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With