title | date |
---|---|
Mac Catalyst 中强行使用不支持的 API |
2021-09-02 |
Table of Contents
Mac Catalyst 这个从 iOS 平台过渡到 macOS 的中间方案, 肯定从底层来看, API 肯定是和 AppKit 共享一套 Runtime, 然而, AppKit 中的大多数 API 均被标记为 MacCatalyst 不可用😅. 那么本文将横空出世, 来总结一下我最近遇到的类似问题以及整理一下解决方案, 话不多说, 🏂
共有 2 种解决方案, 一种是通过 Dynamic
这个库来去做, 另外一种是通过植入 macOS 版的插件来去实现.
这个不多说了, 去看 API
吧
注意: 对于一些简单的调用, 推荐使用这个库, 如果说是非常复杂的情况, 还是建议使用第二种, 创建 macOS 版的插件更好一些.
File
-> New
-> Target...
-> macOS
-> Framework & Library
-> Bundle
在 主Target
中添加该 bundle
, 并将 platform filters
修改为 MacCatalyst
由于我们不能在 iOS 的开发环境中直接调用 macOS 的类, 所以我们曲线救国, 创建一个通用 Protocol, 我们在 iOS 的环境中仅调用这个 Protocol 的方法, 不去直接操纵类.
注意: 这里创建的 Protocol Target Membership
需要同时勾选
主 Target
和 Bundle
@objc(WeChatLauncher)
protocol WeChatLauncher: NSObjectProtocol {
init()
func launchWeChat()
}
import AppKit
class MacLauncher: NSObject, WeChatLauncher {
required override init() {
super.init()
}
func launchWeChat() {
let config = NSWorkspace.OpenConfiguration()
NSWorkspace.shared.openApplication(at: URL(fileURLWithPath: "/Applications/WeChat.app"), configuration: config) { app, error in
}
}
}
import Foundation
#if !os(macOS)
import UIKit
#endif
class PluginHelper {
static func presentWeChat4Pasting() {
#if targetEnvironment(macCatalyst)
// 1. 这里 fileName 就是创建 bundle 时填的 Product Name + Bundle Extension. (可以在 Products 中看到)
let bundleFileName = "WeChatPlugins.Mac.bundle"
guard let bundleURL = Bundle.main.builtInPlugInsURL?
.appendingPathComponent(bundleFileName) else { return }
guard let bundle = Bundle(url: bundleURL) else { return }
// 2. 加载 bundle 以及获取插件的类, 类名是插件 [ProductName.类名] 的形式
let className = "WeChatPlugins.MacLauncher"
guard let pluginClass = bundle.classNamed(className) as? WeChatLauncher.Type else {
return
}
// 3. 初始化类, 并调用
pluginClass.init().launchWeChat()
#elseif os(iOS)
UIApplication.shared.open(URL(string: "WeChat://")!) { flag in
}
#endif
}
}
其实如果这里插件只实现了一个类的话, 也可以在 Bundle 的 info
中指定 Principal class
为 $(PRODUCT_MODULE_NAME).MacLauncher
(格式为: $(PRODUCT_MODULE_NAME).类名
)
那么我们获取类时就使用如下代码替代 2 中的代码:
guard let pluginClass = bundle.principalClass as? WeChatLauncher.Type else { return }
Apple 显然是想将 macOS 跟 iOS 在不久的将来进行大一统, 现在的 MacCatalyst 只是一个临时蹩脚方案, 其实我们现在使用的 UIKit
已经是一个外壳了, 真正的核心代码都在 UIKitCore
这个库中了. 至于其中的些许联系, 你或许可以在另外一篇文章中窥得一二.