如何在swift

时间:2017-03-18 15:29:36

标签: plugins swift3

我发现article描述了如何使用Swift和Cocoa创建插件。它使用NSBundle加载插件,但据我所知,这不是纯swift(没有Cocoa)。有没有办法如何在不使用Cocoa的情况下实现相同的结果?

更多信息:

如果相关,这就是我想要实现的目标。我在swift中创建运行在linux服务器上的app。用户可以使用他们的浏览器连接到它。我希望能够让其他人编写“插件”来实现功能本身(用户在连接后可以看到和做什么),从打印出来的hello world,聊天程序到游戏,而不必担心提供低级别的东西通过我的应用程序某种dll,我的服务器应用程序加载并运行。

2 个答案:

答案 0 :(得分:3)

解决这个问题并非易事,但要做到这一点并非不可能。我更喜欢使用swift包管理器来管理依赖项,使用Xcode作为IDE。这种组合并不完美,因为它需要大量的修补,但目前还没有任何其他可用的免费swift IDE。

您需要设置两个项目,我们称之为插件(第三方库)和 PluginConsumer (使用其他人插件的应用)。您还需要决定API,现在我们将使用简单的

TestPluginFunc()

插件项目中使用TestPluginFunc实现创建Plugin.swift文件:

public func TestPluginFunc() {
    print("Hooray!")
}

将项目设置为构建框架,而不是可执行文件并构建 [1] 。您将获得包含插件的Plugin.framework文件。

现在切换到 PluginConsumer 项目

插件项目中的Plugin.framework复制到您可以轻松找到的位置。要实际加载框架并使用它:

// we need to define how our plugin function looks like
typealias TestPluginFunc = @convention(c) ()->()
// and what is its name
let pluginFuncName = "TestPluginFunc"

func loadPlugin() {
    let pluginName = "Plugin"
    let openRes = dlopen("./\(pluginName).framework/\(pluginName)", RTLD_NOW|RTLD_LOCAL)
    if openRes != nil {
        // this is fragile
        let symbolName = "_TF\(pluginName.utf8.count)\(pluginName)\(initFuncName.utf8.count)\(initFuncName)FT_T_"
        let sym = dlsym(openRes, symbolName)
        if sym != nil {
            // here we load func from framework based on the name we constructed in "symbolName" variable
            let f: TestPluginFunc = unsafeBitCast(sym, to: TestPluginFunc.self)

            // and now all we need to do is execute our plugin function
            f()

        } else {
            print("Error loading \(realPath). Symbol \(symbolName) not found.")
            dlclose(openRes)
        }
    } else {
        print("error opening lib")
    }
}

如果操作正确,你应该看到“万岁!”被打印到你的日志。

还有很大的改进空间,首先应该做的是用参数替换Plugin.framework字符串,最好使用一些文件库(我使用的是PerfectLib)。另一件需要注意的事情是将PluginConsumer项目中的插件API定义为协议或基类,从中创建框架,在插件项目中导入该框架并将实现基于该协议/基类。我想知道如何做到这一点。如果我能够正确地做到这一点,我会更新这篇文章。

[1]:我通常通过创建Package.swift文件并使用swift package generate-xcodeproj创建xcode项目来完成此操作。如果您的项目不包含main.swift,xcode将创建框架而不是可执行文件

答案 1 :(得分:0)

你想要做的是创建一个程序将要查看的文件夹。假设它叫做'plugins'。它应该从那里的文件中创建一个名称列表,然后迭代使用它们,将参数传递给文件并获取输出并以某种方式使用它。

Activating a program and getting output

func runCommand(cmd : String, args : String...) -> (output: [String], error: [String], exitCode: Int32) {

    var output : [String] = []
    var error : [String] = []

    let task = Process()
    task.launchPath = cmd
    task.arguments = args

    let outpipe = Pipe()
    task.standardOutput = outpipe
    let errpipe = Pipe()
    task.standardError = errpipe

    task.launch()

    let outdata = outpipe.fileHandleForReading.readDataToEndOfFile()
    if var string = String(data: outdata, encoding: .utf8) {
        string = string.trimmingCharacters(in: .newlines)
        output = string.components(separatedBy: "\n")
    }

    let errdata = errpipe.fileHandleForReading.readDataToEndOfFile()
    if var string = String(data: errdata, encoding: .utf8) {
        string = string.trimmingCharacters(in: .newlines)
        error = string.components(separatedBy: "\n")
    }

    task.waitUntilExit()
    let status = task.terminationStatus

    return (output, error, status)
}

`

以下是swift插件如何接受参数:

    for i in 1..C_ARGC {
    let index = Int(i);

    let arg = String.fromCString(C_ARGV[index])
       switch arg {
        case 1:
            println("1");

        case 2:
            println("2")

        default:
            println("3)
       }
    }

因此,一旦你有程序和插件进行通信,你只需要根据输出在程序中添加处理,这样插件输出就可以做一些有意义的事情。没有可可库,这似乎是要走的路,但如果你使用C,那里还有其他几个选项。希望这可以帮助。