如何将您的Cocoa应用程序设置为默认Web浏览器?

38

如何将你的Cocoa应用设置为默认的网页浏览器?

我想创建一个应用程序,当用户在其他应用程序中(如Mail、iChat等)点击HTTP或HTTPS链接时,默认启动该应用程序。


https://github.com/Lord-Kamina/SwiftDefaultApps#usage-notes 可以帮助你,它允许你从其 GUI 中重新关联 URI 方案处理程序到不同的应用程序。 - Jaime Hablutzel
4个回答

81
制作一个可作为默认网页浏览器的应用程序需要四个步骤。前三个步骤允许您的应用程序作为相关URL scheme(HTTP和HTTPS)的角色处理程序,而最后一步则会使其成为这些scheme的默认角色处理程序。
1) 将应用程序可以处理的URL Scheme添加到应用程序的info.plist文件中
要添加对http://https://的支持,您需要将以下内容添加到应用程序的info.plist文件中。这告诉操作系统您的应用程序能够处理HTTP和HTTPS URL。
<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>http URL</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>http</string>
        </array>
    </dict>
    <dict>
        <key>CFBundleURLName</key>
        <string>Secure http URL</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>https</string>
        </array>
    </dict>
</array>

2) 编写URL处理程序方法

当操作系统想要使用您的应用程序打开URL时,此方法将被调用。无论您向哪个对象添加此方法,都会在下一步中明确传递给事件管理器。URL处理程序方法应该类似于以下内容:

- (void)getUrl:(NSAppleEventDescriptor *)event 
    withReplyEvent:(NSAppleEventDescriptor *)replyEvent
{
  // Get the URL
  NSString *urlStr = [[event paramDescriptorForKeyword:keyDirectObject] 
    stringValue];

  //TODO: Your custom URL handling code here
}

3) 注册URL处理程序方法

接下来,告诉事件管理器在使用您的应用程序加载URL时调用哪个对象和方法。在这里的代码中,我将self作为事件处理程序传递,假设我们从定义getUrl:withReplyEvent:方法的同一对象中调用setEventHandler

您应该在您的应用程序初始化代码的某个地方添加此代码。

NSAppleEventManager *em = [NSAppleEventManager sharedAppleEventManager];
[em 
  setEventHandler:self 
  andSelector:@selector(getUrl:withReplyEvent:) 
  forEventClass:kInternetEventClass 
  andEventID:kAEGetURL];

一些应用程序,包括早期版本的Adobe AIR,使用替代的WWW!/OURL AppleEvent来请求应用程序打开网址,为了与这些应用程序兼容,您还应该添加以下内容:

[em
  setEventHandler:self 
  andSelector:@selector(getUrl:withReplyEvent:) 
  forEventClass:'WWW!' 
  andEventID:'OURL'];

4) 将你的应用设置为默认浏览器

到目前为止,我们已经告诉操作系统你的应用程序是一个浏览器,现在我们需要将其设置为默认浏览器

我们需要使用启动服务 API 来完成此操作。在这种情况下,我们将我们的应用程序设置为处理 HTTP 和 HTTPS 链接的默认角色处理程序:

CFStringRef bundleID = (CFStringRef)[[NSBundle mainBundle] bundleIdentifier];
OSStatus httpResult = LSSetDefaultHandlerForURLScheme(CFSTR("http"), bundleID);
OSStatus httpsResult = LSSetDefaultHandlerForURLScheme(CFSTR("https"), bundleID);
//TODO: Check httpResult and httpsResult for errors

在更改用户的默认浏览器之前最好征得用户的许可。

自定义 URL 方案

值得注意的是,您也可以使用同样的步骤处理自己的自定义 URL 方案。如果您正在创建一个自定义 URL 方案,建议基于您应用程序的 Bundle Identifier 进行命名,以避免与其他应用程序冲突。因此,如果您的 Bundle ID 是 com.example.MyApp,您应该考虑使用 x-com-example-myapp:// URLs。


1
启动服务是CoreServices的一部分,而不是Carbon。(因此,它在64位过渡中得以存活。) - Peter Hosey
1
值得注意的是,在上述的第3步中,事件处理程序的注册必须发生在init(或类似方法)内部。如果放置在applicationDidFinishLaunching:中,将不会起作用。 - tomwhipple
1
不需要创建一个 NSString 字面量,然后将其转换为 CFStringRef,例如 (CFStringRef)@"http"。相反,直接创建一个 CFString 字面量:CFSTR("http") - Jeremy W. Sherman
你能发布一个示例项目的下载链接吗?我不太清楚在哪里粘贴所有代码片段。另外,有没有想过将其作为Cocoa-AppleScript应用程序实现? - Geoffrey Booth
1
作为一款邮件应用程序,有没有什么额外/不同的事情需要做来注册应用程序? - RJVB
显示剩余3条评论

4

macOS Big Sur及以上版本

将此代码复制并粘贴至您的Info.plist文件中

<key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleURLName</key>
            <string>Web site URL</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>http</string>
                <string>https</string>
            </array>
        </dict>
        <dict>
            <key>CFBundleURLName</key>
            <string>http URL</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>http</string>
            </array>
        </dict>
        <dict>
            <key>CFBundleURLName</key>
            <string>Secure http URL</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>https</string>
            </array>
        </dict>
        <dict>
            <key>CFBundleTypeName</key>
            <string>HTML document</string>
            <key>CFBundleTypeRole</key>
            <string>Viewer</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>public.html</string>
            </array>
        </dict>
        <dict>
            <key>CFBundleTypeName</key>
            <string>XHTML document</string>
            <key>CFBundleTypeRole</key>
            <string>Viewer</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>public.xhtml</string>
            </array>
        </dict>
    </array>
    <key>CFBundleDocumentTypes</key>
    <array>
        <dict>
            <key>CFBundleTypeIconFile</key>
            <string>document.icns</string>
            <key>CFBundleTypeName</key>
            <string>GIF image</string>
            <key>CFBundleTypeRole</key>
            <string>Viewer</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>com.compuserve.gif</string>
            </array>
        </dict>
        <dict>
            <key>CFBundleTypeIconFile</key>
            <string>document.icns</string>
            <key>CFBundleTypeName</key>
            <string>HTML document</string>
            <key>CFBundleTypeRole</key>
            <string>Viewer</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>public.html</string>
            </array>
        </dict>
        <dict>
            <key>CFBundleTypeIconFile</key>
            <string>document.icns</string>
            <key>CFBundleTypeName</key>
            <string>XHTML document</string>
            <key>CFBundleTypeRole</key>
            <string>Viewer</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>public.xhtml</string>
            </array>
        </dict>
        <dict>
            <key>CFBundleTypeIconFile</key>
            <string>document.icns</string>
            <key>CFBundleTypeName</key>
            <string>JavaScript script</string>
            <key>CFBundleTypeRole</key>
            <string>Viewer</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>com.netscape.javascript-​source</string>
            </array>
        </dict>
        <dict>
            <key>CFBundleTypeIconFile</key>
            <string>document.icns</string>
            <key>CFBundleTypeName</key>
            <string>JPEG image</string>
            <key>CFBundleTypeRole</key>
            <string>Viewer</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>public.jpeg</string>
            </array>
        </dict>
        <dict>
            <key>CFBundleTypeIconFile</key>
            <string>document.icns</string>
            <key>CFBundleTypeName</key>
            <string>MHTML document</string>
            <key>CFBundleTypeRole</key>
            <string>Viewer</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>org.ietf.mhtml</string>
            </array>
        </dict>
        <dict>
            <key>CFBundleTypeIconFile</key>
            <string>document.icns</string>
            <key>CFBundleTypeName</key>
            <string>HTML5 Audio (Ogg)</string>
            <key>CFBundleTypeRole</key>
            <string>Viewer</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>org.xiph.ogg-audio</string>
            </array>
        </dict>
        <dict>
            <key>CFBundleTypeIconFile</key>
            <string>document.icns</string>
            <key>CFBundleTypeName</key>
            <string>HTML5 Video (Ogg)</string>
            <key>CFBundleTypeRole</key>
            <string>Viewer</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>org.xiph.ogv</string>
            </array>
        </dict>
        <dict>
            <key>CFBundleTypeIconFile</key>
            <string>document.icns</string>
            <key>CFBundleTypeName</key>
            <string>PNG image</string>
            <key>CFBundleTypeRole</key>
            <string>Viewer</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>public.png</string>
            </array>
        </dict>
        <dict>
            <key>CFBundleTypeIconFile</key>
            <string>document.icns</string>
            <key>CFBundleTypeName</key>
            <string>SVG document</string>
            <key>CFBundleTypeRole</key>
            <string>Viewer</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>public.svg-image</string>
            </array>
        </dict>
        <dict>
            <key>CFBundleTypeIconFile</key>
            <string>document.icns</string>
            <key>CFBundleTypeName</key>
            <string>Plain text document</string>
            <key>CFBundleTypeRole</key>
            <string>Viewer</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>public.text</string>
            </array>
        </dict>
        <dict>
            <key>CFBundleTypeIconFile</key>
            <string>document.icns</string>
            <key>CFBundleTypeName</key>
            <string>HTML5 Video (WebM)</string>
            <key>CFBundleTypeRole</key>
            <string>Viewer</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>org.webmproject.webm</string>
            </array>
        </dict>
        <dict>
            <key>CFBundleTypeIconFile</key>
            <string>document.icns</string>
            <key>CFBundleTypeName</key>
            <string>WebP image</string>
            <key>CFBundleTypeRole</key>
            <string>Viewer</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>org.webmproject.webp</string>
            </array>
        </dict>
        <dict>
            <key>CFBundleTypeRole</key>
            <string>Viewer</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>org.chromium.extension</string>
            </array>
        </dict>
        <dict>
            <key>CFBundleTypeIconFile</key>
            <string>document.icns</string>
            <key>CFBundleTypeName</key>
            <string>PDF Document</string>
            <key>CFBundleTypeRole</key>
            <string>Viewer</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>com.adobe.pdf</string>
            </array>
        </dict>
    </array>

您的应用将会显示在系统偏好设置中,并成为默认浏览器

确保您完成以下步骤

    func application(_ application: NSApplication, open urls: [URL]) {
// do a for loop, I recommend it
}

我已经做了这个,但似乎在后续的安装中它并没有改变。只有在第一次安装时才会改变。比如说,如果用户手动更改了默认应用程序设置,然后我们再次安装该应用程序,它不会仅针对那些扩展名更改默认应用程序 :( - Skywalker

2

如果您只想更改http(s)的默认助手应用程序,可以在Safari首选项中进行更改。在那里,您将找到一个下拉菜单,可以让您选择所有已注册的http处理程序应用程序。要自动将应用程序设置为默认浏览器,请参阅先前的说明。


2

macOS Ventura 13.0 更新

设置

默认浏览器现在出现在:
系统偏好设置 > 桌面与 Dock > 默认 Web 浏览器

info.plist

info.plist保持不变,为了完整起见,再次将其粘贴在此处。一些答案建议向该文件添加更多条目,但这并不是真正必要的,这是更精简和最低要求的选项。

<key>CFBundleDocumentTypes</key>
<array>
    <dict>
        <key>CFBundleTypeIconSystemGenerated</key>
        <integer>1</integer>
        <key>CFBundleTypeName</key>
        <string>HTML document</string>
        <key>CFBundleTypeRole</key>
        <string>Viewer</string>
        <key>LSHandlerRank</key>
        <string>Default</string>
        <key>LSItemContentTypes</key>
        <array>
            <string>public.html</string>
        </array>
    </dict>
    <dict>
        <key>CFBundleTypeIconSystemGenerated</key>
        <integer>1</integer>
        <key>CFBundleTypeName</key>
        <string>XHTML document</string>
        <key>CFBundleTypeRole</key>
        <string>Viewer</string>
        <key>LSHandlerRank</key>
        <string>Default</string>
        <key>LSItemContentTypes</key>
        <array>
            <string>public.xhtml</string>
        </array>
    </dict>
</array>
<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
        <key>CFBundleURLName</key>
        <string>Website URL</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>http</string>
            <string>https</string>
        </array>
    </dict>
</array>
<key>NSUserActivityTypes</key>
<array>
    <string>NSUserActivityTypeBrowsingWeb</string>
</array>

设置为默认

LSSetDefaultHandlerForURLScheme已被弃用,现在我们可以使用NSWorkspace的功能,支持完成处理程序和async/await

setDefaultApplication(at:toOpenURLsWithScheme:completion:)

func setDefaultApplication(at applicationURL: URL, toOpenURLsWithScheme urlScheme: String) async throws

例子

try? await NSWorkspace.shared.setDefaultApplication(
                                     at: Bundle.main.bundleURL,
                                     toOpenURLsWithScheme: "http")

用户体验

现在系统提示用户确认更改默认浏览器,这是一个很大的改进。

HTTP或HTTPS

我发现只使用http协议就足够了,就像上面的例子一样。尝试使用https会抛出错误。

旧版本(原始答案)

为了出现在系统偏好设置 > 通用 > 默认网页浏览器选项中(至少适用于macOS 11),您需要将HTMLXHTML文档类型添加到Info.plist中(在已经描述的4个步骤之后,如接受的答案所述),就像这样:

<key>CFBundleDocumentTypes</key>
<array>
    <dict>
        <key>CFBundleTypeName</key>
        <string>HTML document</string>
        <key>CFBundleTypeRole</key>
        <string>Viewer</string>
        <key>LSItemContentTypes</key>
        <array>
            <string>public.html</string>
        </array>
    </dict>
    <dict>
        <key>CFBundleTypeName</key>
        <string>XHTML document</string>
        <key>CFBundleTypeRole</key>
        <string>Viewer</string>
        <key>LSItemContentTypes</key>
        <array>
            <string>public.xhtml</string>
        </array>
    </dict>
</array>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接