Sign in Join
Wiki copytemporary

macOS Programming Tips: what changed

Revision 3 to 4. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11*(macOS only)*
22
33## Choice of Lazarus LCL widgetsets
44
55With macOS there are various widgetsets which can be used with Lazarus. Each has its strengths and weaknesses.
66
77**Carbon widgetset** (see the [[Carbon Interface]] page)
88
99| Pros | Cons |
1010|----|----|
1111| 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). The Carbon widgetset is no longer being developed. |
1212| No additional libraries or frameworks to install; uses Carbon framework included with macOS | |
1313| Native macOS look and feel | Only relevant to macOS |
1414
1515**Cocoa widgetset** (see the [[Cocoa Interface]] page)
1616
17-<thead>
18-
19-</thead>
20-<tbody>
21-
22-uses the Cocoa framework included with macOS</td>
23-
24-</tbody>
17+| Pros | Cons |
18+|---|---|
19+| All standard LCL controls working | There may still be some minor bugs lurking; please report them! |
20+| No additional libraries or frameworks to install; · uses the Cocoa framework included with macOS | |
21+| The only graphical framework for macOS 10.15 Catalina and later | |
22+| Necessary for graphical 64 bit applications | |
23+| Native macOS look and feel | Only relevant to macOS |
2524
2625**GTK widgetset**
2726
2827| Pros | Cons |
2928|----|----|
3029| All standard LCL controls working | Ugly; clunky common dialogs; doesn't look like Mac software |
3130| This widgetset is well tested | Requires X11 and bulky GTK to be installed to run Lazarus or any GUI apps created with Lazarus |
3231| | 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. |
3332
3433**Qt widgetset** (see the [[Qt Interface Mac]] page)
3534
3635| Pros | Cons |
3736|----|----|
3837| Native macOS look and feel | Requires the Qt interface framework to be installed to run app, which is rather large |
3938| All standard LCL controls working | |
4039| The Qt widgetset also available for other platforms, so any effort put into developing Qt widgetset benefits multiple platforms | |
4140| The Qt interface provides rich functionality | |
4241| The Qt widgetset is easier to develop than other widgetsets | |
4342
4443## Cross compiling macOS applications
4544
4645See the [[Cross compiling]] article.
4746
4847## Commonly used Unix commands
4948
5049If 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.
5150
5251| Action | Windows command prompt window | macOS Terminal or X11 window |
5352|----|----|----|
5453| Change to a different directory | cd | cd |
5554| Make a new directory | mkdir | mkdir |
5655| Delete a directory | rmdir | rmdir |
5756| List file directory in chronological order with detail | dir /od | ls -ltr |
5857| Copy a file, preserving its date-time stamp | copy | cp -p |
5958| Display contents of a text file | type | cat |
6059| Delete a file | erase | rm |
6160| Move a file | move | mv |
6261| Rename a file | ren | mv |
6362| Find a file | dir /s | find |
6463| Grep a file | findstr | grep |
6564| Display differences between two text files | fc | diff |
6665| Change file attributes | attrib | chmod |
6766| “Super-user” root authorization | N/A | sudo |
6867| Create symbolic link to a file or directory | mklink | ln |
6968| Shrink executable file size | strip (included w/ Free Pascal) | strip |
7069
7170## Useful commands and tools included with macOS
7271
7372**open**
7473
7574See the [[macOS Open Sesame]] article for the many and varied uses of the macOS `open` command.
7675
7776**zip / unzip**
7877
7978These are standard command-line zip archive compression/uncompression utilities.
8079
8180**Console**
8281
8382Drag the *Console* application 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.
8483
8584**Activity Monitor**
8685
8786The *Activity Monitor* application is also in `/Applications/Utilities` and is useful for monitoring CPU, memory, energy, network and disk usage.
8887
8988**otool**
9089
9190Use the *otool* command line utility to display information about an executable file or library. For example, enter the following to see information about Lazarus:
9291
9392``` bash
9493cd /usr/local/share/lazarus
9594otool –L lazarus
9695```
9796
9897This 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.
9998
10099**install_name_tool**
101100
102101Use *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.
103102
104103**iconutil**
105104
106105Use the *iconutil* command line utility 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.
107106
108107**pkgbuild, productbuild / Disk Utility**
109108
110109Use these utilities to create disk image (.dmg) files for deploying your applications. See [[Deploying Your Application]] for more information.
111110
112111Command 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.
113112
114113A popular third party option is the GUI application [Packages](http://s.sudre.free.fr/Software/Packages/about.html) which creates .pkg installer archives.
115114
116115**PlistBuddy**
117116
118117The command line utility `PlistBuddy`, which is found in `/usr/libexec/`, may be used to read and modify the values inside of [[macOS property list files|property list files]].
119118
120119**Script Editor / osascript**
121120
122121Use the *Script Editor* application to edit, compile and run AppleScript files. It’s located in `/Applications/Utilities/`.
123122
124123Use the command line utility *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.
125124
126125**latency**
127126
128127The *latency* diagnostic command line utility found in `/usr/bin/` monitors scheduling and interrupt latency measuring the number of context switches and interrupts, and reporting on the resulting delays. Open a Terminal and type `man latency` to view its manual page.
129128
130129**fs_usage**
131130
132131The *fs_usage* diagnostic command line utility reports system calls and page faults related to filesystem activity in real-time. The output displays the timestamp, system call, filename, elapsed time and name of the process. Open a Terminal and type `man fs_usage` to view its manual page.
133132
134133**Safari**
135134
136135Yes, I know, it is a web browser - but it is more. You can read manual pages with it too. So, rather than opening a Terminal and then typing `man 3 pause`, you can simply type `x-man-page://3/pause` into Safari's URL field and access the man page that way. Unfortunately recent version of Safari now ask you if you want to allow Safari to open the Terminal and this new security behaviour cannot be turned off and your choice is not remembered.
137136
138137**sc_usage**
139138
140139The *sc_usage* command line utility samples system calls and page faults, displaying them onscreen. Open a Terminal and type `man sc_usage` to view its manual page.
141140
142141**vm_stat**
143142
144143The *vm_stat* command line utility displays virtual memory statistics. Open a Terminal and type `man vm_stat` to view its manual page.
145144
146145**DevToolsSecurity**
147146
148147The *DevToolsSecurity* command line utility changes the security authorisation policies for use of the Apple-code-signed debugger and performance analysis tools on development systems. See further [[lldb|DevToolsSecurity]].
149148
150149## Cross-platform considerations
151150
152151When 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.
153152
154153## Apple-specific UI elements
155154
156155The Apple user interface in macOS has a number of unique features (eg the Dock, Apple main menu bar, sheets, drawers, user notifications, etc). For details on how to provide these non-cross-platform features in your applications, see the article [[Apple-specific UI elements]].
157156
158157## Setting environment variables for an application
159158
160159On macOS, the normal way to set any environment variables needed by an application is to set them in the LSEnvironment property of your application's [[macOS property list files|Info.plist]] file.
161160
162161## How to obtain the path to the Bundle
163162
164163``` pascal
165164
166165{$mode objfpc}{$H+}
167166{$modeswitch objectivec1}
168167
169168...
170169
171170uses
172171 CocoaAll; // for NSBundle
173172
174173...
175174
176175function GetBundlePath: string;
177176begin
178177 Result := NSBundle.mainBundle.bundlePath.UTF8String;
179178end;
180179```
181180
182181This returns the full path up to and including the application bundle name (eg `/Users/Me/MyApp.app`).
183182
184183Related: [[Locating the macOS application resources directory]].
185184
186185## Determining where to store files
187186
188187## Determining if a file is readable
189188
190189The Lazarus function [fileisreadable](https://lazarus-ccr.sourceforge.io/docs/lazutils/lazfileutils/fileisreadable.html) reports if the user has read permissions for the specified file. However, modern versions of macOS will restrict [access to the following locations](https://developer.apple.com/library/archive/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html):
191190
192191- 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.
193192- 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.
194193- 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.
195194- 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).
196195- 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.
197196
198197At 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:
199198
200199``` pascal
201200function FileIsReadableByThisExecutable(const AFilename: string): boolean;
202201// macOS will block applications from reading files outside their sandbox
203202// therefore, simply knowing the user has read acces is not sufficient
204203// requires "uses LazFileUtils"
205204var
206205 f: file;
207206 b: byte;
208207begin
209208 Result := lazfileutils.FileIsReadable(AFilename);
210209 {$IFDEF Darwin}
211210 if not Result then exit; //globally not readable
212211 if FileSizeUtf8(AFilename) < 1 then exit(false);
213212 AssignFile(f, AFilename);
214213 {$I+}
215214 try
216215 FileMode := fmOpenRead; //Set file access to read only
217216 Reset(f, 1);
218217 if ioresult <> 0 then
219218 exit;
220219 b := 0;
221220 BlockRead(f, b, sizeof(b));
222221 CloseFile(f);
223222 result := true;
224223 except
225224 result := false;
226225 end;
227226 {$ENDIF}
228227end;
229228```
230229
231230Many system frameworks that provide access to [protected resources](https://developer.apple.com/documentation/bundleresources/information_property_list/protected_resources) have dedicated APIs for checking and requesting authorization to use those resources. This allows you to adjust your application’s behaviour depending on the current access it has. For example, if the user denies your application permission to do something, you can remove related elements from your user interface. Because a user can change authorization at any time using Settings, always check the authorization status of a feature before accessing it. In cases without a dedicated API, be prepared to gracefully handle access failures as above.
232231
233232For an example of an authorization API, see [AVCaptureDevice API](https://developer.apple.com/documentation/avfoundation/avcapturedevice/1624613-authorizationstatus).
234233
235234## Accessing system information
236235
237236Sysctl 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]].
238237
239238## Retrieve username/ full username
240239
241240The 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]].
242241
243242## Making a beep
244243
245244Procedure declaration:
246245
247246``` pascal
248247procedure NSBeep; cdecl;
249248 external '/System/Library/Frameworks/AppKit.framework/AppKit' name 'NSBeep';
250249```
251250
252251To use it:
253252
254253``` pascal
255254procedure MakeBeep;
256255 begin
257256 {$IFDEF DARWIN}
258257 NSbeep;
259258 {$ENDIF}
260259
261260 [...]
262261 end;
263262```
264263
265264You can also assign NSBeep to the [OnBeep](https://www.freepascal.org/docs-html/rtl/sysutils/onbeep.html) (TBeepHandler in the SysUtils unit), by setting it on Form creation.
266265
267266This way you can call the regular [Beep](https://www.freepascal.org/docs-html/rtl/sysutils/beep.html) function (by default, Beep contains no implementation to actually produce a beep).
268267
269268``` pascal
270269procedure TForm1.FormCreate(Sender: TObject);
271270 begin
272271 {$IFDEF DARWIN}
273272 OnBeep := @NSbeep;
274273 {$ENDIF}
275274
276275 [...]
277276 end;
278277```
279278
280279## Show application in all desktop spaces
281280
282281An 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):
283282
284283``` pascal
285284{$modeswitch objectivec1}
286285
287286Uses CocoaAll ...
288287
289288procedure TForm1.MenuItem35Click(Sender: TObject);
290289var
291290 theWindow: NSWindow;
292291begin
293292 theWindow := NSView(Form1.Handle).window; // Form1 is the name of the main form - adjust as necessary
294293 theWindow.setCollectionBehavior(theWindow.collectionBehavior or NSWindowCollectionBehaviorCanJoinAllSpaces);
295294end;
296295```
297296
298297## Launch/Toggle full screen
299298
300299To toggle an application window full screen for macOS 10.7+ (Lion):
301300
302301``` pascal
303302{$modeswitch objectivec1}
304303
305304Uses CocoaAll ...
306305
307306procedure TForm1.Button1Click(Sender: TObject);
308307var
309308 Window: NSWindow;
310309begin
311310 Window := NSView(Form1.Handle).window;
312311 Window.toggleFullScreen(Nil);
313312end;
314313```
315314
316315To 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:
317316
318317``` pascal
319318procedure TForm1.FormActivate(Sender: TObject);
320319begin
321320 Form1.Button1Click(Form1);
322321end;
323322```
324323
325324## Locale settings (date & time separator, currency symbol, ...)
326325
327326On 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.
328327
329328## Accessibility for users with special needs
330329
331330macOS 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.
332331
333332## LCL FontDialog workaround
334333
335334The FontDialog.OnApplyClicked event handler is not supported in macOS. So this code will have no effect (the message will not be shown):
336335
337336``` pascal
338337procedure TForm1.Button1Click(Sender: TObject);
339338begin
340339 FontDialog1.Execute;
341340end;
342341
343342procedure TForm1.FontDialog1ApplyClicked(Sender: TObject);
344343begin
345344 ShowMessage('Apply Clicked');
346345end;
347346```
348347
349348The workaround is to restructure the code as follows:
350349
351350``` pascal
352351procedure TForm1.Button1Click(Sender: TObject);
353352begin
354353 if FontDialog1.Execute then
355354 begin
356355 ShowMessage('Apply Clicked');
357356 end;
358357end;
359358```
360359
361360## Coping with missing features across macOS versions
362361
363362For an example of how to cope elegantly with new features in macOS which are missing from older macOS versions, see [[Accessing macOS System Information|this example]].
364363
365364## Updating a GUI from a secondary thread
366365
367366Apple’s UI frameworks, and Cocoa in particular, can only be called from the main thread. This means that if you have a background threading process, it cannot interact with the user interface because all user interface objects are marked as not safe for threading. Touching the user interface from a background thread can cause all sorts of issues, including corruption of internal state, crashes, or inexplicable incorrect behaviour.
368367
369368For example code implementing a solution to this issue, refer to the [[macOS NSURLSession|Downloading files with graphical user interface feedback]] article.
370369
371370## Using PrintDialog in Cocoa
372371
373372PrintDialog is only partially implemented (July 2020) in Cocoa. As a result, this code does not display the dialog:
374373
375374``` pascal
376375procedure TForm1.btnPrintDialogClick(Sender: TObject);
377376begin
378377 if PrintDialog1.Execute then
379378 Panel1.Invalidate;
380379end;
381380```
382381
383382This code will display the dialog:
384383
385384``` pascal
386385procedure TForm1.btnPrintDialogClick(Sender: TObject);
387386begin
388387 PrintDialog1.Options:= PrintDialog1.Options + [poBeforeBeginDoc];
389388 if PrintDialog1.Execute then
390389 Panel1.Invalidate;
391390end;
392391```
393392
394393## Detecting the Apple Command key
395394
396395The Apple Command key is *ssMeta*. Heres' how to test for it being depressed:
397396
398397``` pascal
399398procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
400399var
401400 ssCmd: TShiftStateEnum;
402401begin
403402 // Define standard menu command key as ssCmd
404403 {$IFDEF DARWIN}
405404 ssCmd := ssMeta; // Apple
406405 {$ELSE}
407406 ssCmd := ssCtrl // FreeBSD, Linux, Windows
408407 {$ENDIF}
409408
410409 if (ssCmd in Shift) then
411410 Caption:='Command key pressed'
412411 else
413412 Caption := IntToStr(Key) + ' not the command key';
414413end;
415414```
416415
417416## Updating the GUI from a non-main thread
418417
419418Any attempt to provide user feedback to the graphical user interface from within a delegate method running on a secondary background thread will cause the application to die with a log entry similar to this:
420419
421420```text
422421 *** Terminating app due to uncaught exception 'NSInternalInconsistencyException',
423422 reason: 'NSWindow drag regions should only be invalidated on the Main Thread!'
424423```
425424
426425The cause? Apple’s UI frameworks, and Cocoa in particular, can only be called from the main thread. Touching the user interface from a background thread can cause all sorts of issues, including corruption of internal state, crashes, or inexplicable incorrect behaviour.
427426
428427One solution may be found in the [[macOS NSURLSession]] article which also has a code example.
429428
430429## Libraries
431430
432431### Using a library in a Mac application
433432
434433Many 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 pure Pascal developers.
435434
436435So, 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:
437436
438437``` bash
439438install_name_tool libfreetype.dylib -id @executable_path/../Frameworks/libfreetype.dylib
440439```
441440
442441One can then verify if this command worked by using the command:
443442
444443``` bash
445444otool -D libfreetype.dylib
446445```
447446
448447It 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.
449448
450449### Using a library installed using fink
451450
452451For 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:
453452
454453- Copy the .dylib file into the application bundle, for example to the `Contents/MacOS/` next to the executable binary. Then, adjust the paths install_name_tool:
455454
456455- Change the path of the library in the executable:
457456
458457``` bash
459458install_name_tool -change /sw/lib/libfreetype.dylib @executable_path/libfreetype.dylib @executable_path/your_executable_binary
460459```
461460
462461- Adjust the path in the library:
463462
464463``` bash
465464install_name_tool -id @executable_path/libfreetype.dylib @executable_path/libfreetype.dylib
466465install_name_tool -change /sw/lib/libfreetype.dylib @executable_path/libfreetype.dylib @executable_path/libfreetype.dylib
467466```
468467
469468The complete list of libraries in your executable can be obtained with:
470469
471470``` bash
472471otool -L @executable_path/your_executable_binary | grep version | cut -f 1 -d ' '
473472```
474473
475474For the list of libraries from fink only, without libraries from `/usr/lib` and `/System/Library`, use this command:
476475
477476``` bash
478477otool -L @executable_path/your_executable_binary | grep version | cut -f 1 -d ' ' | grep -v \/System\/Library | grep -v \/usr\/lib
479478```
480479
481480With 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.
482481
483482Another 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`.
484483
485484### Dynamic Libraries
486485
487486For the details of creating and using dynamic libraries, see [[macOS Dynamic Libraries]] and [[macOS Frameworks]].
488487
489488### Static Libraries
490489
491490For the details of creating and using static libraries, see [[macOS Static Libraries]].
492491
493492### Which dynamic libraries are used?
494493
495494A 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.
496495
497496| Environment Variable | Description |
498497|----|----|
499498| 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. |
500499| 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. |
501500| DYLD_PRINT_RPATHS | Causes dyld to print a line each time it expands an @rpath variable and whether that expansion was successful or not. |
502501
503502For full details of many other environment variables and their effect, see the manual page for dyld (type `man dyld` in a Terminal).
504503
505504### OpenSSL, LibreSSL, Secure Transport, Network Framework
506505
507506Apple deprecated the use of the included OpenSSL libraries in Mac OS X 10.7 (Lion) in July 2011 and suggested that developers should either include the needed libraries in their application bundle or use the [Secure Transport API](https://developer.apple.com/documentation/security/secure_transport) which itself has now been deprecated in favour of the [Network Framework](https://developer.apple.com/documentation/network) introduced in macOS 10.14 (Mojave). Use of the Apple-provided OpenSSL and LibreSSL libraries by applications is strongly discouraged by Apple (see [this statement](https://developer.apple.com/library/archive/documentation/Security/Conceptual/cryptoservices/SecureNetworkCommunicationAPIs/SecureNetworkCommunicationAPIs.html#//apple_ref/doc/uid/TP40011172-CH13-SW3)).
508507
509508In the FPC 3.0.4 release, when trying to load the unversioned `libcrypto.dylib` or `libssl.dylib`, macOS terminates the application with the error: "Invalid dylib load. Clients should not load the unversioned libssl dylib as it does not have a stable ABI.". The fix for that bug (see [Bug \#36484](https://bugs.freepascal.org/view.php?id=36484)) was included in FPC 3.2.0 which caused FPC to fall back to using LibreSSL v2.2 (`libssl.35.dylib`) from June 2015.
510509
511510As of the FPC 3.2.0 release, FPC breaks the ability of macOS to connect to web sites using HTTPS with TLSv1.2 or TLSv1.3 protocols only, because it fails to load the most recent Apple included LibreSSL libraries. It uses the very old LibreSSL v2.2 because the two libraries (`libssl.35.dylib` and `libcrypto.35.dylib`) for that version have matching version numbers (like OpenSSL) whereas the more recent LibreSSL library versions have mismatching version numbers.
512511
513512The issue has been fixed in FPC trunk (see [Bug \#37977](https://bugs.freepascal.org/view.php?id=37977)) and is included in FPC 3.2.2. However, it still uses the outdated Apple-included system libraries listed in the table below.
514513
515-<thead>
516-
517-</thead>
518-<tbody>
519-
520-libssl.0.9.8.dylib + libcrypto.0.9.8.dylib</td>
521-
522-libssl.0.9.8.dylib + libcrypto.0.9.8.dylib</td>
523-
524-libssl.0.9.8.dylib + libcrypto.0.9.8.dylib</td>
525-
526-libssl.39.dylib + libcrypto.38.dylib</td>
527-
528-libssl.0.9.8.dylib + libcrypto.0.9.8.dylib</td>
529-
530-libssl.39.dylib + libcrypto.38.dylib<br />
531-libssl.43.dylib + libcrypto.41.dylib</td>
532-
533-libssl.0.9.8.dylib + libcrypto.0.9.8.dylib</td>
534-
535-libssl.43.dylib + libcrypto.41.dylib<br />
536-libssl.44.dylib + libcrypto.42.dylib</td>
537-
538-libssl.0.9.8.dylib + libcrypto.0.9.8.dylib</td>
539-
540-libssl.43.dylib + libcrypto.41.dylib<br />
541-libssl.44.dylib + libcrypto.42.dylib<br />
542-libssl.46.dylib + libcrypto.44.dylib</td>
543-
544-libssl.0.9.8.dylib + libcrypto.0.9.8.dylib</td>
545-
546-libssl.43.dylib + libcrypto.41.dylib<br />
547-libssl.44.dylib + libcrypto.42.dylib<br />
548-libssl.46.dylib + libcrypto.44.dylib</td>
549-
550-libssl.0.9.8.dylib + libcrypto.0.9.8.dylib</td>
551-
552-libssl.43.dylib + libcrypto.41.dylib<br />
553-libssl.44.dylib + libcrypto.42.dylib<br />
554-libssl.46.dylib + libcrypto.44.dylib<br />
555-libssl.48.dylib + libcrypto.46.dylib</td>
556-
557-</tbody>
514+| macOS version | OpenSSL libraries | LibreSSL libraries |
515+|---|---|---|
516+| 10.8, 10.9, 10.10 | libssl.0.9.7.dylib + libcrypto.0.9.7.dylib · libssl.0.9.8.dylib + libcrypto.0.9.8.dylib | - - |
517+| 10.11 | libssl.0.9.7.dylib + libcrypto.0.9.7.dylib · libssl.0.9.8.dylib + libcrypto.0.9.8.dylib | libssl.35.dylib + libcrypto.35.dylib |
518+| 10.12 | libssl.0.9.7.dylib + libcrypto.0.9.7.dylib · libssl.0.9.8.dylib + libcrypto.0.9.8.dylib | libssl.35.dylib + libcrypto.35.dylib · libssl.39.dylib + libcrypto.38.dylib |
519+| 10.13 | libssl.0.9.7.dylib + libcrypto.0.9.7.dylib · libssl.0.9.8.dylib + libcrypto.0.9.8.dylib | libssl.35.dylib + libcrypto.35.dylib · libssl.39.dylib + libcrypto.38.dylib · libssl.43.dylib + libcrypto.41.dylib |
520+| 10.14 | libssl.0.9.7.dylib + libcrypto.0.9.7.dylib · libssl.0.9.8.dylib + libcrypto.0.9.8.dylib | libssl.35.dylib + libcrypto.35.dylib · libssl.43.dylib + libcrypto.41.dylib · libssl.44.dylib + libcrypto.42.dylib |
521+| 10.15 | libssl.0.9.7.dylib + libcrypto.0.9.7.dylib · libssl.0.9.8.dylib + libcrypto.0.9.8.dylib | libssl.35.dylib + libcrypto.35.dylib · libssl.43.dylib + libcrypto.41.dylib · libssl.44.dylib + libcrypto.42.dylib · libssl.46.dylib + libcrypto.44.dylib |
522+| 11 | libssl.0.9.7.dylib + libcrypto.0.9.7.dylib · libssl.0.9.8.dylib + libcrypto.0.9.8.dylib | libssl.35.dylib + libcrypto.35.dylib · libssl.43.dylib + libcrypto.41.dylib · libssl.44.dylib + libcrypto.42.dylib · libssl.46.dylib + libcrypto.44.dylib |
523+| 12 | libssl.0.9.7.dylib + libcrypto.0.9.7.dylib · libssl.0.9.8.dylib + libcrypto.0.9.8.dylib | libssl.35.dylib + libcrypto.35.dylib · libssl.43.dylib + libcrypto.41.dylib · libssl.44.dylib + libcrypto.42.dylib · libssl.46.dylib + libcrypto.44.dylib · libssl.48.dylib + libcrypto.46.dylib |
558524
559525All the more reason to **not** use the macOS supplied library versions and either include your own up-to-date library versions or use the Apple Network Framework when you need direct access to protocols like TLS, TCP, and UDP for your custom application protocols. For details of including your own dynamic libraries in FPC or Lazarus applications, refer to [[macOS Dynamic Libraries]].
560526
561527You can continue to use the [[macOS NSURLSession|NSURLSession API]], which is built on the Apple Network Framework, for loading HTTPS-based and URL-based resources. This does not use the OpenSSL or LibreSSL system-included libraries.
562528
563529#### OpenSSL versions
564530
565531As at September 2023, the latest OpenSSL stable version is the 3.0 series which is supported until 7th September 2026. This is also a Long Term Support (LTS) version. Note that OpenSSL 3.0+ has significant ABI changes.
566532
567533The previous LTS version OpenSSL 1.1.1 was on life support until 11th September 2023 at which time all support ceased (see [OpenSSL 1.1.1 End of Life](https://www.openssl.org/blog/blog/2023/09/11/eol-111/)), so no more bug fixes for security problems.
568534
569535All older OpenSSL versions (including 1.1.0, 1.0.2, 1.0.0, 0.9.8 and 0.9.7) have been out of support for much longer, contain multiple security vulnerabilities and should not be used if you value security and your reputation.
570536
571537## Application deployment/distribution
572538
573539### Creating Universal binaries
574540
575541#### aarch64 or Intel x86_64
576542
577543Lazarus will create an application tuned for a single CPU. With the [[Cocoa Interface]] version of the [[LCL]], this is either Apple Silicon aarch64 or Intel x86_64. You may want to distribute your application as a 'Universal Binary', allowing users that may have either an Apple Silicon ARM64 processor or Intel processor to use the application.
578544
579545For the fun details details of creating universal binaries for these two processor architectures, see [[macOS Big Sur changes for developers|Creating a universal binary for aarch64 and x86_64]].
580546
581547#### PowerPC or Intel i386
582548
583549Lazarus 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 application. 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 into a universal application by running this command:
584550
585551```text
586552 lipo -create ./ppc/myproj ./intel/myproj -output ./myproj
587553```
588554
589555Now, copy the newly created application myproj to the MacOS folder inside your [[Application Bundle]].
590556
591557A 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.
592558
593559### Executable size optimisation
594560
595561The normal way to optimise the size of your executable with Lazarus would be:
596562
5975631\. Project \> Project Options \> Compilation and Linking
598564
599565- Check Optimisation level 3 (-O3)
600566- Check Smaller rather than faster (-Os)
601567- Check Smart Linkable (-CX)
602568- Check Link smart (-XX)
603569
6045702\. Project \> Project Options \> Debugging
605571
606572- Uncheck all except Strip Symbols From Executable (-Xs)
607573
608574An example (Xcode 11.3; macOS Mojave 10.14.6):
609575
610576- MyApp compiled with Lazarus defaults (-O1) - executable size 12,011,600 bytes.
611577- MyApp compiled with (-O3) - executable size - executable size 12,011,600 bytes.
612578- MyApp compiled with (-O3) and (-Os) - executable size 12,011,592 bytes.
613579- MyApp compiled with (-O3), (-Os) and (-CX) - executable size 12,011,592 bytes.
614580- MyApp compiled with (-O3), (-Os), (-CX) and (-XX) - executable size 4,624,076 bytes!
615581- MyApp compiled with (-O3), (-Os), (-CX), (-XX) and (-Xs) - executable size 4,624,076 bytes.
616582
617583**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.
618584
619585**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.
620586
621587### Deploying an application for an older version of the operating system
622588
623589When compiling an application on a specific macOS release 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).
624590
625591Here 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:
626592
627593 dyld: Library not loaded: /usr/lib/libiconv.2.dylib
628594 Referenced from: /Volumes/..../yourprogram
629595 Reason: Incompatible library version: yourprogram requires version 7.0.0 or later, but libiconv.2.dylib provides version 5.0.0
630596 Trace/BPT trap
631597
632598Here 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:
633599
634600```text
635601Runtime error 203
636602```
637603
638604#### FPC 2.6.2 and above
639605
640606See the article [[FPC New Features 2.6.2|Support for specifying and querying the deployment version]] which is current up to and including FPC 3.04.
641607
642608For FPC 3.2 (aka fixes branch) see the article [[User Changes 3.2|Default target macOS version]].
643609
644610**Adding custom options only when compiling for macOS**
645611
646612To add the above custom options only when compiling for macOS add to Project / Compiler options / IDE macros / Conditionals:
647613
648614``` pascal
649615if TargetOS = 'darwin' then begin
650616 UsageCustomOptions += ' -WM10.6';
651617end;
652618```
653619
654620#### FPC 2.6.1 and below
655621
656622See the [[FPC 2.6.1 macOS Targets]] article.
657623
658624### Creating an app bundle for a GTK application
659625
660626macOS 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]].
661627
662628### Code Signing
663629
664630Before 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.
665631
666632Users 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.
667633
668634[[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.
669635
670636### Notarization
671637
672638Beginning 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."
673639
674640Beginning 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.
675641
676642The steps to notarize an application are described in the article [[Notarization for macOS 10.14.5+|Notarization for macOS 10.14.5%2B]].
677643
678644### Deploying your application
679645
680646See [[Deploying Your Application|macOS Deployment]].
681647
682648## Using stdout and stderr for logging and debugging
683649
684650### Using a Terminal
685651
686652Unix systems usually allow output to `stdout` and `stderr` for GUI applications (while for Windows it requires creating a console object, using the file handle as `stdout/stderr` or linking the application as a non-GUI). The output is really useful for debugging and logging purposes.
687653
688654macOS GUI applications (the ones created by Lazarus) are deployed as [bundles](http://en.wikipedia.org/wiki/Application_bundle). The bundled application conceals the output. However, it is possible to see the output generated by a simple `WriteLn('Debug: Test')` or `WriteLn(stderr, 'Debug: error alert!');`, by launching the application bundle's executable from a Terminal. Here are the steps:
689655
690656- Launch Terminal (found in Applications \> Utilities)
691657- Change to the project directory
692658- Launch the executable:
693659
694660```text
695661 $ ./MyApp.app/Contents/MacOS/MyApp
696662```
697663
698664- once MyApp is running, the output will be seen in the Terminal window.
699665
700666![](/wiki-images/0/0b/OSXStdOutExample.png)
701667
702668This 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:
703669
704670``` pascal
705671 Heap dump by heaptrc unit of ./MyApp
706672 1441 memory blocks allocated : 99080/102040
707673 1441 memory blocks freed : 99080/102040
708674 0 unfreed memory blocks : 0
709675 True heap size : 458752 (32 used in System startup)
710676 True free heap : 458720
711677```
712678
713679### Using the Console
714680
715681It 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. You can read the System Log in realtime using the Console utility (located in Applications \> Utilities). As there will be many message entries scrolling by, add MyApp as a process to the search window to filter the entries being shown. For example:
716682
717683``` pascal
718684 MyAudioPlayer := AVAudioPlayer.alloc.initWithContentsOfURL_error(url, @err);
719685
720686 if Assigned(MyAudioPlayer) then
721687 MyAudioPlayer.play
722688 else
723689 NSLog(NSStr('Error in procedure PlayAudio(): %@'), err);
724690```
725691
726692When the audio file is missing from the application's Resources directory, the following appears in the Console system log:
727693
728694![](/wiki-images/1/15/NSLog_Console.png)
729695
730696Note that the NSlog information also appears in the Terminal if you have opened the application as explained above.
731697
732698## Troubleshooting
733699
734700The following articles are relevant for troubleshooting application issues:
735701
736702- [[Cocoa Debugging|Cocoa debugging]]
737703- [[lldb|Debugging with the macOS lldb debugger]]
738704- [[macOS Gatekeeper Debugging Tips]]
739705- [[LazLogger|LazLogger (part of Lazarus) will log to a file]]
740706- [[Logging exceptions]]
741707- [[heaptrc|Memory leak checking]]
742708
743709Seek help in the forums:
744710
745711- [[Forum|How to use the Forums effectively]]
746712- [FPC and Lazarus Forums](https://forum.lazarus.freepascal.org)
747713
748714Obscure native macOS error codes?
749715
750716- [Look up Apple API errors quickly!](https://OSStatus.com)
751717
752718Finally, if the issue is in Free Pascal or Lazarus and not your own code:
753719
754720- [[How do I create a bug report]]
755721- [[Tips on writing bug reports]]
756722
757723## Converting legacy Pascal code
758724
759725Bxxx code macro replacements:
760726
761727- BAnd(i,j) = ((i) and (j))
762728- BOr(i,j) = ((i) or (j))
763729- BXOr(i,j) = ((i) xor (j))
764730- Bsr(i,n) = ((i) shr (n))
765731- Bsl(i,n) = ((i) shl (n))
766732- BTST(i,n) = ((((i) shr (n)) and 1)=1)
767733- BSet(i,n) = i:=(i) or (1 shl (n))
768734- BClr(i,n) = i:=(i) and not (1 shl (n))
769735
770736See also: [[Porting from Mac Pascal]].
771737
772738## See also
773739
774740- [[Portal:Mac|Lazarus/FPC macOS Portal]] - more macOS tips and code snippets
775741
776742- [[Installing Lazarus on macOS]]
777743
778744## External Links
779745
780746- [Apple developer documents](https://developer.apple.com/documentation/) - current and up to date
781747
782748- [Apple archived developer documents](https://developer.apple.com/library/archive/navigation/) - dated, but often more helpful
783749
784750- [Making Your Lazarus App Mac-Friendly](https://macpgmr.github.io/MacXPlatform/MacXPlatform_Part8.html) (Apple help info outdated)
785751
786752- Using Xcode for Pascal projects, refer to [this page](https://macpgmr.github.io/ObjP/ProjectXC.html).
787753
788754---
789755*From the Free Pascal wiki, page "macOS Programming Tips", as it stood on 2023-09-13 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](/wiki-contact).*

Back to history