Difference between revisions of "Locating macOS significant directories"

From Free Pascal wiki
Jump to navigationJump to search
m (Fix template issue)
(Added Users and User's directory retrieval examples)
Line 191: Line 191:
 
           + GetSignificantDir(NSTrashDirectory,NSUserDomainMask,0));
 
           + GetSignificantDir(NSTrashDirectory,NSUserDomainMask,0));
 
end;   
 
end;   
 +
</syntaxhighlight>
 +
 +
===Users directory===
 +
 +
Maybe not what you expect... this is the ''/Users'' directory :-)
 +
 +
<syntaxhighlight lang="pascal">
 +
procedure TForm1.MenuItem10Click(Sender: TObject);
 +
begin
 +
  ShowMessage('Users dir: ' +  GetSignificantDir(NSUserDirectory,NSLocalDomainMask,0));
 +
end;
 +
</syntaxhighlight>
 +
 +
To retrieve the current user's home directory, you need to use the ''NSHomeDirectory()'' function instead:
 +
 +
<syntaxhighlight lang="pascal">
 +
procedure TForm1.MenuItem11Click(Sender: TObject);
 +
begin
 +
  ShowMessage('User''s dir: ' + NSStrToAnsiStr(NSHomeDirectory));
 +
end;
 
</syntaxhighlight>
 
</syntaxhighlight>
  

Revision as of 09:09, 15 December 2019

macOSlogo.png

This article applies to macOS only.

See also: Multiplatform Programming Guide

English (en)

Overview

To locate various significant directories in macOS you can use the native macOS Foundation function

NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde);

which creates a list of path strings for the specified directories in the specified domains. The list is in the order in which you should search the directories.

Note: The directory returned by this method may not exist. This method simply gives you the appropriate location for the requested directory. Depending on the application’s needs, it may be up to the developer to create the appropriate directory and any in between.

Determining where to store files

Before we go any further, let's recap where the Apple Guidelines indicate your application should store its files:

  • Use the Application Support directory (this is backed up by Time Machine), appending your <bundle_ID>, for:
    • Resource and data files that your application creates and manages for the user. You might use this directory to store application state information, computed or downloaded data, or even user created data that you manage on behalf of the user.
    • Autosave files.
  • Use the Caches directory (this is not backed up by Time Machine), appending your <bundle_ID>, for cached data files or any files that your application can recreate easily.
  • Use CFPreferences to read and write your application's preferences. This will automatically write preferences to the appropriate location and read them from the appropriate location. This data is backed up by Time Machine.
  • Use NSTemporaryDirectory (this is not backed up by Time Machine) to store temporary files that you intend to use immediately for some ongoing operation but then plan to discard later. Delete temporary files as soon as you are done with them.

String support functions

The following string support functions are needed for our use of the NSSearchPathForDirectoriesInDomains() function:

// String support functions (source https://macpgmr.github.io)

Uses
 ...
 SysUtils,
 CocoaAll,
 ...;

function CFStrToAnsiStr(cfStr    : CFStringRef;
                        encoding : CFStringEncoding = kCFStringEncodingWindowsLatin1): AnsiString;
 {Convert CFString to AnsiString.
  If encoding is not specified, encode using CP1252.}
var
  StrPtr   : Pointer;
  StrRange : CFRange;
  StrSize  : CFIndex;
begin
  StrSize := 0;

  if cfStr = nil then
    begin
    Result := '';
    Exit;
    end;

   {First try the optimized function}
  StrPtr := CFStringGetCStringPtr(cfStr, encoding);
  if StrPtr <> nil then  {Succeeded?}
    Result := PChar(StrPtr)
  else  {Use slower approach - see comments in CFString.pas}
    begin
    StrRange.location := 0;
    StrRange.length := CFStringGetLength(cfStr);

     {Determine how long resulting string will be}
    CFStringGetBytes(cfStr, StrRange, encoding, Ord('?'),
                     False, nil, 0, StrSize);
    SetLength(Result, StrSize);  {Expand string to needed length}

    if StrSize > 0 then  {Convert string?}
      CFStringGetBytes(cfStr, StrRange, encoding, Ord('?'),
                       False, @Result[1], StrSize, StrSize);
    end;
end;  {CFStrToAnsiStr}

function NSStrToAnsiStr(aNSStr   : NSString;
                        encoding : CFStringEncoding = kCFStringEncodingWindowsLatin1): AnsiString;
 {Convert NSString to AnsiString.
  If encoding is not specified, encode using CP1252.}
begin
   {Note NSString and CFStringRef are interchangable}
  Result := CFStrToAnsiStr(CFStringRef(aNSStr), encoding);
end;


Get significant directories function

function GetSignificantDir(DirLocation: qword; DomainMask: qword; Count: byte): string;
var
  paths : NSArray;
begin
  paths := NSSearchPathForDirectoriesInDomains(DirLocation, DomainMask, True);
  if(count < paths.count) then
     Result := NSStrToAnsiStr(paths.objectAtIndex(count))
  else
    Result := '';
end;


Directory Locations
Directory name Directory Description
NSApplicationDirectory Supported applications (/Applications)
NSDemoApplicationDirectory Unsupported applications and demonstration versions
NSDeveloperApplicationDirectory Developer applications (/Developer/Applications)
NSAdminApplicationDirectory System and network administration applications
NSLibraryDirectory Various user-visible documentation, support, and configuration files (/Library)
NSDeveloperDirectory Developer resources (/Developer)
NSUserDirectory User home directories (/Users)
NSDocumentationDirectory Documentation
NSDocumentDirectory Document directory
NSCoreServiceDirectory Core services (System/Library/CoreServices)
NSAutosavedInformationDirectory The user’s autosaved documents (Library/Autosave Information)
NSDesktopDirectory The user’s desktop directory
NSCachesDirectory Discardable cache files (Library/Caches)
NSApplicationSupportDirectory Application support files (Library/Application Support)
NSDownloadsDirectory The user’s downloads directory
NSInputMethodsDirectory Input Methods (Library/Input Methods)
NSMoviesDirectory The user’s Movies directory (~/Movies)
NSMusicDirectory The user’s Music directory (~/Music)
NSPicturesDirectory The user’s Pictures directory (~/Pictures)
NSPrinterDescriptionDirectory The system’s PPDs directory (Library/Printers/PPDs)
NSSharedPublicDirectory The user’s Public sharing directory (~/Public)
NSPreferencePanesDirectory The PreferencePanes directory for use with System Preferences (Library/PreferencePanes)
NSApplicationScriptsDirectory The user scripts folder for the calling application (~/Library/Application Scripts/<code-signing-id>
NSItemReplacementDirectory The constant used to create a temporary directory
NSAllApplicationsDirectory All directories where applications can be stored
NSAllLibrariesDirectory All directories where resources can be stored
NSTrashDirectory The user's trash directory
Path Domains
Domain name Domain Description
NSUserDomainMask The user’s home directory—the place to install user’s personal items (~).
NSLocalDomainMask The place to install items available to everyone on this machine
NSNetworkDomainMask The place to install items available on the network (/Network)
NSSystemDomainMask A directory for system files provided by Apple (/System)
NSAllDomainsMask All domains

You can now locate various significant directories and display them as demonstrated by the code examples below.

User caches directory

procedure TForm1.MenuItem5Click(Sender: TObject);
begin
  ShowMessage('User caches dir: ' + GetSignificantDir(NSCachesDirectory,NSUserDomainMask,0));
end;

User trash directory

procedure TForm1.MenuItem16Click(Sender: TObject);
begin
  ShowMessage('User trash dir: '
          + GetSignificantDir(NSTrashDirectory,NSUserDomainMask,0));
end;

Users directory

Maybe not what you expect... this is the /Users directory :-)

procedure TForm1.MenuItem10Click(Sender: TObject);
begin
  ShowMessage('Users dir: ' +  GetSignificantDir(NSUserDirectory,NSLocalDomainMask,0));
end;

To retrieve the current user's home directory, you need to use the NSHomeDirectory() function instead:

procedure TForm1.MenuItem11Click(Sender: TObject);
begin
  ShowMessage('User''s dir: ' + NSStrToAnsiStr(NSHomeDirectory));
end;

All application directories

procedure TForm1.MenuItem7Click(Sender: TObject);
var
  count: byte;
begin
  for count := 0 to 12 do
   if GetSignificantDir(NSAllApplicationsDirectory, NSAllDomainsMask, count) <> '' then
      ShowMessage('Application dir ' + IntToStr(count) + ': '
           + GetSignificantDir(NSAllApplicationsDirectory,NSAllDomainsMask,count))
   else
      exit; 
end;

All user application directories

procedure TForm1.MenuItem8Click(Sender: TObject);
var
  count: byte;
begin
  for count := 0 to 12 do
   if GetSignificantDir(NSAllApplicationsDirectory, NSUserDomainMask, count) <> '' then
      ShowMessage('User application dir ' + IntToStr(count) + ': '
           + GetSignificantDir(NSAllApplicationsDirectory,NSUserDomainMask,count))
   else
      exit; 
end;

All system application directories

procedure TForm1.MenuItem9Click(Sender: TObject);
var
  count: byte;
begin
  for count := 0 to 12 do
   if GetSignificantDir(NSAllApplicationsDirectory, NSSystemDomainMask, count) <> '' then
      ShowMessage('System application dir ' + IntToStr(count) + ': '
           + GetSignificantDir(NSAllApplicationsDirectory,NSSystemDomainMask,count))
   else
      exit; 
end;