繁体   English   中英

在使用 UIKit for macOS (Catalyst) 构建的应用程序中生成进程

[英]Spawning a process in an app built with UIKit for macOS (Catalyst)

我正在构建一个在 macOS 和 iOS 版本之间共享大部分代码的应用程序(针对 macOS 11 和 iOS 14)。 适用于 Mac 的 UIKit 似乎是解决此问题的自然选择。 不幸的是,其中一个库在后台使用Process类型 当添加对它的依赖项以及针对 macOS 时,构建它会产生“无法在范围内找到类型Process ”错误。 我可以排除 iOS 的这个库,但我仍然需要在 macOS 上链接它,同时保持在所有平台上使用 UIKit 的能力。

在此处输入图像描述

我已选择此库仅在 Xcode 中为 macOS 链接,但这没有任何效果,并且相同的构建错误仍然存在。 此外,我没有在应用程序中添加单个import SwiftLSPClient语句就收到了这个错误,所以我认为条件导入在这种情况下不会有帮助。

Xcode 中的框架、库和嵌入式内容设置

在上面列出的限制范围内解决此问题的最佳方法是什么?

我在我的 Mac Catalyst 应用程序中创建了一个 LSPCatalyst class 来替换 MacOS LanguageServerProcessHost。 为了完成这项工作,我将process属性替换为processProxy ,它使用 FoundationApp 协议访问 MacOS 包中的流程实例,如下所述。

按照@Adam 的建议,我创建了一个 MacOS 包来代理流程实例。 您遵循与他指出的从 Catalyst 应用程序访问 AppKit 相同的想法,但您只需要 Foundation 即可访问 Process。 我调用了捆绑包 FoundationGlue 并将所有内容放在我的 Xcode 项目的 FoundationGlue 文件夹中。 该捆绑包需要一个 Info.plist,将主体 class 标识为“FoundationGlue.MacApp”,MacApp.swift 如下所示:

    import Foundation

    class MacApp: NSObject, FoundationApp {
    var process: Process!
    var terminationObserver: NSObjectProtocol!
    
    func initProcess(_ launchPath: String!, _ arguments: [String]?, _ environment: [String : String]?) {
        process = Process()
        process.launchPath = launchPath
        process.arguments = arguments
        process.environment = environment
    }
    
    func setTerminationCompletion(_ completion: (()->Void)!) {
        let terminationCompletion = {
            NotificationCenter.default.removeObserver(self.terminationObserver!)
            completion?()
        }
        terminationObserver =
            NotificationCenter.default.addObserver(
                forName: Process.didTerminateNotification,
                object: process,
                queue: nil) { notification -> Void in
                terminationCompletion()
            }
    }
    
    func setupProcessPipes(_ stdin: Pipe!, _ stdout: Pipe!, _ stderr: Pipe!) {
        process.standardInput = stdin
        process.standardOutput = stdout
        process.standardError = stderr
    }
    
    func launchProcess() {
        process.launch()
        print("Launched process \(process.processIdentifier)")
    }

    func terminateProcess() {
        process.terminate()
    }
    
    func isRunningProcess() -> Bool {
        return process.isRunning
    }

    
}

我调用FoundationApp.h对应的header看起来像:

#import <Foundation/Foundation.h>

@protocol FoundationApp <NSObject>

typedef void (^terminationCompletion) ();
- (void)initProcess: (NSString *) launchPath :(NSArray<NSString *> *) arguments :(NSDictionary<NSString *, NSString *> *) environment;
- (void)setTerminationCompletion: (terminationCompletion) completion;
- (void)setupProcessPipes: (NSPipe *) stdin :(NSPipe *) stdout :(NSPipe *) stderr;
- (void)launchProcess;
- (void)terminateProcess;
- (bool)isRunningProcess;

@end

FoundationAppGlue-Bridging-Header.h 仅包含:

#import "FoundationApp.h"

为 MacOS 构建包后,将其作为框架添加到 Mac Catalyst 项目中。 我在该项目中创建了一个 Catalyst.swift 用于访问 FoundationGlue 捆绑功能:

import Foundation

@available(macCatalyst 13, *)
struct Catalyst {

    /// Catalyst.foundation gives access to the Foundation functionality identified in FoundationApp.h and implemented in FoundationGlue/MacApp.swift
    static var foundation: FoundationApp! {
        let url = Bundle.main.builtInPlugInsURL?.appendingPathComponent("FoundationGlue.bundle")
        let bundle = Bundle(path: url!.path)!
        bundle.load()
        let cls = bundle.principalClass as! NSObject.Type
        return cls.init() as? FoundationApp
    }
    
}

然后,您可以从您的应用程序中使用它,例如:

let foundationApp = Catalyst.foundation!
foundationApp.initProcess("/bin/sh", ["-c", "echo 1\nsleep 1\necho 2\nsleep 1\necho 3\nsleep 1\necho 4\nsleep 1\nexit\n"], nil)
foundationApp.setTerminationCompletion({print("terminated")})
foundationApp.launchProcess()

这是一个混乱的解决方案,但我知道它有效:将“Mac 捆绑包”添加到您的 Catalyst 应用程序,然后导入仅适用于 MacOS 的框架。

以下是创建和加载 Mac 包的指南: https://medium.com/better-programming/how-to-access-the-appkit-api-from-mac-catalyst-apps-2184527020b5

获得捆绑包后,您可以向其中添加仅限 Mac 的库和框架。 您必须在包和您的 iOS 应用程序之间桥接数据和方法调用,但它是可管理的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM