如何在Swift'中导入模块;什么是JavaScriptCore?

如何在Swift'中导入模块;什么是JavaScriptCore?,javascript,ios,swift,ecmascript-6,javascriptcore,Javascript,Ios,Swift,Ecmascript 6,Javascriptcore,我试图使用Swift的JavaScriptCore框架来利用现有的使用ES6模块的JavaScript库。特别是斯蒂芬·菲利普斯。我已将这些文件添加到Xcode游乐场,然后使用以下代码加载库并在JavaScript上下文中运行它: import JavaScriptCore var jsContext = JSContext() // set up exception handler for javascript errors jsContext?.exceptionHandler = { co

我试图使用Swift的JavaScriptCore框架来利用现有的使用ES6模块的JavaScript库。特别是斯蒂芬·菲利普斯。我已将这些文件添加到Xcode游乐场,然后使用以下代码加载库并在JavaScript上下文中运行它:

import JavaScriptCore
var jsContext = JSContext()
// set up exception handler for javascript errors
jsContext?.exceptionHandler = { context, exception in
    if let exc = exception {
        print("JS Exception:", exc.toString())
    }
}
// read the javascript files in and evaluate
if let jsSourcePath = Bundle.main.path(forResource: "morse-pro-master/src/morse-pro-message", ofType: "js") {
    do {
        let jsSourceContents = try String(contentsOfFile: jsSourcePath)
        jsContext?.evaluateScript(jsSourceContents)
    } catch {
        print(error.localizedDescription)
    }
}
这种方法适用于简单的“Hello world”类测试,但它会因以下JavaScript错误而阻塞morse pro库:

SyntaxError:意外标记“*”。需要导入调用 只有一个论点

错误似乎是由morse-pro-message.js中的这一行引起的:

import * as Morse from './morse-pro';
我相信它正在尝试将所有morse pro文件作为一个模块导入

我不熟悉ES6模块,但该库似乎在正常JavaScript上下文中为其他模块工作。我在Swift中加载库的方式有问题吗?或者模块是JavaScriptCore不支持的特性?(只是说它支持“JavaScript”,没有更具体的内容。)


如果有任何建议能为我指明在JavaScriptCore VM中运行这个库的方向,我将不胜感激。

我在访问自己的JS代码时也遇到了同样的问题,它使用了3个外部库(Lodash、Moment和Moment range)

最初,我尝试将所有4.js文件(1-我自己的文件和3-库)导入我的应用程序包中。但是当我这样尝试的时候,它不起作用我在自己的JS代码中导入这些库时遇到了问题

解决方法:

lazy var context: JSContext? = {
    let context = JSContext()

    guard let timeSlotJS = Bundle.main.path(forResource: "app", ofType: "js") else {
        print("Unable to read resource file")
        return nil
    }

    do {
        let timeSlotContent = try String(contentsOfFile: timeSlotJS, encoding: String.Encoding.utf8)

        _ = context?.evaluateScript(timeSlotContent)
    } catch {
        print("Error on extracting js content")
    }

    let _ = context?.evaluateScript("var console = { log: function(message) { _consoleLog(message) } }")

    // Print log messages
    let consoleLog: @convention(block) (String) -> Void = { message in
        print("Javascript log: " + message)
    }
    context?.setObject(unsafeBitCast(consoleLog, to: AnyObject.self), forKeyedSubscript: "_consoleLog" as NSCopying & NSObjectProtocol)

    // Print exception messages
    context!.exceptionHandler = { context, exception in
        print("JS Error: \(exception!)")
    }

    return context
}()

func accessingJSCode() {
    let timeSlotScript = "DigitalVaultTimeSlotAvailabilityFuntion({\"services\": \(services)}, {\"response\": {\"result\": {\"CustomModule1\": {\"row\":\(rowValue)}}, \"uri\": \"/crm/private/json/CustomModule1/searchRecords\"}});"
    print(timeSlotScript)
    let timeSlots = context!.evaluateScript(timeSlotScript)

    // Then here i parsed my output JSON from script.
}
最后,我将这些库中的所有代码复制并粘贴到我自己的代码中,并在我的应用程序中只导入了一个.js文件(我自己的文件)。因此,在这种情况下,不需要从任何库导入任何内容。所以我的代码成功地工作了

注意:我不确定我的解决方法是否是好方法。我只是想注册我的问题和解决方法,这对我很有帮助

我的代码:

lazy var context: JSContext? = {
    let context = JSContext()

    guard let timeSlotJS = Bundle.main.path(forResource: "app", ofType: "js") else {
        print("Unable to read resource file")
        return nil
    }

    do {
        let timeSlotContent = try String(contentsOfFile: timeSlotJS, encoding: String.Encoding.utf8)

        _ = context?.evaluateScript(timeSlotContent)
    } catch {
        print("Error on extracting js content")
    }

    let _ = context?.evaluateScript("var console = { log: function(message) { _consoleLog(message) } }")

    // Print log messages
    let consoleLog: @convention(block) (String) -> Void = { message in
        print("Javascript log: " + message)
    }
    context?.setObject(unsafeBitCast(consoleLog, to: AnyObject.self), forKeyedSubscript: "_consoleLog" as NSCopying & NSObjectProtocol)

    // Print exception messages
    context!.exceptionHandler = { context, exception in
        print("JS Error: \(exception!)")
    }

    return context
}()

func accessingJSCode() {
    let timeSlotScript = "DigitalVaultTimeSlotAvailabilityFuntion({\"services\": \(services)}, {\"response\": {\"result\": {\"CustomModule1\": {\"row\":\(rowValue)}}, \"uri\": \"/crm/private/json/CustomModule1/searchRecords\"}});"
    print(timeSlotScript)
    let timeSlots = context!.evaluateScript(timeSlotScript)

    // Then here i parsed my output JSON from script.
}

谢谢

在黑暗中徘徊了很多之后,我找到了一种方法,使图书馆可以使用Swift,而不必手动修改它

首先,正如@estus所建议的,我使用NPM安装了库,它将库转换为ES5,但不解决依赖关系。因此,仍然是一堆单独的文件,它们使用浏览器和JavaScriptCore都无法理解的
require
export
关键字相互调用

然后我将所有依赖项捆绑到一个文件中,以便JavaScriptCore能够理解它。Browserify的正常操作隐藏了所有代码,因此我使用“-standalone”标志告诉它使标记的函数可用。如果直接导出ES5文件,它将创建一个通用对象,并将导出的函数置于
.default
下。我希望它们更容易访问,所以我创建了一个新文件来列出导出,然后在上面运行Browserify。例如,一个名为“morse export.js”的文件包含:

module.exports.MorseMessage = require('./lib/morse-pro-message.js').default;
然后我在上面运行Browserify,如下所示:

browserify ./morse-export.js --standalone Morse > ./morse-bundle.js
并使用
bundle.main.path(forResource)
将morse-bundle.js文件包含在我的Swift代码中。现在,我可以使用
Morse.MorseMessage
访问MorseMessage类,请返回Swift:

jsContext?.evaluateScript("var morseMessage = new Morse.MorseMessage()")
print(jsContext!.evaluateScript("morseMessage.translate('abc')"))
打印出你所期望的“----.”


这样做的缺点是,您必须手动添加要导出到导出文件中的任何类和函数。不过,这似乎是最简单的方法。如果有更好的办法,我很想听听

对模块使用
导入
时是不可能的

您可以支持
require('path/filename')语法

这是通过将require作为函数提供给JS来实现的。不幸的是,
import
命令太离奇,无法在
JSContext
的给定限制中实现

请参见以下实现

目的-C。 此示例基于Phoenix中的代码

斯威夫特4 此示例基于CutBox中的代码


不知道这是否有帮助。尝试安装babel并将javascript文件转换为es5只包含
src
,即ES6。不向回购协议提交dist
是很常见的。如果您克隆了它,那么您将使用
npm build
自行将其构建到ES5。为了避免这种情况,请根据经验从NPM安装软件包。我很确定“导入”在JavaScriptCore中没有正确实现。新的Morse.MorseMessage()是什么??你能解释一下js吗code@mirhpedanielle我对此有点生疏,所以请原谅我的错误
MorseMessage
是斯蒂芬·菲利普斯(Stephen Phillips)的莫尔斯专业版的一款手机。在这个回答中,我使用了Browserify的
--standalone
标志来创建一个名为
Morse
的javascript对象,并将MorseMessage附加到该对象上,以便于导入Swift的javascript上下文。完成后(导入包),
MorseMessage
与任何普通Javascript类一样工作。谢谢你,伙计!!。。但我怀疑是否有必要使用bundle名称创建bundle。。而且调用jsContext.evaluateScript也不起作用,对我来说也不起作用。如果你想找到一个更好的方法不用捆绑,请发布一个答案。我正在尝试使用wkwebview中的外部库。我也需要这样做吗?或者我应该在路径中加载bundle.js文件。请给我一些建议。
import JavascriptCore

class JSService {

    let context = JSContext()

    let shared = JSService()

    let require: @convention(block) (String) -> (JSValue?) = { path in
        let expandedPath = NSString(string: path).expandingTildeInPath

        // Return void or throw an error here.
        guard FileManager.default.fileExists(atPath: expandedPath)
            else { debugPrint("Require: filename \(expandedPath) does not exist")
                   return nil }

        guard let fileContent = try? String(contentsOfFile: expandedFilename) 
            else { return nil }

        return JSService.shared.context.evaluateScript(fileContent)
    }

    init() {
        self.context.setObject(self.require, 
                               forKeyedSubscript: "require" as NSString)
    }

    func repl(_ string: String) -> String {
        return self.context.evaluateScript(string).toString()
    }
}