如何在Swift'中导入模块;什么是JavaScriptCore?
我试图使用Swift的JavaScriptCore框架来利用现有的使用ES6模块的JavaScript库。特别是斯蒂芬·菲利普斯。我已将这些文件添加到Xcode游乐场,然后使用以下代码加载库并在JavaScript上下文中运行它:如何在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
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()
}
}