macOS Find Default Application: what changed
Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.
Title: macOS Find Default Application
| Old | New | Line | |
|---|---|---|---|
| 1 | + | *(macOS only)* | |
| 2 | + | ||
| 3 | + | ## Overview | |
| 4 | + | ||
| 5 | + | From time to time your application may need to determine the user's default application for opening certain content - for example, a web URL or a system file. | |
| 6 | + | ||
| 7 | + | Example code which demonstrates how to do this is set out below. Note that these are obviously not the only two content for which you may wish to determine the user's default application, but the same general approach will apply for those other content types. | |
| 8 | + | ||
| 9 | + | The alternative example code below demonstrates how to do this for various URL schemes (eg mailto:, http:, https:, sms:, tel:, etc). | |
| 10 | + | ||
| 11 | + | ## Example code | |
| 12 | + | ||
| 13 | + | ``` pascal | |
| 14 | + | unit Unit1; | |
| 15 | + | ||
| 16 | + | {$mode objfpc}{$H+} | |
| 17 | + | {$modeswitch objectivec2} | |
| 18 | + | ||
| 19 | + | interface | |
| 20 | + | ||
| 21 | + | uses | |
| 22 | + | Classes, | |
| 23 | + | Forms, | |
| 24 | + | Dialogs, // needed for ShowMessage() | |
| 25 | + | StdCtrls, // needed for buttons | |
| 26 | + | CocoaAll, // needed for Cocoa framework (origin the NeXTSTEP libraries) | |
| 27 | + | CocoaUtils; // needed for NSStringToString() | |
| 28 | + | ||
| 29 | + | type | |
| 30 | + | ||
| 31 | + | { TForm1 } | |
| 32 | + | ||
| 33 | + | TForm1 = class(TForm) | |
| 34 | + | Button1: TButton; | |
| 35 | + | Button2: TButton; | |
| 36 | + | procedure Button1Click(Sender: TObject); | |
| 37 | + | procedure Button2Click(Sender: TObject); | |
| 38 | + | private | |
| 39 | + | ||
| 40 | + | public | |
| 41 | + | ||
| 42 | + | end; | |
| 43 | + | ||
| 44 | + | var | |
| 45 | + | Form1: TForm1; | |
| 46 | + | ||
| 47 | + | implementation | |
| 48 | + | ||
| 49 | + | {$R *.lfm} | |
| 50 | + | ||
| 51 | + | { TForm1 } | |
| 52 | + | ||
| 53 | + | procedure TForm1.Button1Click(Sender: TObject); | |
| 54 | + | var | |
| 55 | + | contentUrl: NSUrl; | |
| 56 | + | defAppUrl: NSUrl; | |
| 57 | + | defAppName: String; | |
| 58 | + | begin | |
| 59 | + | contentUrl := NSUrl.URLWithString(NSSTR('https://sentinel.sentry.org/')); | |
| 60 | + | defAppUrl := NSWorkspace.sharedWorkspace.URLForApplicationToOpenURL(contentUrl); | |
| 61 | + | ||
| 62 | + | // Bundle ID (required) | |
| 63 | + | ShowMessage(NSStringToString(NSBundle.bundleWithURL(defAppUrl).bundleIdentifier)); | |
| 64 | + | ||
| 65 | + | // Bundle Display Name (optional) | |
| 66 | + | defAppName := NSStringToString(NSBundle.bundleWithURL(defAppUrl).objectForInfoDictionaryKey(NSStr('CFBundleDisplayName').description)); | |
| 67 | + | // Bundle Name (conventionally required) | |
| 68 | + | if (defAppName = '') then | |
| 69 | + | defAppName := NSStringToString(NSBundle.bundleWithURL(defAppUrl).objectForInfoDictionaryKey(NSStr('CFBundleName').description)); | |
| 70 | + | // Cannot fail :-) | |
| 71 | + | if (defAppName = '') then | |
| 72 | + | defAppName := NSStringToString(defAppUrl.URLByDeletingPathExtension.lastPathComponent); | |
| 73 | + | ||
| 74 | + | // Show result | |
| 75 | + | ShowMessage(defAppName); | |
| 76 | + | end; | |
| 77 | + | ||
| 78 | + | procedure TForm1.Button2Click(Sender: TObject); | |
| 79 | + | var | |
| 80 | + | contentUrl: NSUrl; | |
| 81 | + | defAppUrl: NSUrl; | |
| 82 | + | defAppName: String; | |
| 83 | + | begin | |
| 84 | + | contentUrl := NSUrl.URLWithString(NSSTR('file:///Users/trev/my_defaults.txt')); | |
| 85 | + | defAppUrl := NSWorkspace.sharedWorkspace.URLForApplicationToOpenURL(contentUrl); | |
| 86 | + | ||
| 87 | + | // Bundle ID (required) | |
| 88 | + | ShowMessage(NSStringToString(NSBundle.bundleWithURL(defAppUrl).bundleIdentifier)); | |
| 89 | + | ||
| 90 | + | // Bundle Display Name (optional) | |
| 91 | + | defAppName := NSStringToString(NSBundle.bundleWithURL(defAppUrl).objectForInfoDictionaryKey(NSStr('CFBundleDisplayName').description)); | |
| 92 | + | // Bundle Name (conventionally required) | |
| 93 | + | if (defAppName = '') then | |
| 94 | + | defAppName := NSStringToString(NSBundle.bundleWithURL(defAppUrl).objectForInfoDictionaryKey(NSStr('CFBundleName').description)); | |
| 95 | + | // Cannot fail :-) | |
| 96 | + | if (defAppName = '') then | |
| 97 | + | defAppName := NSStringToString(defAppUrl.URLByDeletingPathExtension.lastPathComponent); | |
| 98 | + | ||
| 99 | + | // Show result | |
| 100 | + | ShowMessage(defAppName); | |
| 101 | + | end; | |
| 102 | + | ||
| 103 | + | end. | |
| 104 | + | ``` | |
| 105 | + | ||
| 106 | + | Full project source code is available from [SourceForge](https://sourceforge.net/p/lazarus-wiki-projects/code/ci/master/tree/laz_default_app/). | |
| 107 | + | ||
| 108 | + | ## Alternative example code for URL schemes | |
| 109 | + | ||
| 110 | + | Note that *LSGetApplicationForURL* was deprecated in macOS 10.10 (Yosemite) in favour of *LSCopyDefaultApplicationURLForURL* which itself was deprecated in macOS 12 (Monterey), both functions are still working in macOS 12.0.1. There is no example code for *LSCopyDefaultApplicationURLForURL* because the function is missing from the FPC `packages/univint/src/LSInfo.pas` file. | |
| 111 | + | ||
| 112 | + | ``` pascal | |
| 113 | + | unit Unit1; | |
| 114 | + | ||
| 115 | + | {$mode objfpc}{$H+} | |
| 116 | + | {$modeswitch objectivec1} | |
| 117 | + | ||
| 118 | + | interface | |
| 119 | + | ||
| 120 | + | uses | |
| 121 | + | SysUtils, Forms, StdCtrls, | |
| 122 | + | MacOSAll, CocoaAll, CocoaUtils; | |
| 123 | + | ||
| 124 | + | type | |
| 125 | + | ||
| 126 | + | { TForm1 } | |
| 127 | + | ||
| 128 | + | TForm1 = class(TForm) | |
| 129 | + | Button1: TButton; | |
| 130 | + | Memo1: TMemo; | |
| 131 | + | procedure Button1Click(Sender: TObject); | |
| 132 | + | private | |
| 133 | + | ||
| 134 | + | public | |
| 135 | + | ||
| 136 | + | end; | |
| 137 | + | ||
| 138 | + | var | |
| 139 | + | Form1: TForm1; | |
| 140 | + | ||
| 141 | + | implementation | |
| 142 | + | ||
| 143 | + | {$R *.lfm} | |
| 144 | + | ||
| 145 | + | { TForm1 } | |
| 146 | + | ||
| 147 | + | procedure TForm1.Button1Click(Sender: TObject); | |
| 148 | + | var | |
| 149 | + | myURL: NSURL; | |
| 150 | + | URLRef: CFURLRef; | |
| 151 | + | OSStatus: Integer; | |
| 152 | + | myStr: String; | |
| 153 | + | begin | |
| 154 | + | // Create URL scheme from one of: | |
| 155 | + | // file:/// | |
| 156 | + | // file://localhost/ | |
| 157 | + | // mailto: | |
| 158 | + | // http: | |
| 159 | + | // https: | |
| 160 | + | // ftp: | |
| 161 | + | // sms: | |
| 162 | + | // tel: | |
| 163 | + | // facetime: | |
| 164 | + | // ... There are probably more. | |
| 165 | + | myURL := NSURL.alloc.initWithString(NSStr('mailto:')); | |
| 166 | + | ||
| 167 | + | // Check for default pplication | |
| 168 | + | OSStatus := LSGetApplicationForURL(CFURLRef(myURL), kLSRolesAll, Nil, @URLRef); | |
| 169 | + | ||
| 170 | + | // Check for error (eg application not found for URL = -10814) | |
| 171 | + | if(OSStatus <> 0) then | |
| 172 | + | begin | |
| 173 | + | Memo1.Append('LSGetApplicationForURL error status: ' + IntToStr(OSStatus)); | |
| 174 | + | Exit; | |
| 175 | + | end; | |
| 176 | + | ||
| 177 | + | // Get CFString from CFURLRef, Cast to NSString, Convert to AnsiString | |
| 178 | + | myStr := NSStringToString(NSString(CFURLGetString(URLRef))); | |
| 179 | + | ||
| 180 | + | // Output to memo | |
| 181 | + | memo1.Append('LSGetApplicationForURL status: ' + IntToStr(OSStatus) | |
| 182 | + | + LineEnding + LineEnding + 'Default app: ' + myStr); | |
| 183 | + | ||
| 184 | + | // Housekeeping | |
| 185 | + | myURL.release; | |
| 186 | + | end; | |
| 187 | + | ||
| 188 | + | end. | |
| 189 | + | ``` | |
| 190 | + | ||
| 191 | + | Full project source code is available from [SourceForge](https://sourceforge.net/p/lazarus-wiki-projects/code/ci/master/tree/laz_default_app2/). | |
| 192 | + | ||
| 193 | + | ## See also | |
| 194 | + | ||
| 195 | + | - [[Webbrowser|Find the default web browser]] - cross-platform example. | |
| 196 | + | ||
| 197 | + | ## External links | |
| 198 | + | ||
| 199 | + | - [Apple: NSWorkspace](https://developer.apple.com/documentation/appkit/nsworkspace) - available from macOS 10.0 | |
| 200 | + | - [Apple: NSURL](https://developer.apple.com/documentation/foundation/nsurl) - available from macOS 10.0 | |
| 201 | + | - [Apple: CFURL, CFURLRef](https://developer.apple.com/documentation/corefoundation/cfurl?language=objc) - available from macOS 10.0 | |
| 202 | + | - [Apple: LSGetApplicationForURL](https://developer.apple.com/documentation/coreservices/1445210-lsgetapplicationforurl) - available from macOS 10.0 - deprecated in macOS 10.10 | |
| 203 | + | - [Apple: LSCopyDefaultApplicationURLForURL](https://developer.apple.com/documentation/coreservices/1448824-lscopydefaultapplicationurlforur) - available from macOS 10.10 - deprecated in macOS 12 | |
| 204 | + | ||
| 205 | + | --- | |
| 206 | + | *From the Free Pascal wiki, page "macOS Find Default Application", as it stood on 2021-12-08 after a change by Alextp, 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).* | |
| 207 | + | Category: Code Snippets | |
| 208 | + | Category: Lazarus | |
| 209 | + | Category: macOS |
