Lazarus

Programming => Widgetset => Cocoa => Topic started by: jwdietrich on October 01, 2019, 12:51:28 am

Title: [Solved] Finding correct preferences folder under Cocoa
Post by: jwdietrich on October 01, 2019, 12:51:28 am
What is the best practice of finding the preferences folder with Cocoa widgetset?

The usual method to use GetAppConfigDir (and corresponding GetAppConfigFile) are incompatible with Apple's user interface guidelines. They deliver an invisible directory with name ".config" in the home folder, which is, BTW, an inelegant location (since the folder is invisible). The correct way would be to return the folder "Preferences" in the user's library folder.

There is a working solution for Carbon provided at https://wiki.freepascal.org/Multiplatform_Programming_Guide#Proper_macOS_file_locations (https://wiki.freepascal.org/Multiplatform_Programming_Guide#Proper_macOS_file_locations), but what is the correct way for Cocoa?
Title: Re: Finding correct preferences folder under Cocoa
Post by: dbannon on October 01, 2019, 09:10:31 am
jwdietrich, while I agree that ~/.config seems quite un-Mac like, the alternative seems to be in the application bundle. While thats tidy and nice and modular, if the user upgrades to a new version of your app, you better have a plan to migrate the preferences from the old, about to be deleted folder to the new one !

I decided to use the ~/.config approach because my app is cross platform, Windows, Linux and Mac. Mac is, sort of Unix and multiuser and the right place for config files is in the user's home directory. Lazarus keeps its config files there (although not in .config). A number of other apps I looked at do too.

Davo
Title: Re: Finding correct preferences folder under Cocoa
Post by: Thaddy on October 01, 2019, 09:16:16 am
Hidden directories for user preferences are a long standing feature of Unix like platforms (and windows!).
Can you be more specific why that would clash with the design guidelines? I can't find it...

And even on OSX you can force the file browser to make them visible if so required.
user preferences are usually stored in a custom format specific to an application, so should be hidden from view and only editable from the application itself.
I think that is conformant. It is about persistence, not about outside editability.
Title: Re: Finding correct preferences folder under Cocoa
Post by: jwdietrich on October 01, 2019, 09:36:19 am
jwdietrich, while I agree that ~/.config seems quite un-Mac like, the alternative seems to be in the application bundle. While thats tidy and nice and modular, if the user upgrades to a new version of your app, you better have a plan to migrate the preferences from the old, about to be deleted folder to the new one !

No, preferences aren't to be stored in the application bundle. The correct path is in one of the preferences folders (either within ~/Library/ for user-specific preferences or within /Library/ for global preferences). Of course it is possible to pass ~/Library/Preferences/ to the app, but by this way the directory is read-only on newer versions of macOS (and, since locations may change over time, it would be preferable to have a more abstract representation to prepare for potential redefinitions in future versions of macOS).
Title: Re: Finding correct preferences folder under Cocoa
Post by: Thaddy on October 01, 2019, 09:49:46 am
@jwdietrich

The hidden parts are per user overrides. Why is this not acceptable:
Code: Bash  [Select][+][-]
  1. $ cd ~/.lazarus
to store user preferences? Such directories are not read-only, merely hidden.
Title: Re: Finding correct preferences folder under Cocoa
Post by: jwdietrich on October 01, 2019, 11:13:09 am
@jwdietrich

The hidden parts are per user overrides. Why is this not acceptable:
Code: Bash  [Select][+][-]
  1. $ cd ~/.lazarus
to store user preferences? Such directories are not read-only, merely hidden.

Well, if there isn't another solution I will have to use this option. At least it works. I hope that it will pass App Store checking.
Title: Re: Finding correct preferences folder under Cocoa
Post by: Thaddy on October 01, 2019, 11:27:45 am
Yes it will, but you can double check with Apple developer support which is part of your license.
Make sure about asking just about the proper place for persistent user settings storage. (Since Lazarus itself is not the issue, and they are not really familiar, except one or two)
Response is actually pretty quick. And the answer will be close to the same. But check! (I have 2 tokens left, just in case you ran out of free support)
Title: Re: Finding correct preferences folder under Cocoa
Post by: VTwin on October 01, 2019, 09:32:33 pm
This is what I have been using:

Code: Pascal  [Select][+][-]
  1. function GetApplicationSupportDir(global: boolean = false): string;
  2. var
  3.   t: string;
  4. begin
  5.   {$IFDEF DARWIN}
  6.   t := ExcludeTrailingPathDelimiter(LazUTF8.SysToUTF8(GetUserDir));
  7.   result := t + '/Library/Application Support/' +  LazUTF8.SysToUTF8(ApplicationName);
  8.   {$ELSE}
  9.   result := GetAppConfigDirUTF8(global);
  10.   {$ENDIF}
  11. end;  

This has worked so far for Carbon and Cocoa, but I'd be happy to have a definitive solution.
Title: Re: Finding correct preferences folder under Cocoa
Post by: trev on October 03, 2019, 12:42:21 am
This is what I use:

https://wiki.lazarus.freepascal.org/Multiplatform_Programming_Guide#Proper_macOS_file_locations
Title: Re: Finding correct preferences folder under Cocoa
Post by: VTwin on October 15, 2019, 05:16:51 pm
This is what I use:

https://wiki.lazarus.freepascal.org/Multiplatform_Programming_Guide#Proper_macOS_file_locations

Thanks. I had been using code obtained elsewhere some time ago, and missed this. Hopefully this works on all macOS versions in common use.

I'd prefer to just use ".config", but that is a macOS  "violation".
Title: Re: Finding correct preferences folder under Cocoa
Post by: jwdietrich on October 15, 2019, 07:11:05 pm
This is what I use:

https://wiki.lazarus.freepascal.org/Multiplatform_Programming_Guide#Proper_macOS_file_locations

As already stated in the first post, this works with Carbon only. Cocoa needs a different approach.
Title: Re: Finding correct preferences folder under Cocoa
Post by: trev on October 16, 2019, 09:01:06 am
This is what I use:

https://wiki.lazarus.freepascal.org/Multiplatform_Programming_Guide#Proper_macOS_file_locations

As already stated in the first post, this works with Carbon only. Cocoa needs a different approach.

No! I am using it successfully in a Cocoa-only application which runs on Catalina. Honest!
Title: Re: Finding correct preferences folder under Cocoa
Post by: VTwin on October 16, 2019, 11:34:53 pm
According to this archive:

https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/MacOSXDirectories/MacOSXDirectories.html

it was ok to directly access ~/Library/Application Support/

I'm still curious to hear an official Apple statement as to any recent changes. I'm still on High Sierra, where it works.
Title: Re: Finding correct preferences folder under Cocoa
Post by: trev on October 17, 2019, 02:12:27 am
According to this archive:

https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/MacOSXDirectories/MacOSXDirectories.html

it was ok to directly access ~/Library/Application Support/

I'm still curious to hear an official Apple statement as to any recent changes. I'm still on High Sierra, where it works.

Specifically with respect to Preferences:   

Quote
This directory contains app-specific preference files. You should not create files in this directory yourself. Instead, use the NSUserDefaults class or CFPreferences API to get and set preference values for your app.
From: https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html#//apple_ref

which is covered on the Wiki at https://wiki.lazarus.freepascal.org/Mac_Preferences_Read_and_Write
Title: Re: Finding correct preferences folder under Cocoa
Post by: trev on October 17, 2019, 09:45:43 am
I just found this while implementing preferences in my application:

Quote
Preference File Locations and Debugging

Preferences files are stored in the system’s or user’s preferences directories. On OS X versions 10.0 to 10.4 these are in /Library/Preferences and in /Library/Preferences in the user’s home directory respectively. When debugging an application, it may sometimes be useful to inspect these files to determine that preferences have been saved correctly, however you should never hardcode these paths into an application. If you do need to access the directory programmatically you should use the NSSearchPathForDirectoriesInDomains API, although there should typically be no reason to do so.

Source: https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFPreferences/Concepts/BestPractices.html#//apple_ref/doc/uid/TP30001219-DontLinkElementID_4
Title: Re: Finding correct preferences folder under Cocoa
Post by: VTwin on October 18, 2019, 02:19:01 am
@trev

Excellent, thanks for those references.

My preference files are cross-platform json. I just need a legal place to save them.
Title: Re: Finding correct preferences folder under Cocoa
Post by: VTwin on November 13, 2019, 05:02:47 pm
The code for GetSupportDir does not compile, stopping at:

Code: Pascal  [Select][+][-]
  1. if Global then   // kLocalDomain
  2.   theError := FSFindFolder(kLocalDomain, FolderType, kDontCreateFolder, theRef)
  3. else             // kUserDomain
  4.   theError := FSFindFolder(kUserDomain , FolderType, kDontCreateFolder, theRef);
  5.  

with error: 'Got "FILES.FSRef" expected "MACOSALL.FSRef"'

Unless I change:
Code: Pascal  [Select][+][-]
  1. Uses
  2.   {$IFDEF DARWIN}
  3.   MacOSAll, Files
  4.   {$ENDIF}

to
Code: Pascal  [Select][+][-]
  1. Uses
  2.   {$IFDEF DARWIN}
  3.   Files, MacOSAll
  4.   {$ENDIF}

I assume this is ok, but I am not a macOS expert. It seems to work, maybe the wiki should be modified.
Title: Re: Finding correct preferences folder under Cocoa
Post by: VTwin on November 13, 2019, 05:35:38 pm
Not quite. My tests on Mac, Win, and Lin suggest you need something like this:

Code: Pascal  [Select][+][-]
  1. { GetSupportDir
  2.   Return path to user application support directory. }
  3. function GetSupportDir: string;
  4. begin
  5.   {$IFDEF DARWIN}
  6.   result := GetSupportDir(false, kApplicationSupportFolderType)
  7.     + ApplicationName + PathDelim;
  8.   {$ELSE}
  9.   result := GetAppConfigDirUTF8(false);
  10.   {$ENDIF}
  11. end;

to get the application support directory. Otherwise the Mac code gives you the 'Application Support' directory, while Win and Lin give the application support directory in that directory.
Title: Re: Finding correct preferences folder under Cocoa
Post by: trev on November 30, 2019, 01:52:37 am
The code for GetSupportDir does not compile, stopping at:

Code: Pascal  [Select][+][-]
  1. if Global then   // kLocalDomain
  2.   theError := FSFindFolder(kLocalDomain, FolderType, kDontCreateFolder, theRef)
  3. else             // kUserDomain
  4.   theError := FSFindFolder(kUserDomain , FolderType, kDontCreateFolder, theRef);
  5.  

with error: 'Got "FILES.FSRef" expected "MACOSALL.FSRef"'

Unless I change:
Code: Pascal  [Select][+][-]
  1. Uses
  2.   {$IFDEF DARWIN}
  3.   MacOSAll, Files
  4.   {$ENDIF}

to
Code: Pascal  [Select][+][-]
  1. Uses
  2.   {$IFDEF DARWIN}
  3.   Files, MacOSAll
  4.   {$ENDIF}

I assume this is ok, but I am not a macOS expert. It seems to work, maybe the wiki should be modified.

Weird. The former works for me with no errors (it was a cut and paste from my application).
Title: Re: Finding correct preferences folder under Cocoa
Post by: trev on November 30, 2019, 02:23:21 am
Not quite. My tests on Mac, Win, and Lin suggest you need something like this:

Code: Pascal  [Select][+][-]
  1. { GetSupportDir
  2.   Return path to user application support directory. }
  3. function GetSupportDir: string;
  4. begin
  5.   {$IFDEF DARWIN}
  6.   result := GetSupportDir(false, kApplicationSupportFolderType)
  7.     + ApplicationName + PathDelim;
  8.   {$ELSE}
  9.   result := GetAppConfigDirUTF8(false);
  10.   {$ENDIF}
  11. end;

to get the application support directory. Otherwise the Mac code gives you the 'Application Support' directory, while Win and Lin give the application support directory in that directory.

Nice catch. Indeed I was doing that in my application too.

I've now updated the Wiki code for GetSupportDir() to make the result for macOS the same as the rest so that you no longer need to add the ApplicationName. I've also fixed a compiler warning about an un-intialised variable. See: https://wiki.lazarus.freepascal.org/Multiplatform_Programming_Guide#Proper_macOS_file_locations

Thanks for posting!
Title: Re: Finding correct preferences folder under Cocoa
Post by: VTwin on November 30, 2019, 02:52:01 am
Excellent, great to see the update!

Cheers,
VTwin
Title: Re: Finding correct preferences folder under Cocoa
Post by: jwdietrich on December 19, 2019, 09:36:39 pm
This is what I use:

https://wiki.lazarus.freepascal.org/Multiplatform_Programming_Guide#Proper_macOS_file_locations

You are right, this works for both Carbon and Cocoa. It is necessary to include MacOSAll in the uses clause for LCLCocoa, too.
TinyPortal © 2005-2018