So I've made some more experiments. I can use named tuples if I stick with simple arrays:
enum MyError: Error {
case cantConvert
}
class QueryResult {
func tuples<A,B>() throws -> [(A,B)] {
let array: [[Any]] = [[1,"Jakob"],[2,"X"]]
var result = [(A,B)]()
for row in array {
guard let a = row[0] as? A else { throw MyError.cantConvert }
guard let b = row[1] as? B else { throw MyError.cantConvert }
result.append((a, b))
}
return result
}
func tuples<A,B,C>() throws -> [(A,B,C)] {
let array: [[Any]] = [[1,"Jakob", "Egger"],[2,"X", "Y"]]
var result = [(A,B,C)]()
for row in array {
guard let a = row[0] as? A else { throw MyError.cantConvert }
guard let b = row[1] as? B else { throw MyError.cantConvert }
guard let c = row[2] as? C else { throw MyError.cantConvert }
result.append((a, b,c))
}
return result
}
}
let result = QueryResult()
let tuples = try! result.tuples() as [(id: Int, name: String)]
for tuple in tuples {
print("\(tuple.id): \(tuple.name)")
}
let triples = try! result.tuples() as [(id: Int, firstName: String, lastName: String)]
for triple in triples {
print("\(triple.id): \(triple.firstName) \(triple.lastName)")
}
But I wanted to use my own result type, not just an array (so I can return additional information). It seems that Swift has special treatment of tuple arrays that don't extend to other generic types. Consider the following example:
let arr1: Array<(Int,String)> = [(1,"Jakob")]
let arr2: Array<(id: Int, name: String)> = arr1
// works
struct MyStruct<T> {
let tuple: T
}
let struct1: MyStruct<(Int,String)> = MyStruct(tuple:(1,"Jakob"))
let struct2: MyStruct<(id: Int, name: String)> = struct1
// error: cannot convert value of type 'MyStruct<(Int, String)>'
// to specified type 'MyStruct<(id: Int, name: String)>'
So it seems that Swift sometimes treats labelled and unlabelled as the same type, and sometimes as a different type.