Selecting location to save my app's preferences file

My app successfully saves and reads data from a “Printer Settings” that I create, but it lives in the default folder where the application lives. This is from my code for saving the prefs:

var f As FolderItem
f = New FolderItem("Printer Settings", FolderItem.PathModes.Native)

How do I modify this with the correct syntax to save instead to the current user’s Documents folder? This way, whoever logs into the computer will simply be saving or reading the app preferences from their own Documents folder.

Thanks!

Check out the documentation around SpecialFolder

https://documentation.xojo.com/api/files/specialfolder.html#api-files-specialfolder-usage

This is a quick way to access common folders across all targets.

SpecialFolder.Documents is one of the choices, but you also might want to consider SpecialFolder.Preferences or SpecialFolder.ApplicationData

2 Likes

SpecialFolder.ApplicationData.Child(“My App Name”) would be a good choice. This folder, that you create, could also hold other custom settings. Since this is in the ~/Library folder, it would indeed allow settings unique to each user account on the machine.

3 Likes

This.

1 Like

Thanks all. I’ve been through the documentation above but can’t figure out how to modify my code to support using SpecialFolder.ApplicationData.Child(“My App Name”) .

Here is my code for saving print settings:

Var f As FolderItem
Var bs As BinaryStream

// get a folderitem
f = New FolderItem("Printer Settings", FolderItem.PathModes.Native)

// create a binary file with the type of text (defined in the file types dialog)
bs = BinaryStream.Create(f, True)

// check to see if it was created
If bs <> Nil Then
  //write the contents of the editField
  bs.Write(App.s.ConvertEncoding(Encodings.UTF8))
  // close the binaryStream
  bs.Close
End If

Thanks! Still learning here…

Replace “My App Name” with whatever your app is going to be called.

// get a folderitem
f = SpecialFolder.ApplicationData.child("My App Name").child("Printer Settings")

This time, developing is quite simple, just replace the above line with:
f = SpecialFolder.ApplicationData.Child("My App Name").child("Printer Settings")
Also, no PathModes.Native is required here.

The result looks like:
~/Users/YourName/Library/Application Support/My App Name/Printer Settings

This looks easy enough, thank you! But when executed it breaks on the next line for bs = BinaryStream…

And is f Nil at that point? If not, is f.Exists True?

Does the Folder “My App” exist yet?
You have to create that folder before writing to a file inside it.

1 Like

On macOS that folder should be YOUR reverse bundle identifier, the one that matches the app.

SpecialFolder.ApplicationData.Child("com.mycompany.myapp")

1 Like

On app start, you should check if this folder exists, and if not, then .create

Yes. @Scott_Kahn needs to make his app go step by step through the setting up process. Is the folder there? If not, create it. If it is, is it readable and writeable? If not, abort and tell the user. If it is, does it contain the app’s prefs file? If so, is it really the app’s prefs file? (mine is an SQLite database, so I check it has the expected tables?) If the app’s Prefs file is not there, then create a default one. At this stage, abort the app if a step that is expected to work, doesn’t. But tell the user and preferably write something into a log file.

And so on.

2 Likes

There are many hundreds of folders and files in my ~Library/ApplicationSupport folder, and not a single one, including those of Apple’s own apps, Adobe’s, and Xojo, conforms to that convention.

In ~Library/Preferences you’ll find a lot of reverse bundle identifiers, but I never use the Preferences folder for my apps’ data.

Weird, I have 18 com.apple.xxx folders from Apple, but yes, almost all does not use com.company. Only com.company from apple, brother and 2 other companies, the rest use the product name (Firefox, Xojo, LibreOffice, Slack, and so on).

I use since many years a slightly modified and API2 updated variant of GitHub - CharlieXojo/classPreferences: A cross platform preferences class for Xojo using SQLite
Maybe this can help you here and with other projects? :slight_smile:

Ah, you’re right, I do have some in Application Support, I stand corrected. But the vast majority do not conform.

@AlbertoD @Julia_Truchsess I can no longer find the reference material and reasons we had been recommending this in the past, so I would agree that it doesn’t look like it’s a requirement.

However, consider how much more useful a bundle identifier is than just a name after an application is gone. It provides the user, should your domain actually exist, a place to start looking into who made that folder.

The bundle identifier system has other benefits too. A matching bundle identifier helps app cleaners and uninstallers know that files belong to your app when a user is attempting to clean it up. It’s also much more reasonably unique when you’re hoping to write app data in that folder.

Considering the benefits for both us as developers and the end user, no it’s not a requirement, but it’s a pretty good place to start.

2 Likes

You can also add aMenuItem in your application that delete all its app_data folder (not user data, data owned by the application).

This could be the issue. No, the folder does not exist, because the code I was using was writing the file into the app’s default location.