接受和编码可编码的列表

  • 本文关键字:编码 列表 swift
  • 更新时间 :
  • 英文 :


我正在编写一个用于从 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知道要编码的内容?

一种解决方案是使用元组类型而不是Array。这样做的缺点是函数的arity需要硬编码。

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

我认为错误是由于编译器被发送了令人困惑的信息,您正在尝试传递StringInt,因此它抛出了一个摇摆不定的人,不知道如何处理生活。

这样使用时确实有效:

var array: Array<Codable> = [1, 2, "Hello"]

但是在函数中使用时不是,所以假设所述数组中的所有对象都是同一类型,您可以简单地将函数更改为:

func callJS<Value: Encodable>(function: String, withArgs args: [Value])

但是,如果您希望在数组中使用多个对象,您可能希望查看我曾经编写的这段代码(在此处找到(,我意识到ArrayCodable存在局限性,因此构建了此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 结构