arraysswifttypescasting

func argument with an Array of mixed types (protocol), then call a static method of the protocol


Here are my structures (I can have lot more, that why I need a generic way to do with MyProtocol):

protocol MyCustomCodable {
    static func buildQuery()
}

struct A: MyCustomCodable {
    ...
}

struct B: MyCustomCodable {
    ...
}

...

Then, my problem: I have a code somewhere that I apply on a group of them:

    func updateAllData(customCodableArrays: [[some MyCustomCodable]]) {
        for customCodableArray in customCodableArrays {
            type(of: dbCodablesArray).Element.buildQuery()
        }
    }

    let As: [A] = [A(), A()]
    let Bs: [B] = [] // some arrays can be empty ! That's why buildQuery is static.

    updateAllData([As, Bs])

But here, Swift is complaining that in the call to updateAllData, B cannot be converted to A.

I also tried something like this:

    func updateAllData(customCodableArrays: [some MyCustomCodable]...) {
        for customCodableArray in customCodableArrays {
            type(of: dbCodablesArray).Element.buildQuery()
        }
    }

    let As: [A] = [A(), A()]
    let Bs: [B] = []

    updateAllData([As, Bs])

But 'some' seems not allowed on variadic.

I tried with any (or without any, since it is implicit):

    func updateAllData(customCodableArrays: [[any MyCustomCodable]]) {
        for customCodableArray in customCodableArrays {
            type(of: dbCodablesArray).Element.buildQuery()
        }
    }

    let As: [A] = [A(), A()]
    let Bs: [B] = []

    updateAllData([As, Bs])

But here, it's the call to updateAllData which fails because "Static member 'updateAllData' cannot be used on protocol metatype '(any MyCustomCodable).Type'"

The only solution I found was to pass arrays through individual arguments to updateAllData, with some keyword. But that's not a valid solution since I don't know how much tables I will need, and it enforce me to duplicate (more or less) the code for each of them (I cannot simply iterate on all arrays).

... I don't see how to solve my problem... Please, help !

I tried using any, using some, using variadic, using Any, or casting.


Solution

  • You can also use parameter packs to do this.

    func updateAllData<each T: MyCustomCodable>(customCodableArrays: repeat [each T]) {
        repeat type(of: each customCodableArrays).Element.buildQuery()
    }
    
    updateAllData(customCodableArrays: As, Bs)
    

    or if the contents of the arrays don't matter, take the metatypes directly:

    func updateAllData<each T: MyCustomCodable>(types: repeat (each T).Type) {
        repeat (each types).buildQuery()
    }
    
    updateAllData(types: A.self, B.self)
    

    If buildQuery returns something, say, a Query, you can collect all of them in an array like this:

    func updateAllData<each T: MyCustomCodable>(types: repeat (each T).Type) {
        var queries = [Query]()
        repeat array.append((each types).buildQuery())
    }