Locating the macOS application resources directory
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
- Apple's Bundle Programming Guide.