我正在编写一个函数,用于从Swift调用JavaScript,并希望接受函数的名称和调用参数的列表。列表应该能够包含任何我可以转换成JSON的内容。
例如:
callJS(function: "console.log", withArgs: [1, "Hello"])我天真地试图这样做:
func callJS(function: String, withArgs args: [Encodable]) {
let encoder = JSONEncoder()
let data = try! encoder.encode(list)
let jsonArgs = String(data: data, encoding: .utf8)
executeJS("\(function)(...\(jsonArgs))")
}不幸的是,这在运行时失败了,出现了以下致命错误:
致命错误:数组不符合Encodable,因为Encodable不符合它本身。您必须使用具体类型来编码或解码。
有没有办法让编译器从调用站点嵌入类型,以便Encodable知道该编码什么?
发布于 2018-03-15 14:14:34
一种解决方案是使用元组类型而不是Array。这样做的缺点是函数的独立性需要硬编码。
func callJS(function: String) {
executeJS("\(function)()")
}
func callJS<A: Encodable>(function: String, withArg arg: A) {
let a = try! String(data: self.encoder.encode([arg]), encoding: .utf8)!
executeJS("\(function)(\(a))")
}
func callJS<A: Encodable, B: Encodable>(function: String, withArgs args: (A, B)) {
let a = try! String(data: self.encoder.encode([args.0]), encoding: .utf8)!
let b = try! String(data: self.encoder.encode([args.1]), encoding: .utf8)!
executeJS("\(function)(\(a),\(b))")
}
func callJS<A: Encodable, B: Encodable, C: Encodable>(function: String, withArgs args: (A, B, C)) {
let a = try! String(data: self.encoder.encode([args.0]), encoding: .utf8)!
let b = try! String(data: self.encoder.encode([args.1]), encoding: .utf8)!
let c = try! String(data: self.encoder.encode([args.2]), encoding: .utf8)!
executeJS("\(function)(\(a),\(b),\(c))")
}
func callJS<A: Encodable, B: Encodable, C: Encodable, D: Encodable>(function: String, withArgs args: (A, B, C, D)) {
let a = try! String(data: self.encoder.encode([args.0]), encoding: .utf8)!
let b = try! String(data: self.encoder.encode([args.1]), encoding: .utf8)!
let c = try! String(data: self.encoder.encode([args.2]), encoding: .utf8)!
let d = try! String(data: self.encoder.encode([args.3]), encoding: .utf8)!
executeJS("\(function)(\(a),\(b),\(c),\(d))")
}
// and so on for each arity that you need to support然后,可以简单地将其称为:
callJS(function: "console.log", withArgs: (1, "Hello"))发布于 2018-03-15 14:23:29
我认为造成错误的原因是因为编译器被发送了令人困惑的信息,您试图同时传递一个String和Int,这样它就会抛出一个抖动,而不知道该如何处理生活。
当像这样使用时,它确实会起作用:
var array: Array<Codable> = [1, 2, "Hello"]但是,如果函数中使用的对象都是相同类型的,那么让我们假设在该数组中的所有对象都是相同类型的,那么只需将函数更改为:
func callJS<Value: Encodable>(function: String, withArgs args: [Value])但是,如果您希望在数组中使用多个对象,您可能希望查看我曾经编写过的这段代码(在这里发现的),其中我意识到了Array和Codable的局限性,因此构建了这个AnyCodable,可以在使用Array<Any>或Array<Codable>的地方使用。
像这样使用:
func callJS(function: String, withArgs args: [AnyCodable]) {
let encoder = JSONEncoder()
let data = try! encoder.encode(list)
let jsonArgs = String(data: data, encoding: .utf8)
executeJS("\(function)(...\(jsonArgs))")
}
callJS(function: "console.log", withArgs: [1, "Hello"])注意:限制是它只在这种状态下与stdlib结构一起工作。
https://stackoverflow.com/questions/49301659
复制相似问题