2017-03-18 43 views
1

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

更多信息:

如果它是相关的,这里是我想达到的目标。我在swift上创建应用程序,运行在Linux服务器上。用户可以使用他们的浏览器连接到它。我希望能让其他人编写能够自行实现功能(用户可以看到并且一旦连接就能完成的功能)的“插件”,从打印Hello世界,通过聊天程序到游戏,而不必担心提供的低级别内容由我的应用程序。某种类型的DLL,我的服务器应用程序加载和运行。

回答

2

对此的解决方案并不简单,但也不是不可能。我更愿意使用swift包管理器来管理依赖项,并将Xcode作为IDE进行管理。这种组合并不完美,因为它需要大量修补,但目前还没有任何其他可用的免费swift IDE。

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

TestPluginFunc() 

创建Plugin.swift与TestPluginFunc实现文件在您的插件项目:

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") 
    } 
} 

如果操作正确,您应该看到“Hooray!“

有很大的改进空间,你应该做的第一件事是用参数替换Plugin.framework字符串,最好使用一些文件库(我正在使用PerfectLib)。在您的PluginConsumer项目中定义插件API作为协议或基类,创建框架,在您的插件项目中导入该框架,并将您的实现基于该协议/基类。我试图弄清楚如何做。如果我奶源正确做到这一点,我会更新这个帖子

[1]:我通常通过创建Package.swift文件和创建Xcode项目出它使用swift package generate-xcodeproj的做到这一点。如果你的项目没有。包含main.sw ift,xcode将创建框架而不是可执行文件

0

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

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) 
} 

`

这里有一个快速的插件将如何接受参数:

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,那么还有其他一些选项可供选择。希望这可以帮助。