2017-03-18 82 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,那麼還有其他一些選項可供選擇。希望這可以幫助。