How to update an application which relies on existing data?

Hello, I have the following problem.

I have some testers which uses my application to create, modify of delete some data. These data are stored on a specific .plist file (consider a part of my app as a NSMutableDictionary editor).

This plist file is, now, into the application bundle. But I want to send my testers new builds of the application – without smashing the previous plist datafile. How to do it?

a) I could rely on an external file (where to store it? In the Preferences folder?) but the problem is, one of my testers has more than one computer – if she moves or copies the application, the datafile will not come with it…

b) I could use an installer which preserves this existing file of being replaced by the (empty) new one, but which one? PackageMaker? It allows pre-Lion installation, and my app works with ARC.

Both solutions have good and bad points, which one is the best one? Or maybe there is an other solution? My testers are not computer geeks, just honest users (that’s why I chose them): I want the solution be as simple as possible.

Do you already encountered such a situation?


simplest solution is an AppleScript installer application with the option to preserve the file.

The designated place for non-preference variable files is in a folder in ~/Library/Application Support with the same name as the application.

Most of my software that needs some testing have built-in server-client software so applications can share their work (like in coda) in a local network. When some files are needed I mostly develop a small built-in installer. When the application launches it check the application support folder if there are already the needed items in there. If not I copy them from my resources folder in my Application bundle and save them in the application support folder of the user domain.

I could have sworn it! But how? Post a folder in which I have the fresh application bundle and a Applescript executable just near it, whose title would be “Double-click to install the application”? Or making the app package invisible and giving the script the bundle (or a custom) icon? I don’t want the user to drag-and-drop the new bundle to his application folder, thinking “Oh, yes, I know where this goes” (I don’t rely on textEdit file screaming “Read me first absolutely!!!”)

Use a script application bundle with your application in the resources folder.
Launching the installer app could display a simple dialog to install (and probably completely uninstall) the application.

I’m distributing my TimeMachineScheduler pref pane this way. You could even write a script to build the installer app and put it in a .dmg image

A bundle inside a bundle. Smart! Ok, back to ApplescriptObC then.

P.S. Nice work on TimeMachineScheduler :slight_smile:

. exactly like a .pkg installer :wink:

Well, well, well. It’s gonna be more tedious that I thought.

If I use this:

NSWorkspace *ws =[NSWorkspace sharedWorkspace];
NSString *fullPath = [ws fullPathForApplication:@"Evals7"];

I get my application. well, one of my copies. Not bad, if the user has moved the app out of the /Applications folder. I don’t know where the workspace goes sneaking. I got my app once into the Xcode Debug folder, once into the /Applications folder, once into Xcode Release folder.

I should enforce the users to put the app into the /Applications folder. But to be sure, I tried this:

NSString *appFullPath = [NSString stringWithString:@"~/Applications"];
appFullPath = [appFullPath stringByExpandingTildeInPath];
NSFileManager *fm =[NSFileManager defaultManager];
NSLog(@"%@",[fm fileExistsAtPath:[NSString stringWithFormat:@"%@/",appFullPath]]);

And got (null)-- it should have logged “YES” or “NO”.


consider that ~/Applications represents /Users//Applications

Normally a boolean value with an object placeholder

NSLog(@"%@", YES);

causes the app to crash, use this instead

NSLog(@"%d", YES);


You mean, I can use ~/Applications everywhere and it will be equal to /Users//Applications?

The FM shows obstinately 0, even if there IS an Evals7 application in the Applications Folder.

You know what? I have not even managed to find my app, and I will still dig in the application package. It seemed so simple (and maybe it is):

IF (existing application) THEN
IF existing data file THEN
preserve the data file
Replace/copy application
Reinstall the data file

One of my testers has THREE unconnected computers. She may install an update and make two copies – i can’t ask her to browse the ~Library folder and make copies of the external plist files.

Aren’t you perhaps over-complicating things?

You have two initial choices: in the app bundle, or outside. In the app bundle is frowned upon, and likely to fail if the user doesn’t have the right privileges.

If you want to support the ability to move data between computers, you need the data in an accessible place because it’s going to have to be moved too. That rules out the library, and leaves somewhere like the home folder or desktop.

But if all you want is to store it locally, with a fall-back copy within the app – well, that’s exactly what user defaults does.

But maybe I’m missing something…


You mean the values key of the Shared User Defaults Controller? Doesn’t’ it save automatically the preferences? And what if the user does not want to save the data at the end of his work?

Ok, I resign, I didn’t think about this privileges problem. I’ll put the data into the user’s Document folder.

a) The data file is a document, so this place is the best one.
b) No need of an Installer anymore;
c) This is a place the user has an easy access, and if he/she goofed, there is always Time Machine;
d) There is easy for him/her to copy (or duplicate) the file.

And about complication, is this the best way to access the “Classes.plist” file into the Document folder:

NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* docsDirectory = [paths objectAtIndex:0];   
dictionaryPath = [NSString stringWithFormat:@"%@/Classes.plist",docsDirectory];
theDictionary = [NSMutableDictionary dictionaryWithContentsOfFile: dictionaryPath];

Thank you, I was fighting against this idea too long.

You read the preference into a separate variable,

id myData = [[NSUserDefaults sharedUserDefaults] valueforKey:@“userData”];

The user works on the data, and when they want to save you do something like:

[[NSUserDefaults sharedUserDefaults] setValue:myData forKey:@“userData”];

Better to use:

dictionaryPath = [docsDirectory stringByAppendingPathComponent:@“Classes.plist”];