Difference between revisions of "Locating the macOS application resources directory"

From Lazarus wiki
(More macOS content :-)
 
m (Minor code tweak)
Line 92: Line 92:
 
function GetResourcesPath(): string;
 
function GetResourcesPath(): string;
 
begin
 
begin
   Result := CFStrToAnsiStr(CFStringRef(NSBundle.mainBundle.resourcePath));
+
   Result := CFStrToAnsiStr(CFStringRef(NSBundle.mainBundle.resourcePath)) + PathDelim;
 
end;  
 
end;  
 
</syntaxhighlight>
 
</syntaxhighlight>

Revision as of 16:05, 17 December 2019

macOSlogo.png

This article applies to macOS only.

See also: Multiplatform Programming Guide

Resources are data files that live outside of your application’s executable file. The resources directory is where you should store these files (eg your image files, sound files, icon files and other data files necessary for your application's operation). The contents of this directory may be further divided into subdirectories where you can store localized and non-localized versions of your resource files.

The basic structure of the application bundle is:

MyApp.app/
   Contents/
      Info.plist
      MacOS/
      Resources/

A more complex application bundle layout can be seen in my Apple Help Book article.

Set out below are two ways to find the location of the resources directory. The first way is to retrieve the bundle directory (ie MyApp.app/) and concatenate the standard contents and resources directories to create the full resources directory path like so:

...
Uses
  MacOSAll;
...
function GetBundlePath(): string;

var
  pathRef: CFURLRef;
  pathCFStr: CFStringRef;
  pathStr: shortstring;
begin
  pathRef := CFBundleCopyBundleURL(CFBundleGetMainBundle());
  pathCFStr := CFURLCopyFileSystemPath(pathRef, kCFURLPOSIXPathStyle);
  CFStringGetPascalString(pathCFStr, @pathStr, 255, CFStringGetSystemEncoding());
  CFRelease(pathRef);
  CFRelease(pathCFStr);
...
  Result := pathStr;
end;

...
AppResDir := GetBundlePath + '/Contents/Resources/';


The second way is to actually interrogate the application bundle for the full path to its resources directory without the need to concatenate any directories as shown here:

...
Uses
  MacOSAll;
...

// CFStrToAnsiStr() from https://macpgmr.github.io/
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 GetResourcesPath(): string;
begin
  Result := CFStrToAnsiStr(CFStringRef(NSBundle.mainBundle.resourcePath)) + PathDelim;
end;


External links