Build Status Platforms Documentation Matrix

Extendable

一套用于更轻松地使用 ExtensionKit 的实用工具集

安装

dependencies: [
    .package(url: "https://github.com/ChimeHQ/Extendable", from: "0.1.0")
],
targets: [
    .target(
        name: "ExtensionSide",
        dependencies: ["Extendable"]
    ),
    .target(
        name: "HostSide",
        dependencies: [.product(name: "ExtendableHost", package: "Extendable")]
    ),
]

全局连接

设置 ExtensionKit 扩展可能令人困惑,并且需要大量的样板代码。ConnectableExtension 使管理全局主机连接更容易。

@main
final class ExampleExtension: ConnectableExtension {
    init() {
    }

    func acceptConnection(_ connection: NSXPCConnection) throws {
        // configure your global connection and possibly
        // store references to host interface objects
    }
}

场景

处理基于 View 的扩展更加复杂。而且,没有明确的方法可以在您的视图中访问主机连接。 Extendable 提供了一些组件,可以更轻松地构建场景和管理视图连接。

ConnectingAppExtensionScene

这是一个 AppExtensionScene,可以更容易地在您的 View 中访问场景的连接。

ConnectingAppExtensionScene(sceneID: "one") { (sceneId, connection) in
    try ConnectionView(sceneId: sceneId, connection: connection)
}

AppExtensionSceneGroup

我预计 Ventura 发布后,这种类型将不再需要。而且,也许只是我个人观点,但我一直无法弄清楚如何在没有包装类型的情况下使用 AppExtensionSceneBuilder。 所以它就在这里。

示例视图

您可以独立使用 ConnectingAppExtensionSceneAppExtensionSceneGroup,或者作为更标准扩展结构的一部分。 但是,如果您愿意,您也可以使用 ConnectableSceneExtension 协议来真正简化您的视图类。 这是一个完整的例子

@main
final class ViewExtension: ConnectableSceneExtension {
    init() {
    }

    func acceptConnection(_ connection: NSXPCConnection) throws {
        // handle global connection
    }
    
    var scene: some AppExtensionScene {
        AppExtensionSceneGroup {
            ConnectingAppExtensionScene(sceneID: "one") { (sceneId, connection) in
                try ConnectionView(sceneId: sceneId, connection: connection)
            }
            ConnectingAppExtensionScene(sceneID: "two") { (sceneId, connection) in
                try ConnectionView(sceneId: sceneId, connection: connection)
            }
        }
    }
}

struct ConnectionView: View {
    let sceneName: String
    let connection: NSXPCConnection?

    init(sceneId: String, connection: NSXPCConnection?) throws {
        self.sceneName = sceneId
        self.connection = connection
    }

    var value: String {
        return String(describing: connection)
    }

    var body: some View {
        VStack {
            Rectangle().frame(width: nil, height: 4).foregroundColor(.green)
            Spacer()
            Text("\(sceneName): \(value)")
            Spacer()
            Rectangle().frame(width: nil, height: 4).foregroundColor(.red)
        }
    }
}

ExtendableHost

Extendable 还包括一个名为 ExtendableHost 的第二个库。

您可以使用它的 AppExtensionBrowserViewExtensionHostingView,将 ExtensionKit 视图系统与您主机应用程序中的 SwiftUI 集成。

// very simple init extension to help with actor-isolation compatibility
let process = try await AppExtensionProcess(appExtensionIdentity: identity)

隔离和 AppExtension

目前,AppExtention 协议中的 init 缺乏任何隔离。 如果您依赖扩展的真实但未表达的 @MainActor 隔离,这将使初始化实例变量变得困难。 我包含了一个可以提供帮助的解决方法。 SE-0414 将使这不再必要,ExtensionFoundation 添加注释也会如此。 但与此同时,没有警告是件好事。

@main
final class MyExtension: AppExtension {
    @InitializerTransferred private var value: MainActorType

    nonisolated init() {
        self._value = InitializerTransferred(mainActorProvider: {
            MainActorType()
        })
    }
}

贡献和协作

我很乐意收到你的来信! Issues 或 pull requests 都很棒。 Matrix 空间 也可用于实时帮助,但我强烈倾向于以文档的形式回答。

我更喜欢协作,如果您有类似的项目,我很乐意找到合作的方式。

我更喜欢使用制表符进行缩进,以提高可访问性。 但是,我宁愿你使用你想要的系统并创建一个 PR,而不是因为空格而犹豫不决。

参与此项目即表示您同意遵守 贡献者行为准则