Difference between revisions of "macOS Programming Tips"

From Lazarus wiki
Jump to navigationJump to search
(Relocate Detecting Light or Dark mode to Apple-specific UI elements)
(46 intermediate revisions by 3 users not shown)
Line 1: Line 1:
 
{{Platform only|macOS}}
 
{{Platform only|macOS}}
 
{{macOS Programming Tips}}
 
{{macOS Programming Tips}}
{{Other Interfaces}}
 
== Cross compiling macOS applications on Linux ==
 
See [[Cross compiling OSX on Linux|Cross compiling macOS on Linux]]
 
  
==Choice of Lazarus LCL widgetsets==
+
== Choice of Lazarus LCL widgetsets ==
  
 
With macOS there are various widgetsets which can be used with Lazarus. Each has its strengths and weaknesses.
 
With macOS there are various widgetsets which can be used with Lazarus. Each has its strengths and weaknesses.
Line 11: Line 8:
 
'''Carbon widgetset''' (see the [[Carbon Interface]] page)
 
'''Carbon widgetset''' (see the [[Carbon Interface]] page)
  
{| class="wikitable"
+
{| class="wikitable" width=100%
 
! Pros !! Cons
 
! Pros !! Cons
 
|-
 
|-
|All standard LCL controls working||The entire Carbon framework has been deprecated by Apple. It is not available in 64 bit macOS applications. It was completely removed from macOS 10.15 Catalina (October 2019).
+
|style="width: 50%"|All standard LCL controls working||style="width: 50%"|The entire Carbon framework has been deprecated by Apple. It is not available in 64 bit macOS applications. It was completely removed from macOS 10.15 Catalina (October 2019).
 
|-
 
|-
 
|No additional libraries or frameworks to install; uses Carbon framework included with macOS
 
|No additional libraries or frameworks to install; uses Carbon framework included with macOS
Line 23: Line 20:
 
'''Cocoa widgetset''' (see the [[Cocoa Interface]] page)
 
'''Cocoa widgetset''' (see the [[Cocoa Interface]] page)
  
{| class="wikitable"
+
{| class="wikitable" width=100%
 
! Pros !! Cons
 
! Pros !! Cons
|-
+
|-  
|All standard LCL controls working || There may still be some minor bugs lurking                                       
+
|style="width: 50%"|All standard LCL controls working ||style="width: 50%"| There may still be some minor bugs lurking; please report them!                                        
 
|-
 
|-
 
|No additional libraries or frameworks to install;<br> uses the Cocoa framework included with macOS
 
|No additional libraries or frameworks to install;<br> uses the Cocoa framework included with macOS
Line 42: Line 39:
 
! Pros !! Cons
 
! Pros !! Cons
 
|-
 
|-
|All standard LCL controls working||Ugly; clunky common dialogs; doesn't look like Mac software
+
|style="width: 50%"|All standard LCL controls working||style="width: 50%"|Ugly; clunky common dialogs; doesn't look like Mac software
 
|-
 
|-
 
|This widgetset is well tested||Requires X11 and bulky GTK to be installed to run Lazarus or any GUI apps created with Lazarus
 
|This widgetset is well tested||Requires X11 and bulky GTK to be installed to run Lazarus or any GUI apps created with Lazarus
 
|-
 
|-
| ||Have to start Lazarus at command line -- can't double-click Lazarus or place on dock (same with GUI apps) -- although see next topic for how you can add this capability yourself
+
| ||Have to start Lazarus at command line -- can't double-click Lazarus or place on dock (same with GUI apps) -- although see [[#Creating an app bundle for a GTK application|Creating an app bundle for a GTK application]] for how you can add this capability yourself.
 
|}
 
|}
 
  
 
'''Qt widgetset''' (see the [[Qt Interface Mac]] page)
 
'''Qt widgetset''' (see the [[Qt Interface Mac]] page)
Line 55: Line 51:
 
! Pros !! Cons
 
! Pros !! Cons
 
|-
 
|-
|Native macOS look and feel||Requires the Qt interface framework to be installed to run app, which is rather large
+
|style="width: 50%"|Native macOS look and feel||style="width: 50%"|Requires the Qt interface framework to be installed to run app, which is rather large
 
|-
 
|-
 
|All standard LCL controls working||
 
|All standard LCL controls working||
Line 66: Line 62:
 
|}
 
|}
  
==Creating an app bundle for a GTK application==
+
== Using Xcode for Pascal projects ==
  
macOS needs an app bundle for an executable file in order to drop it on the dock or launch it by double-clicking. An app bundle is really just a special folder that can have the same name as the executable, but with an .app extension. Finder doesn't display the .app extension, although you'll see it if you use ls in a Terminal window. For details of how to create an app bundle for a GTK application, see [[Creating an app bundle for GTK applications]].
+
For details of how to use Xcode for Pascal projects refer to [https://macpgmr.github.io/ObjP/ProjectXC.html this page].
  
== Using Xcode for Pascal projects ==
+
== Cross compiling macOS applications ==
 +
 
 +
See the [[Cross compiling]] article.
 +
 
 +
== Commonly used Unix commands ==
 +
 
 +
If you’re coming to macOS from Windows, you may find some of its Unix terminal commands confusing. Here are some equivalents. For more information about a command, enter <tt>man command</tt> in an Applications > Utilities > Terminal.
  
For details of how to use Xcode for Pascal projects refer to [https://macpgmr.github.io/ObjP/ProjectXC.html this page].
+
{| class="wikitable"
 +
! Action !! Windows command prompt window !! macOS Terminal or X11 window
 +
|-
 +
|Change to a different directory||cd||cd
 +
|-
 +
|Make a new directory||mkdir||mkdir
 +
|-
 +
|Delete a directory||rmdir||rmdir
 +
|-
 +
|List file directory in chronological order with detail||dir /od||ls -ltr
 +
|-
 +
|Copy a file, preserving its date-time stamp||copy||cp -p
 +
|-
 +
|Display contents of a text file||type||cat
 +
|-
 +
|Delete a file||erase||rm
 +
|-
 +
|Move a file||move||mv
 +
|-
 +
|Rename a file||ren||mv
 +
|-
 +
|Find a file||dir /s||find
 +
|-
 +
|Grep a file||findstr||grep
 +
|-
 +
|Display differences between two text files||fc||diff
 +
|-
 +
|Change file attributes||attrib||chmod
 +
|-
 +
|“Super-user” root authorization||N/A||sudo
 +
|-
 +
|Create symbolic link to a file or directory||mklink||ln
 +
|-
 +
|Shrink executable file size||strip (included w/ Free Pascal)||strip
 +
|}
  
==Using stdout for logging and debugging==
+
== Useful commands and tools included with macOS ==
  
=== Using a Terminal ===
+
'''open'''
  
Unix systems usually allow output to stdout for GUI applications (while for Windows it requires creating a console object for stdout, using file handle as stdout or linking the application as a non-GUI). The output is really useful for debugging and logging purposes.  
+
See the [[macOS Open Sesame]] article for the many uses of the macOS <tt>open</tt> command.
  
macOS GUI applications (the ones created by Lazarus) are deployed as [http://en.wikipedia.org/wiki/Application_bundle bundles]. The bundled application conceals the output. However, it is possible to see the output generated by a simple WriteLn(), by launching the application bundle's executable from a Terminal. Here are the steps:
+
'''zip / unzip'''
  
* Launch Terminal (found in /Applications/Utilities)
+
These standard command-line programs are pre-installed.
* Change to the project directory
 
* Launch the executable:
 
  $ ./MyApp.app/Contents/MacOS/MyApp
 
* once MyApp is running, the output will be seen in the Terminal window
 
  
[[Image:OSXStdOutExample.png|550px]]
+
'''Console'''
  
This is especially useful when using Project Options > Compiler Options > Debugging when you have ticked the Other debugging info - Use Heaprtc unit (check for mem-leaks). The Terminal will show the report:
+
Drag this app from /Applications/Utilities and drop it on the dock so you always have it handy. Launch it whenever you want to see messages or errors output to the console by GUI apps (for example, when they crash). Invaluable for debugging.
  
  Heap dump by heaptrc unit of ./MyApp
+
'''Activity Monitor'''
  1441 memory blocks allocated : 99080/102040
 
  1441 memory blocks freed    : 99080/102040 
 
  0 unfreed memory blocks : 0
 
  True heap size : 458752 (32 used in System startup)
 
  True free heap : 458720
 
  
=== Using the Console ===
+
This app is also in /Applications/Utilities and is useful for monitoring CPU, memory, energy, network and disk usage.
  
It is also possible to see the output by using the native macOS NSLog() procedure which logs your error message to the Apple System Log facility which you can read using the Console utility (located in Applications > Utilities). As there will be many message entries scrolling by, add MyApp to the search window to filter the entries being shown. For example:
+
'''otool'''
  
<syntaxhighlight lang="pascal">
+
Use <tt>otool</tt> to display information about an executable file or library. For example, enter the following to see information about Lazarus:
  MyAudioPlayer := AVAudioPlayer.alloc.initWithContentsOfURL_error(url, @err);
 
  
  if Assigned(MyAudioPlayer) then
+
<syntaxhighlight lang="bash">
      MyAudioPlayer.play
+
cd /usr/local/share/lazarus
  else
+
otool –L lazarus
    NSLog(NSStr('Error in procedure PlayAudio(): %@'), err);
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
When the audio file is missing from the application's Resources directory, the following appears in the Console system log:
+
This shows that Lazarus is dependent on various libraries in /usr/lib and /System/Library/Frameworks/, as you would expect. Open a Terminal and type <tt>man otool</tt> to view its manual page.
 +
 
 +
'''install_name_tool'''
 +
 
 +
Use <tt>install_name_tool</tt> with the –change switch to change where an executable file or library looks for a library that it requires. Open a Terminal and type <tt>man install_name_tool</tt> to view its manual page.
 +
 
 +
'''Grab'''
 +
 
 +
Use Grab (in /Applications/Utilities) to create a screenshot or window shot, then save it to a disk file.
 +
Note: replaced in recent versions of macOS by CMD+SHIFT+4 for image capture and CMD+SHIFT+5 for video capture (full or part screen).
 +
 
 +
'''iconutil'''
 +
 
 +
Use <tt>iconutil</tt> in /usr/bin/ to create icon files (.icns) for use with your .app bundles. Open a Terminal and type <tt>man iconutil</tt> for its manual page.
 +
 
 +
'''pkgbuild, productbuild / Disk Utility'''
 +
 
 +
Use these apps to create disk image (.dmg) files for deploying your apps. See [[Deploying Your Application]] for more information.
 +
 
 +
Command line tools <tt>pkgbuild</tt> and <tt>productbuild</tt> are in /usr/bin/. Disk Utility is in /Applications/Utilities. You can drag and drop Disk Utility on the dock.
  
[[Image:NSLog_Console.png]]
+
A popular third party option is the GUI application ''Packages'' (http://s.sudre.free.fr/Software/Packages/about.html) which creates .pkg installer archives.
  
==Deploying an application for an older version of the operating system==
+
'''Script Editor / osascript'''
  
When compiling an application on without any special options, the application is only guaranteed to work on that particular major operating system release and later (eg when compiling under Snow Leopard 10.6.8, the application is only guaranteed to work on Snow Leopard 10.6.8 and later).
+
Use Script Editor to edit, compile and run AppleScript files. It’s located in /Applications/Utilities/.
  
Here is an example of an error that you may encounter when running an application compiled for Leopard 10.5 under Tiger 10.4 in case you use the widestring manager:
+
Use <tt>osascript</tt> from /usr/bin to execute an AppleScript command or file from the command line or from a script file. For more information, enter <tt>man osascript</tt> in a Terminal to view its manual page.
  
<pre>
+
== Cross-platform considerations ==
dyld: Library not loaded: /usr/lib/libiconv.2.dylib
 
  Referenced from: /Volumes/..../yourprogram
 
  Reason: Incompatible library version: yourprogram requires version 7.0.0 or later, but libiconv.2.dylib provides version 5.0.0
 
Trace/BPT trap
 
</pre>
 
  
Here is an example of an error that you may encounter when running an application compiled for Snow Leopard 10.6.8 under Snow Leopard 10.6.7:
+
When you plan to compile the same application for different widget sets (eg Windows, GTK and Cocoa) or plan to use multiple languages, you should layout the controls on your form(s) using the [[Anchor_Sides|anchor editor]] and set the [[Autosize_/_Layout|AutoSize]] property of your controls to ''True''. Doing this from the beginning will save you a lot of frustrating layout problems later.
Runtime error 203
 
  
===FPC 2.6.2 and above===
+
== Apple-specific UI elements ==
  
See the article [[FPC_New_Features_2.6.2#Support_for_specifying_and_querying_the_deployment_version|Support for specifying and querying the deployment version]]
+
The Apple user interface in macOS has a number of unique features (eg the Dock, Apple main menu bar, sheets, user notifications,  etc). For details on how to provide these non-cross-platform features in your applications, see the article [[Apple-specific UI elements]].
  
====Adding the custom options only when compiling for macOS====
+
== How to obtain the path to the Bundle ==
  
To add the above custom options only when compiling for macOS add to Project / Compiler options / IDE macros / Conditionals:
+
<syntaxhighlight lang="pascal">  
<syntaxhighlight lang="pascal">
+
...
if TargetOS = 'darwin' then begin
+
uses
   UsageCustomOptions += ' -WM10.6';
+
  MacOSAll;
 +
...
 +
 +
function GetBundlePath(): string;
 +
var
 +
  pathRef: CFURLRef;
 +
  pathCFStr: CFStringRef;
 +
  pathStr: shortstring;
 +
begin
 +
  pathRef := CFBundleCopyBundleURL(CFBundleGetMainBundle());
 +
  pathCFStr := CFURLCopyFileSystemPath(pathRef, kCFURLPOSIXPathStyle);
 +
  CFStringGetPascalString(pathCFStr, @pathStr, 255, CFStringGetSystemEncoding());
 +
 +
   Result := pathStr;
 
end;
 
end;
 
</syntaxhighlight>
 
</syntaxhighlight>
  
===FPC 2.6.1 and below===
+
This returns the full path up to and including the application bundle name (eg /Users/Me/MyApp.app).
  
To make sure that it runs under previous macOS releases, use the ''-macosx_version_min'' linker parameter and link against the appropriate SDK (e.g. -XR/Developer/SDKs/MacOSX10.5.sdk/ or -XR/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/). For example, to compile an application that should work on Leopard 10.5 and higher:
+
== Determining where to store files ==
  
Add to /etc/fpc.cfg OR to Project / Compiler options / Other / Custom options:
+
{{macOS_file_storage}}
<pre>
+
 
-k-macosx_version_min -k10.5
+
== Determining if a file is readable ==
-XR/Developer/SDKs/MacOSX10.5.sdk/
+
 
</pre>
+
The Lazarus function [https://lazarus-ccr.sourceforge.io/docs/lazutils/lazfileutils/fileisreadable.html fileisreadable] reports if the user has read permissions for the specified file. However, modern versions of macOS will restrict [https://developer.apple.com/library/archive/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html access to the following locations]:
 +
 
 +
* The app container directory. Upon first launch, the operating system creates a special directory for use by your app—and only by your app—called a container. Each user on a system gets an individual container for your app, within their home directory; your app has unfettered read/write access to the container for the user who ran it.
 +
* App group container directories. A sandboxed app can specify an entitlement that gives it access to one or more app group container directories, each of which is shared among all apps with that entitlement.
 +
* User-specified files. A sandboxed app (with an appropriate entitlement) automatically obtains access to files in arbitrary locations when those files are explicitly opened by the user or are dragged and dropped onto the application by the user.
 +
* Related items. With the appropriate entitlement, your app can access a file with the same name as a user-specified file, but a different extension. This can be used for accessing files that are functionally related (such as a subtitle file associated with a movie) or for saving modified files in a different format (such as re-saving an RTF flat file as an RTFD container after the user added a picture).
 +
* Temporary directories, command-line tool directories, and specific world-readable locations. A sandboxed app has varying degrees of access to files in certain other well-defined locations.
 +
 
 +
At the moment, there appears to be no reliable API to determine if a file is readable in all situations. Apple's own ''isReadableFileAtPath'' method is essentially a wrapper around the BSD ''access'' system call and does not take into account the various security and privacy features that Apple has added since macOS 10.0. Below, is a brute force method that determines if a file is readable by physically reading the first byte of data from the file. This is necessarily slower than simply reading the file permissions, but does provide a robust solution:
 +
 
 +
<syntaxhighlight lang=pascal>
 +
function FileIsReadableByThisExecutable(const AFilename: string): boolean;
 +
// macOS will block applications from reading files outside their sandbox
 +
// therefore, simply knowing the user has read acces is not sufficient
 +
// requires "uses LazFileUtils"
 +
var
 +
  f: file;
 +
  b: byte;
 +
begin
 +
  Result := lazfileutils.FileIsReadable(AFilename);
 +
  {$IFDEF Darwin}
 +
  if not Result then exit; //globally not readable
 +
  if FileSizeUtf8(AFilename) < 1 then exit(false);
 +
  AssignFile(f, AFilename);
 +
  {$I+}
 +
  try
 +
    FileMode := fmOpenRead;  //Set file access to read only
 +
    Reset(f, 1);
 +
    if ioresult <> 0 then
 +
      exit;
 +
    b := 0;
 +
    BlockRead(f, b, sizeof(b));
 +
    CloseFile(f);
 +
    result := true;
 +
  except
 +
    result := false;
 +
  end;
 +
  {$ENDIF}
 +
end;
 +
</syntaxhighlight>
  
The 10.4 SDK is the only one with a special name (10.4u instead of 10.4). Other SDK names simply contain the major macOS version number: MacOSX10.5.sdk, MacOSX10.6.sdk, ...
+
== Accessing system information ==
  
Note: The path ''/Developer'' depends on where you installed the Apple developer tools, and may be different if you chose a different location. Do not assume it will always be /Developer.
+
Sysctl provides an interface that allows you to retrieve many kernel parameters in macOS (and Linux and the BSDs). This provides a wealth of information detailing system hardware which can be useful when debugging user issues or simply optimising your application (eg to take advantage of threads on multi-core CPU systems). The NSProcessInfo class provides access to a collection of information about the current process. The IOKit framework implements non-kernel access to I/O Kit objects (eg to retrieve the Mac platform serial number and UUID). For full details and example code, see the article [[Accessing macOS System Information]].
  
The 10.7 is by default:
+
== Retrieve username/ full username ==
<pre>
 
-k-macosx_version_min -k10.7
 
-XR/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/
 
</pre>
 
  
For targeting Tiger 10.4 the following settings are required:
+
The code to retrieve the current user's username (eg joe) or full username (eg Joe Bloggs) can be found in the article [[macOS Retrieve (full)username]].
<pre>
 
-k-macosx_version_min -k10.4
 
-XR/Developer/SDKs/MacOSX10.4u.sdk/
 
</pre>
 
  
====Adding the custom options only when compiling for macOS====
+
== Making a beep ==
  
To add the above custom options only when compiling for macOS add to Project / Compiler options / IDE macros / Conditionals:
+
Procedure declaration:
  
<syntaxhighlight lang="pascal">
+
<syntaxhighlight lang=pascal>
if TargetOS = 'darwin' then begin
+
procedure NSBeep; cdecl;
  UsageCustomOptions += ' -k-macosx_version_min -k10.5';
+
   external '/System/Library/Frameworks/AppKit.framework/AppKit' name 'NSBeep';
   UsageCustomOptions += ' -XR/Developer/SDKs/MacOSX10.5.sdk/';
 
end;
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==Useful tools to download and install==
+
To use it:
  
[http://www.barebones.com/products/bbedit/ '''BBedit''']
+
<syntaxhighlight lang=pascal>
Every programmer needs a great text editor, but the macOS TextEdit app is not it. Instead, download and install Bbedit (was once called TextWrangler), a freeware text editor that can highlight Pascal syntax, even highlight script file syntax.
+
procedure MakeBeep;
 +
  begin
 +
    {$IFDEF DARWIN}
 +
    NSbeep;
 +
    {$ENDIF}
  
Other editors include AlphaX and Emacs or you can use the Xcode IDE to edit text files.
+
    [...]
 +
  end;
 +
</syntaxhighlight>
 +
 
 +
You can also assign NSBeep to the [https://www.freepascal.org/docs-html/rtl/sysutils/onbeep.html OnBeep] (TBeepHandler in the SysUtils unit), by setting it on Form creation.  
  
[http://www.neooffice.org/ '''NeoOffice''']
+
This way you can call the regular [https://www.freepascal.org/docs-html/rtl/sysutils/beep.html Beep] function (by default, Beep contains no implementation to actually produce a beep).
  
NeoOffice is a macOS version of OpenOffice.org that actually looks and behaves like a native macOS app. Once you've started using Neo you'll never want to use the plain X11 version of OO again.
+
<syntaxhighlight lang=pascal>
 +
procedure TForm1.FormCreate(Sender: TObject);
 +
  begin
 +
    {$IFDEF DARWIN}
 +
    OnBeep := @NSbeep;
 +
    {$ENDIF}
  
[http://cyberduck.ch/ '''Cyberduck]
+
    [...]
 +
  end;
 +
</syntaxhighlight>
  
Need an FTP client app for macOS? Cyberduck is an open source FTP client specifically designed for macOS.
+
== Show application in all desktop spaces ==
  
==Useful commands and tools included with macOS==
+
An application defaults to showing only in the desktop space in which it was opened. To show your application in all desktop spaces for macOS 10.5+ (Leopard):
  
'''open'''
+
<syntaxhighlight lang=pascal>
 +
{$modeswitch objectivec1}
  
Use open to start an application or open a file from the command line. This is useful when you’re working in a terminal window and don’t want to fire up the app and navigate to where you’re working just to open a file there. For example, to open a Pascal file in the current directory in TextWrangler, enter the following:
+
Uses CocoaAll ...
  
<syntaxhighlight lang="bash">open –a textwrangler myfile.pas</syntaxhighlight>
+
procedure TForm1.MenuItem35Click(Sender: TObject);
 +
var
 +
  theWindow: NSWindow;
 +
begin
 +
  theWindow := NSView(Form1.Handle).window;      // Form1 is the name of the main form - adjust as necessary
 +
  theWindow.setCollectionBehavior(theWindow.collectionBehavior or NSWindowCollectionBehaviorCanJoinAllSpaces);
 +
end;
 +
</syntaxhighlight>
  
'''zip / unzip'''
+
== Launch/Toggle full screen ==
  
These standard command-line programs are pre-installed.
+
To toggle an application window full screen for macOS 10.7+ (Lion):
  
'''Console'''
+
<syntaxhighlight lang=pascal>
 +
{$modeswitch objectivec1}
  
Drag this app from /Applications/Utilities and drop it on the dock so you always have it handy. Launch it whenever you want to see messages or errors outputted to the console by GUI apps (for example, when they crash). Invaluable for debugging.
+
Uses CocoaAll ...
  
'''Activity Monitor'''
+
procedure TForm1.Button1Click(Sender: TObject);
 +
var
 +
  Window: NSWindow;
 +
begin
 +
  Window := NSView(Form1.Handle).window;
 +
  Window.toggleFullScreen(Nil);
 +
end;
 +
</syntaxhighlight>
  
This app is also in /Applications/Utilities and is useful for monitoring CPU and disk usage.
+
To launch your application full screen you can call the button event handler (as below) or move the button event handler code (above) to the form's on activate event handler:
  
'''otool / install_name_tool'''
+
<syntaxhighlight lang=pascal>
 +
procedure TForm1.FormActivate(Sender: TObject);
 +
begin
 +
  Form1.Button1Click(Form1);
 +
end; 
 +
</syntaxhighlight>
  
Use otool to display information about an executable file or library. For example, enter the following to see information about Lazarus:
+
== Locale settings (date & time separator, currency symbol, ...) ==
  
<syntaxhighlight lang="bash">
+
On macOS, like on other Unix platforms, the RTL does not load the locale settings by default. They can be initialised in three ways. See the article [[Locale settings for macOS]] for details.
cd /usr/local/share/lazarus
 
otool –L lazarus
 
</syntaxhighlight>
 
  
This shows that Lazarus is dependent on various libraries in /usr/lib and /System/Library/Frameworks/, as you would expect.
+
== Accessibility for users with special needs ==
  
Use install_name_tool with the –change switch to change where an executable file or library looks for a library that it requires.
+
macOS includes a wide variety of features and assistive technologies, such as screen and cursor magnification, a full-featured screen reader, visual flash alerts, closed captioning support, and much more. Take advantage of these features to make your apps accessible to users with special needs. Please refer to the [[LCL Accessibility|Accessibility]] article for more details.
  
'''Grab'''
+
== LCL FontDialog workaround ==
  
Use Grab (in /Applications/Utilities) to create a screenshot or window shot, then save it to a disk file.
+
The FontDialog.OnApplyClicked event handler is not supported in macOS. So this code will have no effect (the message will not be shown):
Note: replaced in recent versions of macOS by CMD+SHIFT+4 for image capture and CMD+SHIFT+5 for video capture (full or part screen).
 
  
'''iconutil'''
+
<syntaxhighlight lang=pascal>
 +
procedure TForm1.Button1Click(Sender: TObject);
 +
begin
 +
  FontDialog1.Execute;
 +
end;
 +
 +
procedure TForm1.FontDialog1ApplyClicked(Sender: TObject);
 +
begin
 +
  ShowMessage('Apply Clicked');
 +
end;
 +
</syntaxhighlight>
  
Use iconutil in /usr/bin/ to create icon files (.icns) for use with your .app bundles.
+
The workaround is to restructure the code as follows:
  
'''pkgbuild, productbuild / Disk Utility'''
+
<syntaxhighlight lang=pascal>
 +
procedure TForm1.Button1Click(Sender: TObject);
 +
begin
 +
  if FontDialog1.Execute then
 +
    begin
 +
      ShowMessage('Apply Clicked');
 +
    end;
 +
end;
 +
</syntaxhighlight>
  
Use these apps to create disk image (.dmg) files for deploying your apps. See [[Deploying Your Application]] for more information.
+
== Using PrintDialog in Cocoa ==
  
Command line tools pkgbuild and productbuild are in /usr/bin/. Disk Utility is in /Applications/Utilities. You can drag and drop Disk Utility on the dock.
+
PrintDialog is only partially implemented (July 2020) in Cocoa. As a result, this code does not display the dialog:
  
A popular third party option is the Packages GUI application (http://s.sudre.free.fr/Software/Packages/about.html).
+
<syntaxhighlight lang=pascal>
 +
procedure TForm1.btnPrintDialogClick(Sender: TObject);
 +
begin
 +
  if PrintDialog1.Execute then
 +
    Panel1.Invalidate;
 +
end;   
 +
</syntaxhighlight>
  
'''Script Editor / osascript'''
+
This code will display the dialog:
  
Use Script Editor to edit, compile and run AppleScript files. It’s located in /Applications/AppleScript.
+
<syntaxhighlight lang=pascal>
 +
procedure TForm1.btnPrintDialogClick(Sender: TObject);
 +
begin
 +
  PrintDialog1.Options:= PrintDialog1.Options + [poBeforeBeginDoc];
 +
  if PrintDialog1.Execute then
 +
    Panel1.Invalidate;
 +
end;   
 +
</syntaxhighlight>
  
Use osascript to execute an AppleScript command or file from the command line or from a script file. For more information, enter man:osascript in Safari once you have Sogudi installed (see above).
+
== Detecting the Apple Command key ==
  
==Commonly used Unix commands==
+
The Apple Command key is ''ssMeta''. Heres' how to test for it being depressed:
  
If you’re coming to macOS from Windows, you may find some of its Unix terminal commands confusing. Here are some equivalents. For more information about a command, enter man:''command'' in Safari once you have Sogudi installed (see above).
+
<syntaxhighlight lang=pascal>
 +
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
 +
var
 +
  ssCmd: TShiftStateEnum;
 +
begin
 +
  // Define standard menu command key as ssCmd
 +
  {$IFDEF DARWIN}
 +
    ssCmd := ssMeta; // Apple
 +
  {$ELSE}
 +
    ssCmd := ssCtrl  // FreeBSD, Linux, Windows
 +
  {$ENDIF}
  
{| class="wikitable"
+
  if (ssCmd in Shift) then
! Action !! Windows command prompt window !! macOS Terminal or X11 window
+
    Caption:='Command key pressed'
|-
+
  else
|Change to a different directory||cd||cd
+
    Caption := IntToStr(Key) + ' not the command key';
|-
+
end;   
|Make a new directory||mkdir||mkdir
+
</syntaxhighlight>
|-
 
|Delete a directory||rmdir||rmdir
 
|-
 
|List file directory in chronological order with detail||dir /od||ls -ltr
 
|-
 
|Copy a file, preserving its date-time stamp||copy||cp -p
 
|-
 
|Display contents of a text file||type||cat
 
|-
 
|Delete a file||erase||rm
 
|-
 
|Move a file||move||mv
 
|-
 
|Rename a file||ren||mv
 
|-
 
|Find a file||dir /s||find
 
|-
 
|Grep a file||findstr||grep
 
|-
 
|Display differences between two text files||fc||diff
 
|-
 
|Change file attributes||attrib||chmod
 
|-
 
|“Super-user” root authorization||N/A||sudo
 
|-
 
|Create symbolic link to a file or directory||mklink||ln
 
|-
 
|Shrink executable file size||strip (included w/ Free Pascal)||strip
 
|}
 
  
==Libraries==
+
== Libraries ==
  
===Using a library in a Mac application===
+
=== Using a library in a Mac application ===
  
 
Many software projects use external libraries to complement their functionality. For example, OpenAL might be utilized to provide sound support, or FreeType might be utilized to provide font rendering support for FCL-Image. macOS already comes with a number of open source libraries installed, being that OpenAL is one of them. If one of these libraries is utilized, one should simply add the corresponding import unit to the uses clause and start using the library. To use other open source libraries which aren't included with macOS, the easiest way is by grabbing a pre-compiled library from the application bundle of popular open source applications. FreeType for example is included inside the application bundle from Gimp in the path Gimp.app/Resources/lib/libfreetype.6.dylib. Another option is downloading the source code from the library and building it manually, but compiling a C library is usually an unpleasant task for Pascal developers.
 
Many software projects use external libraries to complement their functionality. For example, OpenAL might be utilized to provide sound support, or FreeType might be utilized to provide font rendering support for FCL-Image. macOS already comes with a number of open source libraries installed, being that OpenAL is one of them. If one of these libraries is utilized, one should simply add the corresponding import unit to the uses clause and start using the library. To use other open source libraries which aren't included with macOS, the easiest way is by grabbing a pre-compiled library from the application bundle of popular open source applications. FreeType for example is included inside the application bundle from Gimp in the path Gimp.app/Resources/lib/libfreetype.6.dylib. Another option is downloading the source code from the library and building it manually, but compiling a C library is usually an unpleasant task for Pascal developers.
Line 301: Line 425:
 
So, again in the previous example, let's say that an application requires the FreeType library. One can grab the libfreetype.6.dylib file from Gimp and then place it in a location of our source code tree, for example ../Mac/libfreetype.dylib relative to the path of the main project file, which is where the executable is placed. To properly link the application the Free Pascal command line option "-Fl../Mac" should be added, so that this path is added to the library search path. The linker will then find the library in this location and read the install path which is written inside it. At runtime the linker will search for the library in this install path. To change the install path to our desired one, the command line application install_name_tool, which comes with the Apple Developer Tools, can be used. It can be utilized with the following command line command:
 
So, again in the previous example, let's say that an application requires the FreeType library. One can grab the libfreetype.6.dylib file from Gimp and then place it in a location of our source code tree, for example ../Mac/libfreetype.dylib relative to the path of the main project file, which is where the executable is placed. To properly link the application the Free Pascal command line option "-Fl../Mac" should be added, so that this path is added to the library search path. The linker will then find the library in this location and read the install path which is written inside it. At runtime the linker will search for the library in this install path. To change the install path to our desired one, the command line application install_name_tool, which comes with the Apple Developer Tools, can be used. It can be utilized with the following command line command:
  
<syntaxhighlight lang="bash">install_name_tool libfreetype.dylib -id @executable_path/../Resources/lib/libfreetype.dylib</syntaxhighlight>
+
<syntaxhighlight lang="bash">install_name_tool libfreetype.dylib -id @executable_path/../Frameworks/libfreetype.dylib</syntaxhighlight>
  
 
One can then verify if this command worked by using the command:
 
One can then verify if this command worked by using the command:
Line 307: Line 431:
 
<syntaxhighlight lang="bash">otool -D libfreetype.dylib</syntaxhighlight>
 
<syntaxhighlight lang="bash">otool -D libfreetype.dylib</syntaxhighlight>
  
It should return the written path. When building the application bundle, we should copy the libfreetype.dylib file to the directory Resources/lib. The ''install_name_tool'' command uses the ''@executable_path'' macro to set a install path ''relative to the executable'', which is inside the MacOS folder in the application bundle. Now the application can be built, the bundle and be built and then the application can be run and it will load the library correctly from the application bundle.
+
It should return the written path. When building the application bundle, we should copy the libfreetype.dylib file to the Frameworks sub-directory. The ''install_name_tool'' command uses the ''@executable_path'' macro to set an install path ''relative to the executable'', which is inside the MacOS folder in the application bundle. Now the application can be built, the bundle and then the application can be run and it will load the library correctly from the application bundle.
  
 
=== Using a library installed using fink ===
 
=== Using a library installed using fink ===
Line 328: Line 452:
 
<syntaxhighlight lang="bash">otool -L @executable_path/your_executable_binary | grep version | cut -f 1 -d ' '</syntaxhighlight>
 
<syntaxhighlight lang="bash">otool -L @executable_path/your_executable_binary | grep version | cut -f 1 -d ' '</syntaxhighlight>
  
The list with libraries from fink only without libraries from /usr/lib and /System/Library gives this command:
+
For the list of libraries from fink only, without libraries from /usr/lib and /System/Library, use this command:
  
 
<syntaxhighlight lang="bash">otool -L @executable_path/your_executable_binary | grep version | cut -f 1 -d ' ' | grep -v \/System\/Library | grep -v \/usr\/lib</syntaxhighlight>
 
<syntaxhighlight lang="bash">otool -L @executable_path/your_executable_binary | grep version | cut -f 1 -d ' ' | grep -v \/System\/Library | grep -v \/usr\/lib</syntaxhighlight>
  
With this a script can be created, which loops over all libraries from fink in the executable.
+
With this a script can be created, which loops over all libraries from fink in the executable. Be aware of the fact that the libraries can call libraries from fink. You have to adjust them as well.
  
Beware of the fact that the libraries can itself call libraries from fink. You have to adjust them as well.
+
Another important point of libraries from fink is, that they have only one architecture, ie i386, ppc, aarch64, x86_64 or ppc64. For a universal application, you have to combine single architecture libraries with <code>lipo</code>.
  
Another important point of libraries from fink is, that they have only one architecture, i.e. i386, ppc, x86_64 or ppc64. For a universal application, you have to combine single architecture libraries with lipo.
+
=== Dynamic Libraries ===
  
==How to obtain the path to the Bundle==
+
For the details of creating and using dynamic libraries, see [[macOS Dynamic Libraries]].
  
<syntaxhighlight lang="pascal">
+
=== Static Libraries ===
...
 
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());
 
 
  Result := pathStr;
 
end;
 
</syntaxhighlight>
 
  
This returns the full path up to and including the application bundle name (eg /Users/Me/MyApp.app).
+
For the details of creating and using static libraries, see [[macOS Static Libraries]].
  
== Determining where to store files ==
+
=== Which dynamic libraries are used? ===
  
{{macOS_file_storage}}
+
A little known method for checking which dynamic libraries an executable uses is as simple as setting an environment variable. The macOS dynamic linker checks the following environment variables during the launch of each process.
  
== Accessing system information ==
+
{| class=wikitable
 +
|-
 +
! Environment Variable || Description
 +
|-
 +
| DYLD_PRINT_TO_FILE || This is a path to a (writable) file. Normally, the dynamic linker writes all logging output (triggered by DYLD_PRINT_* settings) to file descriptor 2 (usually stderr). This setting causes the dynamic linker to write logging output to the specified file.
 +
|-
 +
| DYLD_PRINT_LIBRARIES || When this is set, the dynamic linker writes to file descriptor 2 (normally  stderr) the filenames of the libraries the program is using. This is useful to make sure that your program is loading the dynamic libraries you want.
 +
|-
 +
| DYLD_PRINT_RPATHS || Causes dyld to print a line each time it expands an @rpath variable and whether that expansion was successful or not.
 +
|}
  
Sysctl provides an interface that allows you to retrieve many kernel parameters in macOS (and Linux and the BSDs). This provides a wealth of information detailing system hardware which can be useful when debugging user issues or simply optimising your application (eg to take advantage of threads on multi-core CPU systems). For full details and example code, see the article [[Accessing macOS System Information]].
+
For full details of many other environment variables and their effect, see the manual page for dyld (type <tt>man dyld</tt> in a Terminal).
  
== Retrieve username ==
+
== Application deployment/distribution ==
  
The code snippet below will retrieve the current user's username. In the example code below, the function to retrieve the username is called from a menu item.
+
=== Creating Universal binaries ===
 
 
<syntaxhighlight lang="pascal">
 
...
 
 
 
Uses
 
  CocoaAll,
 
...
 
 
 
function NSUserName: CFStringRef
 
  external name '_NSUserName';
 
...
 
 
 
procedure TForm1.MenuItem21Click(Sender: TObject);
 
var
 
  usernameStr: ShortString;
 
  status: Boolean = false;
 
begin
 
  status := CFStringGetPascalString(CFStringRef(NSusername),@usernameStr,255,CFStringGetSystemEncoding);
 
  if(status = true) then
 
    ShowMessage(usernameStr)
 
  else
 
    ShowMessage('Error retrieving username');
 
end;       
 
</syntaxhighlight>
 
 
 
== Making a beep ==
 
 
 
Procedure declaration:
 
 
 
<syntaxhighlight lang=pascal>
 
procedure NSBeep; cdecl;
 
  external '/System/Library/Frameworks/AppKit.framework/AppKit' name 'NSBeep';
 
</syntaxhighlight>
 
 
 
 
 
To use it:
 
 
 
<syntaxhighlight lang=pascal>
 
procedure MakeBeep;
 
  begin
 
    {$IFDEF DARWIN}
 
    NSbeep;
 
    {$ENDIF}
 
 
 
    [...]
 
  end;
 
</syntaxhighlight>
 
 
 
== Creating Universal binaries ==
 
  
 
Lazarus will create an application tuned for a single CPU. With the [[Carbon Interface]] version of the [[LCL]], this is either [[PowerPC]] or Intel i386 (32 bit). You may want to distribute your application as a 'Universal Binary', allowing users that may have either a PowerPC or Intel computer to use the program. To do this, you need to compile two versions of your application: one with PowerPC as the target CPU and one as Intel. Next, you need to stitch these two executables together using the macOS program "lipo" (installed with the other Xcode developer tools required by Lazarus).  For example, consider two versions of the application 'myprogram'. One in the 'ppc' folder compiled for PowerPC and one in the 'intel' folder compiled for Intel CPUs. You can then combine these two to a univeral application by running this command:
 
Lazarus will create an application tuned for a single CPU. With the [[Carbon Interface]] version of the [[LCL]], this is either [[PowerPC]] or Intel i386 (32 bit). You may want to distribute your application as a 'Universal Binary', allowing users that may have either a PowerPC or Intel computer to use the program. To do this, you need to compile two versions of your application: one with PowerPC as the target CPU and one as Intel. Next, you need to stitch these two executables together using the macOS program "lipo" (installed with the other Xcode developer tools required by Lazarus).  For example, consider two versions of the application 'myprogram'. One in the 'ppc' folder compiled for PowerPC and one in the 'intel' folder compiled for Intel CPUs. You can then combine these two to a univeral application by running this command:
Line 429: Line 495:
 
A note on 64 bit binaries: Depending on the version of macOS and after corresponding (cross-)compilers are installed, programs which do not use the LCL can also be built as ppc64 or x86_64 binaries, in particular command line utilities. lipo is used in the same way to put them into universal binaries.
 
A note on 64 bit binaries: Depending on the version of macOS and after corresponding (cross-)compilers are installed, programs which do not use the LCL can also be built as ppc64 or x86_64 binaries, in particular command line utilities. lipo is used in the same way to put them into universal binaries.
  
== Executable size optimisation ==
+
=== Executable size optimisation ===
  
 
The normal way to optimise the size of your executable with Lazarus would be:
 
The normal way to optimise the size of your executable with Lazarus would be:
Line 454: Line 520:
 
'''Solution:''' The strip command line utility will strip the symbol table. Manually running '''strip MyApp''' in a Terminal resulted in the executable size further reducing from 4,624,076 bytes to 2,922,396 bytes.
 
'''Solution:''' The strip command line utility will strip the symbol table. Manually running '''strip MyApp''' in a Terminal resulted in the executable size further reducing from 4,624,076 bytes to 2,922,396 bytes.
  
== Code Signing ==
+
=== Deploying an application for an older version of the operating system ===
 +
 
 +
When compiling an application on without any special options, the application is only guaranteed to work on that particular major operating system release and later (eg when compiling under Snow Leopard 10.6.8, the application is only guaranteed to work on Snow Leopard 10.6.8 and later).
 +
 
 +
Here is an example of an error that you may encounter when running an application compiled for Leopard 10.5 under Tiger 10.4 in case you use the widestring manager:
 +
 
 +
<pre>
 +
dyld: Library not loaded: /usr/lib/libiconv.2.dylib
 +
  Referenced from: /Volumes/..../yourprogram
 +
  Reason: Incompatible library version: yourprogram requires version 7.0.0 or later, but libiconv.2.dylib provides version 5.0.0
 +
Trace/BPT trap
 +
</pre>
 +
 
 +
Here is an example of an error that you may encounter when running an application compiled for Snow Leopard 10.6.8 under Snow Leopard 10.6.7:
 +
Runtime error 203
 +
 
 +
==== FPC 2.6.2 and above ====
 +
 
 +
See the article [[FPC_New_Features_2.6.2#Support_for_specifying_and_querying_the_deployment_version|Support for specifying and querying the deployment version]] which is current up to and including FPC 3.04.
 +
 
 +
For FPC 3.2 (aka fixes branch) see the article [[User_Changes_3.2#Default_Target_macOS_version|Default target macOS version]].
 +
 
 +
'''Adding custom options only when compiling for macOS'''
 +
 
 +
To add the above custom options only when compiling for macOS add to Project / Compiler options / IDE macros / Conditionals:
 +
<syntaxhighlight lang="pascal">
 +
if TargetOS = 'darwin' then begin
 +
  UsageCustomOptions += ' -WM10.6';
 +
end;
 +
</syntaxhighlight>
 +
 
 +
==== FPC 2.6.1 and below ====
 +
 
 +
See the [[FPC 2.6.1 macOS Targets]] article.
 +
 
 +
=== Creating an app bundle for a GTK application ===
 +
 
 +
macOS needs an app bundle for an executable file in order to drop it on the dock or launch it by double-clicking. An app bundle is really just a special folder that can have the same name as the executable, but with an .app extension. Finder doesn't display the .app extension, although you'll see it if you use the UNIX <tt>ls</tt> command in a Terminal window to list the directory contents. For details of how to create an app bundle for a GTK application, see [[Creating an app bundle for GTK applications]].
 +
 
 +
=== Code Signing ===
  
 
Before macOS Sierra users had the option to allow the installation of applications from "anywhere", "the App Store" and "identified developers". From macOS Sierra on, the "anywhere" option was removed. Thereafter, clicking on an application to run it results in a scary dialog being presented to your users stating: "YourApp can't be opened because it is from an unidentified developer. Your security preferences allow installations of only apps from the App Store and identified developers". The only dialog option is to close it.
 
Before macOS Sierra users had the option to allow the installation of applications from "anywhere", "the App Store" and "identified developers". From macOS Sierra on, the "anywhere" option was removed. Thereafter, clicking on an application to run it results in a scary dialog being presented to your users stating: "YourApp can't be opened because it is from an unidentified developer. Your security preferences allow installations of only apps from the App Store and identified developers". The only dialog option is to close it.
Line 462: Line 567:
 
[[Code signing]] an application that has been written in Lazarus and Free Pascal requires only a few steps that are described in the article [[Code Signing for macOS]]. Note: You will need to pay Apple $US 99 per year to be an "identified developer" and code sign your applications.
 
[[Code signing]] an application that has been written in Lazarus and Free Pascal requires only a few steps that are described in the article [[Code Signing for macOS]]. Note: You will need to pay Apple $US 99 per year to be an "identified developer" and code sign your applications.
  
== Notarization ==
+
=== Notarization ===
  
 
Beginning with macOS Mojave 10.14.5 all new or updated kernel extensions and all software from developers new to distributing with a Developer ID must also be notarized in order to run or users are prevented from running the application with a very scary dialog stating: "YourApp can't be opened because Apple cannot check it for malicious software. This software needs to be updated. Contact the developer for more information."
 
Beginning with macOS Mojave 10.14.5 all new or updated kernel extensions and all software from developers new to distributing with a Developer ID must also be notarized in order to run or users are prevented from running the application with a very scary dialog stating: "YourApp can't be opened because Apple cannot check it for malicious software. This software needs to be updated. Contact the developer for more information."
Line 470: Line 575:
 
The steps to notarize an application are described in the article [[Notarization for macOS 10.14.5%2B]].
 
The steps to notarize an application are described in the article [[Notarization for macOS 10.14.5%2B]].
  
== Locale settings (date & time separator, currency symbol, ...) ==
+
== Using stdout for logging and debugging ==
 +
 
 +
=== Using a Terminal ===
 +
 
 +
Unix systems usually allow output to stdout for GUI applications (while for Windows it requires creating a console object for stdout, using file handle as stdout or linking the application as a non-GUI). The output is really useful for debugging and logging purposes.
 +
 
 +
macOS GUI applications (the ones created by Lazarus) are deployed as [http://en.wikipedia.org/wiki/Application_bundle bundles]. The bundled application conceals the output. However, it is possible to see the output generated by a simple WriteLn(), by launching the application bundle's executable from a Terminal. Here are the steps:
 +
 
 +
* Launch Terminal (found in /Applications/Utilities)
 +
* Change to the project directory
 +
* Launch the executable:
 +
  $ ./MyApp.app/Contents/MacOS/MyApp
 +
* once MyApp is running, the output will be seen in the Terminal window
 +
 
 +
[[Image:OSXStdOutExample.png|550px]]
 +
 
 +
This is especially useful when using Project Options > Compiler Options > Debugging when you have ticked the Other debugging info - Use Heaprtc unit (check for mem-leaks). The Terminal will show the report:
 +
 
 +
  Heap dump by heaptrc unit of ./MyApp
 +
  1441 memory blocks allocated : 99080/102040
 +
  1441 memory blocks freed    : 99080/102040 
 +
  0 unfreed memory blocks : 0
 +
  True heap size : 458752 (32 used in System startup)
 +
  True free heap : 458720
 +
 
 +
=== Using the Console ===
 +
 
 +
It is also possible to see the output by using the native macOS NSLog() procedure which logs your error message to the Apple System Log facility which you can read using the Console utility (located in Applications > Utilities). As there will be many message entries scrolling by, add MyApp to the search window to filter the entries being shown. For example:
  
On macOS, like on other Unix platforms, the RTL does not load the locale settings by default. They can be initialised in three ways. See the article [[Locale settings for macOS]] for details.
+
<syntaxhighlight lang="pascal">
 +
  MyAudioPlayer := AVAudioPlayer.alloc.initWithContentsOfURL_error(url, @err);
  
==Apple-specific UI elements==
+
  if Assigned(MyAudioPlayer) then
 +
      MyAudioPlayer.play
 +
  else
 +
    NSLog(NSStr('Error in procedure PlayAudio(): %@'), err);
 +
</syntaxhighlight>
  
The Apple user interface in macOS has a number of unique features (eg the Dock, Apple main menu bar, sheets, user notifications,  etc). For details on how to provide these non-cross-platform features in your applications, see the article [[Apple-specific UI elements]].
+
When the audio file is missing from the application's Resources directory, the following appears in the Console system log:
  
==Accessibility for users with special needs==
+
[[Image:NSLog_Console.png]]
  
macOS includes a wide variety of features and assistive technologies, such as screen and cursor magnification, a full-featured screen reader, visual flash alerts, closed captioning support, and much more. Take advantage of these features to make your apps accessible to users with special needs. Please refer to the [[LCL Accessibility|Accessibility]] article for more details.
+
Note that the NSlog information also appears in the Terminal if you have opened the application as explained [[#Using_a_Terminal|above]].
  
 
== Troubleshooting ==
 
== Troubleshooting ==
Line 488: Line 625:
 
* [[Cocoa Debugging|Cocoa debugging]]
 
* [[Cocoa Debugging|Cocoa debugging]]
 
* [[lldb|Debugging with the macOS lldb debugger]]
 
* [[lldb|Debugging with the macOS lldb debugger]]
 +
* [[macOS Gatekeeper Debugging Tips]]
 
* [[LazLogger|LazLogger (part of Lazarus) will log to a file]]
 
* [[LazLogger|LazLogger (part of Lazarus) will log to a file]]
 
* [[Logging exceptions]]
 
* [[Logging exceptions]]
Line 494: Line 632:
 
Seek help in the forums:
 
Seek help in the forums:
  
 +
* [[Forum|How to use the Forums effectively]]
 
* [https://forum.lazarus.freepascal.org FPC and Lazarus Forums]
 
* [https://forum.lazarus.freepascal.org FPC and Lazarus Forums]
 +
 +
Obscure native macOS error codes?
 +
 +
* [https://OSStatus.com Look up Apple API errors quickly!]
  
 
Finally, if the issue is in Free Pascal or Lazarus and not your own code:
 
Finally, if the issue is in Free Pascal or Lazarus and not your own code:
  
 
* [[How do I create a bug report]]
 
* [[How do I create a bug report]]
 +
* [[Tips on writing bug reports]]
 +
 +
== Converting legacy code ==
 +
 +
Bxxx code macro replacements:
 +
 +
* BAnd(i,j) = ((i) and (j))
 +
* BOr(i,j)  = ((i) or (j))
 +
* BXOr(i,j) = ((i) xor (j))
 +
* Bsr(i,n)  = ((i) shr (n))
 +
* Bsl(i,n)  = ((i) shl (n))
 +
* BTST(i,n) = ((((i) shr (n)) and 1)=1)
 +
* BSet(i,n) = i:=(i) or (1 shl (n))
 +
* BClr(i,n) = i:=(i) and not (1 shl (n))
 +
 +
See also: [[Porting from Mac Pascal]].
 +
 +
{{Other Interfaces}}
  
 
== See also ==
 
== See also ==
* [[Portal:Mac|Lazarus/FPC macOS Portal]]
+
* [[Portal:Mac|Lazarus/FPC macOS Portal]] - more macOS tips and code snippets
* [[Installing Lazarus on MacOS X|Installing Lazarus on macOS]]
+
* [[Installing Lazarus on macOS]]
  
==Links==
+
== External Links ==
  
 
* [https://developer.apple.com/documentation/ Apple developer documents]
 
* [https://developer.apple.com/documentation/ Apple developer documents]
  
 
* [https://macpgmr.github.io/MacXPlatform/MacXPlatform_Part8.html Making Your Lazarus App Mac-Friendly] (Apple help info outdated)
 
* [https://macpgmr.github.io/MacXPlatform/MacXPlatform_Part8.html Making Your Lazarus App Mac-Friendly] (Apple help info outdated)

Revision as of 08:26, 21 September 2020

macOSlogo.png

This article applies to macOS only.

See also: Multiplatform Programming Guide

English (en) 日本語 (ja) 한국어 (ko)

Choice of Lazarus LCL widgetsets

With macOS there are various widgetsets which can be used with Lazarus. Each has its strengths and weaknesses.

Carbon widgetset (see the Carbon Interface page)

Pros Cons
All standard LCL controls working The entire Carbon framework has been deprecated by Apple. It is not available in 64 bit macOS applications. It was completely removed from macOS 10.15 Catalina (October 2019).
No additional libraries or frameworks to install; uses Carbon framework included with macOS
Native macOS look and feel Only relevant to macOS

Cocoa widgetset (see the Cocoa Interface page)

Pros Cons
All standard LCL controls working There may still be some minor bugs lurking; please report them!
No additional libraries or frameworks to install;
uses the Cocoa framework included with macOS
The only graphical framework for macOS 10.15 Catalina and later
Necessary for graphical 64 bit applications
Native macOS look and feel Only relevant to macOS

GTK widgetset

Pros Cons
All standard LCL controls working Ugly; clunky common dialogs; doesn't look like Mac software
This widgetset is well tested Requires X11 and bulky GTK to be installed to run Lazarus or any GUI apps created with Lazarus
Have to start Lazarus at command line -- can't double-click Lazarus or place on dock (same with GUI apps) -- although see Creating an app bundle for a GTK application for how you can add this capability yourself.

Qt widgetset (see the Qt Interface Mac page)

Pros Cons
Native macOS look and feel Requires the Qt interface framework to be installed to run app, which is rather large
All standard LCL controls working
The Qt widgetset also available for other platforms, so any effort put into developing Qt widgetset benefits multiple platforms
The Qt interface provides rich functionality
The Qt widgetset is easier to develop than other widgetsets

Using Xcode for Pascal projects

For details of how to use Xcode for Pascal projects refer to this page.

Cross compiling macOS applications

See the Cross compiling article.

Commonly used Unix commands

If you’re coming to macOS from Windows, you may find some of its Unix terminal commands confusing. Here are some equivalents. For more information about a command, enter man command in an Applications > Utilities > Terminal.

Action Windows command prompt window macOS Terminal or X11 window
Change to a different directory cd cd
Make a new directory mkdir mkdir
Delete a directory rmdir rmdir
List file directory in chronological order with detail dir /od ls -ltr
Copy a file, preserving its date-time stamp copy cp -p
Display contents of a text file type cat
Delete a file erase rm
Move a file move mv
Rename a file ren mv
Find a file dir /s find
Grep a file findstr grep
Display differences between two text files fc diff
Change file attributes attrib chmod
“Super-user” root authorization N/A sudo
Create symbolic link to a file or directory mklink ln
Shrink executable file size strip (included w/ Free Pascal) strip

Useful commands and tools included with macOS

open

See the macOS Open Sesame article for the many uses of the macOS open command.

zip / unzip

These standard command-line programs are pre-installed.

Console

Drag this app from /Applications/Utilities and drop it on the dock so you always have it handy. Launch it whenever you want to see messages or errors output to the console by GUI apps (for example, when they crash). Invaluable for debugging.

Activity Monitor

This app is also in /Applications/Utilities and is useful for monitoring CPU, memory, energy, network and disk usage.

otool

Use otool to display information about an executable file or library. For example, enter the following to see information about Lazarus:

cd /usr/local/share/lazarus
otool –L lazarus

This shows that Lazarus is dependent on various libraries in /usr/lib and /System/Library/Frameworks/, as you would expect. Open a Terminal and type man otool to view its manual page.

install_name_tool

Use install_name_tool with the –change switch to change where an executable file or library looks for a library that it requires. Open a Terminal and type man install_name_tool to view its manual page.

Grab

Use Grab (in /Applications/Utilities) to create a screenshot or window shot, then save it to a disk file. Note: replaced in recent versions of macOS by CMD+SHIFT+4 for image capture and CMD+SHIFT+5 for video capture (full or part screen).

iconutil

Use iconutil in /usr/bin/ to create icon files (.icns) for use with your .app bundles. Open a Terminal and type man iconutil for its manual page.

pkgbuild, productbuild / Disk Utility

Use these apps to create disk image (.dmg) files for deploying your apps. See Deploying Your Application for more information.

Command line tools pkgbuild and productbuild are in /usr/bin/. Disk Utility is in /Applications/Utilities. You can drag and drop Disk Utility on the dock.

A popular third party option is the GUI application Packages (http://s.sudre.free.fr/Software/Packages/about.html) which creates .pkg installer archives.

Script Editor / osascript

Use Script Editor to edit, compile and run AppleScript files. It’s located in /Applications/Utilities/.

Use osascript from /usr/bin to execute an AppleScript command or file from the command line or from a script file. For more information, enter man osascript in a Terminal to view its manual page.

Cross-platform considerations

When you plan to compile the same application for different widget sets (eg Windows, GTK and Cocoa) or plan to use multiple languages, you should layout the controls on your form(s) using the anchor editor and set the AutoSize property of your controls to True. Doing this from the beginning will save you a lot of frustrating layout problems later.

Apple-specific UI elements

The Apple user interface in macOS has a number of unique features (eg the Dock, Apple main menu bar, sheets, user notifications, etc). For details on how to provide these non-cross-platform features in your applications, see the article Apple-specific UI elements.

How to obtain the path to the Bundle

 
...
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());
 
  Result := pathStr;
end;

This returns the full path up to and including the application bundle name (eg /Users/Me/MyApp.app).

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 /Applications or /Applications/Utilities directory for the application bundle. The application bundle should contain everything: libraries, dependencies, help, every file that the application needs to run except those created by the application itself. If the application bundle is copied to another machine's /Applications or /Applications/Utilities directory, it should be able to run. Installing to these folders requires Admin privileges. The data in these folders is backed up by Time Machine.
  • Use the ~/Applications directory should Admin privileges not be available. This is the standard location for a single user application. This directory should not be expected to exist. The application bundle should contain everything: libraries, dependencies, help, every file that the application needs to run except those created by the application itself. If the application bundle is copied to another machine's /Applications or /Applications/Utilities directory, it should be able to run. This data is backed up by Time Machine.
  • Use the Application Support directory (this data 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 the application Resources directory (this is backed up by Time Machine) for your application-supplied image files, sound files, icon files and other unchanging data files necessary for your application's operation.
  • 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.

Determining if a file is readable

The Lazarus function fileisreadable reports if the user has read permissions for the specified file. However, modern versions of macOS will restrict access to the following locations:

  • The app container directory. Upon first launch, the operating system creates a special directory for use by your app—and only by your app—called a container. Each user on a system gets an individual container for your app, within their home directory; your app has unfettered read/write access to the container for the user who ran it.
  • App group container directories. A sandboxed app can specify an entitlement that gives it access to one or more app group container directories, each of which is shared among all apps with that entitlement.
  • User-specified files. A sandboxed app (with an appropriate entitlement) automatically obtains access to files in arbitrary locations when those files are explicitly opened by the user or are dragged and dropped onto the application by the user.
  • Related items. With the appropriate entitlement, your app can access a file with the same name as a user-specified file, but a different extension. This can be used for accessing files that are functionally related (such as a subtitle file associated with a movie) or for saving modified files in a different format (such as re-saving an RTF flat file as an RTFD container after the user added a picture).
  • Temporary directories, command-line tool directories, and specific world-readable locations. A sandboxed app has varying degrees of access to files in certain other well-defined locations.

At the moment, there appears to be no reliable API to determine if a file is readable in all situations. Apple's own isReadableFileAtPath method is essentially a wrapper around the BSD access system call and does not take into account the various security and privacy features that Apple has added since macOS 10.0. Below, is a brute force method that determines if a file is readable by physically reading the first byte of data from the file. This is necessarily slower than simply reading the file permissions, but does provide a robust solution:

function FileIsReadableByThisExecutable(const AFilename: string): boolean;
// macOS will block applications from reading files outside their sandbox
// therefore, simply knowing the user has read acces is not sufficient
// requires "uses LazFileUtils"
var
  f: file;
  b: byte;
begin
  Result := lazfileutils.FileIsReadable(AFilename);
  {$IFDEF Darwin}
  if not Result then exit; //globally not readable
  if FileSizeUtf8(AFilename) < 1 then exit(false);
  AssignFile(f, AFilename);
  {$I+}
  try
    FileMode := fmOpenRead;  //Set file access to read only
    Reset(f, 1);
    if ioresult <> 0 then
       exit;
    b := 0;
    BlockRead(f, b, sizeof(b));
    CloseFile(f);
    result := true;
  except
    result := false;
  end;
  {$ENDIF}
end;

Accessing system information

Sysctl provides an interface that allows you to retrieve many kernel parameters in macOS (and Linux and the BSDs). This provides a wealth of information detailing system hardware which can be useful when debugging user issues or simply optimising your application (eg to take advantage of threads on multi-core CPU systems). The NSProcessInfo class provides access to a collection of information about the current process. The IOKit framework implements non-kernel access to I/O Kit objects (eg to retrieve the Mac platform serial number and UUID). For full details and example code, see the article Accessing macOS System Information.

Retrieve username/ full username

The code to retrieve the current user's username (eg joe) or full username (eg Joe Bloggs) can be found in the article macOS Retrieve (full)username.

Making a beep

Procedure declaration:

procedure NSBeep; cdecl;
  external '/System/Library/Frameworks/AppKit.framework/AppKit' name 'NSBeep';

To use it:

procedure MakeBeep;
  begin
    {$IFDEF DARWIN}
    NSbeep;
    {$ENDIF}

    [...]
  end;

You can also assign NSBeep to the OnBeep (TBeepHandler in the SysUtils unit), by setting it on Form creation.

This way you can call the regular Beep function (by default, Beep contains no implementation to actually produce a beep).

procedure TForm1.FormCreate(Sender: TObject);
  begin
    {$IFDEF DARWIN}
    OnBeep := @NSbeep;
    {$ENDIF}

    [...]
  end;

Show application in all desktop spaces

An application defaults to showing only in the desktop space in which it was opened. To show your application in all desktop spaces for macOS 10.5+ (Leopard):

{$modeswitch objectivec1}

Uses CocoaAll ...

procedure TForm1.MenuItem35Click(Sender: TObject);
var
  theWindow: NSWindow;
begin
  theWindow := NSView(Form1.Handle).window;       // Form1 is the name of the main form - adjust as necessary
  theWindow.setCollectionBehavior(theWindow.collectionBehavior or NSWindowCollectionBehaviorCanJoinAllSpaces);
end;

Launch/Toggle full screen

To toggle an application window full screen for macOS 10.7+ (Lion):

{$modeswitch objectivec1}

Uses CocoaAll ...

procedure TForm1.Button1Click(Sender: TObject);
var
  Window: NSWindow;
begin
  Window := NSView(Form1.Handle).window;
  Window.toggleFullScreen(Nil);
end;

To launch your application full screen you can call the button event handler (as below) or move the button event handler code (above) to the form's on activate event handler:

procedure TForm1.FormActivate(Sender: TObject);
begin
  Form1.Button1Click(Form1);
end;

Locale settings (date & time separator, currency symbol, ...)

On macOS, like on other Unix platforms, the RTL does not load the locale settings by default. They can be initialised in three ways. See the article Locale settings for macOS for details.

Accessibility for users with special needs

macOS includes a wide variety of features and assistive technologies, such as screen and cursor magnification, a full-featured screen reader, visual flash alerts, closed captioning support, and much more. Take advantage of these features to make your apps accessible to users with special needs. Please refer to the Accessibility article for more details.

LCL FontDialog workaround

The FontDialog.OnApplyClicked event handler is not supported in macOS. So this code will have no effect (the message will not be shown):

procedure TForm1.Button1Click(Sender: TObject);
begin
  FontDialog1.Execute;
end;
 
procedure TForm1.FontDialog1ApplyClicked(Sender: TObject);
begin
  ShowMessage('Apply Clicked');
end;

The workaround is to restructure the code as follows:

procedure TForm1.Button1Click(Sender: TObject);
begin
  if FontDialog1.Execute then
    begin
      ShowMessage('Apply Clicked');
    end;
end;

Using PrintDialog in Cocoa

PrintDialog is only partially implemented (July 2020) in Cocoa. As a result, this code does not display the dialog:

procedure TForm1.btnPrintDialogClick(Sender: TObject);
begin
  if PrintDialog1.Execute then
    Panel1.Invalidate;
end;

This code will display the dialog:

procedure TForm1.btnPrintDialogClick(Sender: TObject);
begin
  PrintDialog1.Options:= PrintDialog1.Options + [poBeforeBeginDoc];
  if PrintDialog1.Execute then
    Panel1.Invalidate;
end;

Detecting the Apple Command key

The Apple Command key is ssMeta. Heres' how to test for it being depressed:

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
var
  ssCmd: TShiftStateEnum;
begin
  // Define standard menu command key as ssCmd
  {$IFDEF DARWIN}
    ssCmd := ssMeta; // Apple
  {$ELSE}
    ssCmd := ssCtrl  // FreeBSD, Linux, Windows
  {$ENDIF}

  if (ssCmd in Shift) then
    Caption:='Command key pressed'
  else
    Caption := IntToStr(Key) + ' not the command key';
end;

Libraries

Using a library in a Mac application

Many software projects use external libraries to complement their functionality. For example, OpenAL might be utilized to provide sound support, or FreeType might be utilized to provide font rendering support for FCL-Image. macOS already comes with a number of open source libraries installed, being that OpenAL is one of them. If one of these libraries is utilized, one should simply add the corresponding import unit to the uses clause and start using the library. To use other open source libraries which aren't included with macOS, the easiest way is by grabbing a pre-compiled library from the application bundle of popular open source applications. FreeType for example is included inside the application bundle from Gimp in the path Gimp.app/Resources/lib/libfreetype.6.dylib. Another option is downloading the source code from the library and building it manually, but compiling a C library is usually an unpleasant task for Pascal developers.

So, again in the previous example, let's say that an application requires the FreeType library. One can grab the libfreetype.6.dylib file from Gimp and then place it in a location of our source code tree, for example ../Mac/libfreetype.dylib relative to the path of the main project file, which is where the executable is placed. To properly link the application the Free Pascal command line option "-Fl../Mac" should be added, so that this path is added to the library search path. The linker will then find the library in this location and read the install path which is written inside it. At runtime the linker will search for the library in this install path. To change the install path to our desired one, the command line application install_name_tool, which comes with the Apple Developer Tools, can be used. It can be utilized with the following command line command:

install_name_tool libfreetype.dylib -id @executable_path/../Frameworks/libfreetype.dylib

One can then verify if this command worked by using the command:

otool -D libfreetype.dylib

It should return the written path. When building the application bundle, we should copy the libfreetype.dylib file to the Frameworks sub-directory. The install_name_tool command uses the @executable_path macro to set an install path relative to the executable, which is inside the MacOS folder in the application bundle. Now the application can be built, the bundle and then the application can be run and it will load the library correctly from the application bundle.

Using a library installed using fink

For using libraries, which were installed using fink, you need to add the path to the libraries. The default is /sw/lib. Adding the option -Fl/sw/lib to the command line tells the linker where to find the libraries. The resulting application is fine for distribution with fink, but in order to run without fink, you have to make a "standalone" version. There are several possibilities to achieve this, one of them will be outlined here: First copy the .dylib file into the application bundle, for example to Contents/MacOS/ next to the executable binary. Then, adjust the paths install_name_tool:

First, change the path of the library in the executable:

install_name_tool  -change /sw/lib/libfreetype.dylib @executable_path/libfreetype.dylib @executable_path/your_executable_binary

Second, adjust the path in the library

install_name_tool  -id @executable_path/libfreetype.dylib @executable_path/libfreetype.dylib
install_name_tool  -change /sw/lib/libfreetype.dylib @executable_path/libfreetype.dylib @executable_path/libfreetype.dylib

The complete list of libraries in your executable can be obtained with:

otool -L @executable_path/your_executable_binary | grep version | cut -f 1 -d ' '

For the list of libraries from fink only, without libraries from /usr/lib and /System/Library, use this command:

otool -L @executable_path/your_executable_binary | grep version | cut -f 1 -d ' ' | grep -v \/System\/Library | grep -v \/usr\/lib

With this a script can be created, which loops over all libraries from fink in the executable. Be aware of the fact that the libraries can call libraries from fink. You have to adjust them as well.

Another important point of libraries from fink is, that they have only one architecture, ie i386, ppc, aarch64, x86_64 or ppc64. For a universal application, you have to combine single architecture libraries with lipo.

Dynamic Libraries

For the details of creating and using dynamic libraries, see macOS Dynamic Libraries.

Static Libraries

For the details of creating and using static libraries, see macOS Static Libraries.

Which dynamic libraries are used?

A little known method for checking which dynamic libraries an executable uses is as simple as setting an environment variable. The macOS dynamic linker checks the following environment variables during the launch of each process.

Environment Variable Description
DYLD_PRINT_TO_FILE This is a path to a (writable) file. Normally, the dynamic linker writes all logging output (triggered by DYLD_PRINT_* settings) to file descriptor 2 (usually stderr). This setting causes the dynamic linker to write logging output to the specified file.
DYLD_PRINT_LIBRARIES When this is set, the dynamic linker writes to file descriptor 2 (normally stderr) the filenames of the libraries the program is using. This is useful to make sure that your program is loading the dynamic libraries you want.
DYLD_PRINT_RPATHS Causes dyld to print a line each time it expands an @rpath variable and whether that expansion was successful or not.

For full details of many other environment variables and their effect, see the manual page for dyld (type man dyld in a Terminal).

Application deployment/distribution

Creating Universal binaries

Lazarus will create an application tuned for a single CPU. With the Carbon Interface version of the LCL, this is either PowerPC or Intel i386 (32 bit). You may want to distribute your application as a 'Universal Binary', allowing users that may have either a PowerPC or Intel computer to use the program. To do this, you need to compile two versions of your application: one with PowerPC as the target CPU and one as Intel. Next, you need to stitch these two executables together using the macOS program "lipo" (installed with the other Xcode developer tools required by Lazarus). For example, consider two versions of the application 'myprogram'. One in the 'ppc' folder compiled for PowerPC and one in the 'intel' folder compiled for Intel CPUs. You can then combine these two to a univeral application by running this command:

 lipo -create ./ppc/myproj ./intel/myproj -output ./myproj

Now, copy the newly created application myproj inside your .app folder.

A note on 64 bit binaries: Depending on the version of macOS and after corresponding (cross-)compilers are installed, programs which do not use the LCL can also be built as ppc64 or x86_64 binaries, in particular command line utilities. lipo is used in the same way to put them into universal binaries.

Executable size optimisation

The normal way to optimise the size of your executable with Lazarus would be:

1. Project > Project Options > Compilation and Linking

  • Check Optimisation level 3 (-O3)
  • Check Smaller rather than faster (-Os)
  • Check Smart Linkable (-CX)
  • Check Link smart (-XX)

2. Project > Project Options > Debugging

  • Uncheck all except Strip Symbols From Executable (-Xs)

An example (Xcode 11.3; macOS Mojave 10.14.6):

  • MyApp compiled with Lazarus defaults (-O1) - executable size 12,011,600 bytes.
  • MyApp compiled with (-O3) - executable size - executable size 12,011,600 bytes.
  • MyApp compiled with (-O3) and (-Os) - executable size 12,011,592 bytes.
  • MyApp compiled with (-O3), (-Os) and (-CX) - executable size 12,011,592 bytes.
  • MyApp compiled with (-O3), (-Os), (-CX) and (-XX) - executable size 4,624,076 bytes!
  • MyApp compiled with (-O3), (-Os), (-CX), (-XX) and (-Xs) - executable size 4,624,076 bytes.

Problem: Whoa! Stripping symbols from the executable should have made a difference. The reason it did not make any difference is that Apple removed the ability to strip the symbol table from an executable in Xcode 3.1 (2007 Leopard 10.5). Previously, in Xcode 2.5 (2005 Tiger 10.4), the linker could strip the symbol table from 32 bit executables. So, providing the (-Xs) option to the linker has no effect at all.

Solution: The strip command line utility will strip the symbol table. Manually running strip MyApp in a Terminal resulted in the executable size further reducing from 4,624,076 bytes to 2,922,396 bytes.

Deploying an application for an older version of the operating system

When compiling an application on without any special options, the application is only guaranteed to work on that particular major operating system release and later (eg when compiling under Snow Leopard 10.6.8, the application is only guaranteed to work on Snow Leopard 10.6.8 and later).

Here is an example of an error that you may encounter when running an application compiled for Leopard 10.5 under Tiger 10.4 in case you use the widestring manager:

dyld: Library not loaded: /usr/lib/libiconv.2.dylib
  Referenced from: /Volumes/..../yourprogram
  Reason: Incompatible library version: yourprogram requires version 7.0.0 or later, but libiconv.2.dylib provides version 5.0.0
Trace/BPT trap

Here is an example of an error that you may encounter when running an application compiled for Snow Leopard 10.6.8 under Snow Leopard 10.6.7:

Runtime error 203

FPC 2.6.2 and above

See the article Support for specifying and querying the deployment version which is current up to and including FPC 3.04.

For FPC 3.2 (aka fixes branch) see the article Default target macOS version.

Adding custom options only when compiling for macOS

To add the above custom options only when compiling for macOS add to Project / Compiler options / IDE macros / Conditionals:

if TargetOS = 'darwin' then begin
  UsageCustomOptions += ' -WM10.6';
end;

FPC 2.6.1 and below

See the FPC 2.6.1 macOS Targets article.

Creating an app bundle for a GTK application

macOS needs an app bundle for an executable file in order to drop it on the dock or launch it by double-clicking. An app bundle is really just a special folder that can have the same name as the executable, but with an .app extension. Finder doesn't display the .app extension, although you'll see it if you use the UNIX ls command in a Terminal window to list the directory contents. For details of how to create an app bundle for a GTK application, see Creating an app bundle for GTK applications.

Code Signing

Before macOS Sierra users had the option to allow the installation of applications from "anywhere", "the App Store" and "identified developers". From macOS Sierra on, the "anywhere" option was removed. Thereafter, clicking on an application to run it results in a scary dialog being presented to your users stating: "YourApp can't be opened because it is from an unidentified developer. Your security preferences allow installations of only apps from the App Store and identified developers". The only dialog option is to close it.

Users can still run your application, but they have to know they can right-click or control-click on your application and then they get another scary dialog stating "YourApp is from an unidentified developer. Are you sure you want to open it?" but this time the dialog has an "open" button and a pre-focussed "cancel" button.

Code signing an application that has been written in Lazarus and Free Pascal requires only a few steps that are described in the article Code Signing for macOS. Note: You will need to pay Apple $US 99 per year to be an "identified developer" and code sign your applications.

Notarization

Beginning with macOS Mojave 10.14.5 all new or updated kernel extensions and all software from developers new to distributing with a Developer ID must also be notarized in order to run or users are prevented from running the application with a very scary dialog stating: "YourApp can't be opened because Apple cannot check it for malicious software. This software needs to be updated. Contact the developer for more information."

Beginning with macOS 10.15 Catalina (released in October 2019), notarization is required by default for all software. If your application is not notarised, your users get the very scary dialog stating: "YourApp can't be opened because Apple cannot check it for malicious software. This software needs to be updated. Contact the developer for more information." This is in addition to requiring the application to be code signed.

The steps to notarize an application are described in the article Notarization for macOS 10.14.5+.

Using stdout for logging and debugging

Using a Terminal

Unix systems usually allow output to stdout for GUI applications (while for Windows it requires creating a console object for stdout, using file handle as stdout or linking the application as a non-GUI). The output is really useful for debugging and logging purposes.

macOS GUI applications (the ones created by Lazarus) are deployed as bundles. The bundled application conceals the output. However, it is possible to see the output generated by a simple WriteLn(), by launching the application bundle's executable from a Terminal. Here are the steps:

  • Launch Terminal (found in /Applications/Utilities)
  • Change to the project directory
  • Launch the executable:
 $ ./MyApp.app/Contents/MacOS/MyApp
  • once MyApp is running, the output will be seen in the Terminal window

OSXStdOutExample.png

This is especially useful when using Project Options > Compiler Options > Debugging when you have ticked the Other debugging info - Use Heaprtc unit (check for mem-leaks). The Terminal will show the report:

  Heap dump by heaptrc unit of ./MyApp
  1441 memory blocks allocated : 99080/102040
  1441 memory blocks freed     : 99080/102040   
  0 unfreed memory blocks : 0
  True heap size : 458752 (32 used in System startup)
  True free heap : 458720

Using the Console

It is also possible to see the output by using the native macOS NSLog() procedure which logs your error message to the Apple System Log facility which you can read using the Console utility (located in Applications > Utilities). As there will be many message entries scrolling by, add MyApp to the search window to filter the entries being shown. For example:

  MyAudioPlayer := AVAudioPlayer.alloc.initWithContentsOfURL_error(url, @err);

  if Assigned(MyAudioPlayer) then
      MyAudioPlayer.play
  else
    NSLog(NSStr('Error in procedure PlayAudio(): %@'), err);

When the audio file is missing from the application's Resources directory, the following appears in the Console system log:

NSLog Console.png

Note that the NSlog information also appears in the Terminal if you have opened the application as explained above.

Troubleshooting

The following articles are relevant for troubleshooting application issues:

Seek help in the forums:

Obscure native macOS error codes?

Finally, if the issue is in Free Pascal or Lazarus and not your own code:

Converting legacy code

Bxxx code macro replacements:

  • BAnd(i,j) = ((i) and (j))
  • BOr(i,j) = ((i) or (j))
  • BXOr(i,j) = ((i) xor (j))
  • Bsr(i,n) = ((i) shr (n))
  • Bsl(i,n) = ((i) shl (n))
  • BTST(i,n) = ((((i) shr (n)) and 1)=1)
  • BSet(i,n) = i:=(i) or (1 shl (n))
  • BClr(i,n) = i:=(i) and not (1 shl (n))

See also: Porting from Mac Pascal.

Other Interfaces

Platform specific Tips

Interface Development Articles

See also

External Links