Wiki copymirror

This is a mirror of the wiki at pascal.chat. Editing, history and sign-in are there.

Locating the macOS application resources directory

(macOS only)

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.

macOS File Storage Overview

Application Resources Directory

The basic structure of the application bundle is:

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

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

Using a CFBundle object

CFBundle allows you to use a folder hierarchy called a bundle to organize and locate many types of application resources including images, sounds, localized strings, and executable code.

Set out below are two ways to find the location of the Resources directory using the Core Foundation bundle object.

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:

pascal
...

{$modeswitch objectivec1} 

interface

Uses
  MacOSAll;

...

function GetBundlePath(): string;
var
  pathRef: CFURLRef;
  pathCFStr: CFStringRef;
  pathStr: shortstring;
  status: Boolean = false;
begin
  pathRef := CFBundleCopyBundleURL(CFBundleGetMainBundle());
  pathCFStr := CFURLCopyFileSystemPath(pathRef, kCFURLPOSIXPathStyle);

  status := CFStringGetPascalString(pathCFStr, @pathStr, 255, CFStringGetSystemEncoding());

  if(status = true) then
    Result := pathStr
  else
    raise Exception.Create('Error in GetBundlePath()');
end;

...

procedure TForm1.MenuItem1Click(Sender: TObject);
begin
  ShowMessage(GetBundlePath + PathDelim + 'Contents' + PathDelim + 'Resources' + PathDelim);
end;

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:

pascal
...

{$modeswitch objectivec1} 

interface

Uses
  ...
  MacOSAll,
  CocoaAll;

...

function GetResourcesPath(): string;
var
  pathStr: shortstring;
  status: Boolean = false;
begin
  status := CFStringGetPascalString(CFStringRef(NSBundle.mainBundle.resourcePath), @pathStr, 255, CFStringGetSystemEncoding());

  if(status = true) then
    Result := pathStr + PathDelim
  else
    raise Exception.Create('Error in GetResourcesPath()');
end;

...

procedure TForm1.MenuItem1Click(Sender: TObject);
begin
  ShowMessage(GetResourcesPath);
end;

If you just want to retrieve the directory in which your application bundle is located, it is a simple matter of extracting it from the GetBundlePath() function above like this:

pascal
...

Uses
  SysUtils,
  ...

...

procedure TForm1.MenuItem1Click(Sender: TObject);
begin
  ShowMessage(ExtractFilePath(GetBundlePath));
end;

Using an NSBundle object

Apple uses bundles to represent applications, frameworks, plug-ins, and many other specific types of content. Bundles organize their contained resources into well-defined subdirectories, and bundle structures vary depending on the platform and the type of the bundle. By using a bundle object, you can access a bundle's resources without knowing the structure of the bundle. The bundle object provides a single interface for locating items, taking into account the bundle structure, user preferences, available localizations, and other relevant factors. Any executable can use a bundle object to locate resources, either inside an application’s bundle or in a known bundle located elsewhere. You don't use a bundle object to locate files in a container directory or in other parts of the file system.

The unit below demonstrates how to find the application bundle directory, the bundle resources directory and various other bundle paths using the Foundation NSBundle object.

pascal
unit Unit1;

{$mode objfpc}{$H+}
{$modeswitch objectivec1}

interface

uses
  Classes,
  Forms,
  Dialogs,    // Needed for ShowMessage
  StdCtrls,   // Needed for Buttons
  SysUtils,   // Needed for pathdelimiter
  CocoaAll,   // Needed for NSBundle
  CocoaUtils; // Needed for NSStringToString

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    Button5: TButton;
    Button6: TButton;
    Button7: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);
    procedure Button7Click(Sender: TObject);
  private

  public

  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

Function GetMainBundlePath: string;
begin
  Result := NSStringToString(NSBundle.mainBundle.bundlePath);
end;

function GetExecutablePath : string;
begin
  Result := NSStringToString(NSBundle.mainBundle.executablePath);
end;

function GetResourcesPath : string;
begin
  Result := NSStringToString(NSBundle.mainBundle.resourcePath) + PathDelim;
end;

Function GetSoundResourcePath(Sound: NSString): string;
begin
  Result := NSStringToString(NSBundle.mainBundle.pathForSoundResource(Sound));
end;

Function GetBundleIdentifier : string;
begin
  Result := NSStringToString(NSBundle.mainBundle.bundleIdentifier);
end;

Function GetSharedSupportPath : string;
begin
  Result := NSStringToString(NSBundle.mainBundle.sharedSupportPath);
end;

Function GetPathForImageResource(Image: NSString) : string;
begin
  Result := NSStringToString(NSBundle.mainBundle.pathForImageResource(Image));
end;

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
begin
  ShowMessage(GetExecutablePath);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  ShowMessage(GetResourcesPath);
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  ShowMessage(GetMainBundlePath);
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
  // Find path to the Glass.aiff sound file in the resources directory
  ShowMessage(GetSoundResourcePath(NSStr('Glass')));
end;

procedure TForm1.Button5Click(Sender: TObject);
begin
  ShowMessage(GetBundleIdentifier);
end;

procedure TForm1.Button6Click(Sender: TObject);
begin
  ShowMessage(GetSharedSupportPath);
end;

procedure TForm1.Button7Click(Sender: TObject);
begin
  // Find path to world1.png in resources directory
  ShowMessage(GetPathForImageResource(NSStr('world1')));
end;

end.

You can also simplify the above by omitting the CocoaUtils unit (losing access to NSStringToString) and use the UTF8String function. For example:

pascal
function GetExecutablePath : string;
begin
  //Result := NSStringToString(NSBundle.mainBundle.executablePath);
  Result := NSBundle.mainBundle.executablePath.UTF8String;
end;

See also

External links


From the Free Pascal wiki, page "Locating the macOS application resources directory", as it stood on 2021-03-26 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's Licensing page. If you have concerns about this material, message the admin.

Revision 1, last changed by WikiArchive .