Swift 3: Method expecting variadic String parameter can only receive single String argument

2.7k views Asked by At

I'm calling a method expecting a String... variadic parameter, but the only thing it allows receiving from the enclosing function is a plain String.

My method looks like this:

public func deleteKeys(keysReceived:String..., completionHandler:@escaping () -> Void)
{
        RedisClient.getClient(withIdentifier: RedisClientIdentifier()) {
            c in
            do {
                let client = try c()
                client.delete(keys: keysReceived){}
                completionHandler()
            }
//...
            }
    }

The compile error is

Cannot convert value of type '[String]' to expected argument type 'String'

This method (client.delete()) is from a Redis API for Perfect-Swift so I cannot change the signature, but I can change the enclosing function (deleteKeys). I also cannot call the function directly because it is within a callback closure

Any suggestions on how I can pass a received variadic parameter to an enclosed variadic function? I can break the array down to Single strings and delete individually but that doesn't seem very efficient

2

There are 2 answers

5
AudioBubble On BEST ANSWER

A variadic parameter means it is a type followed by three dots, such as String... They are used to pass in a variable amount of values of the same type. You can't pass a method's variadic parameter to another method. Inside the method it becomes an Array which can't be passed as a variadic without lots of trickery that you really don't want to bother with in this instance.

However, we can see from the source:

public extension RedisClient {
    /// Get the key value.
    func delete(keys: String..., callback: @escaping redisResponseCallback) {
        self.sendCommand(name: "DEL \(keys.joined(separator: " "))", callback: callback)
    }
}

That all they are doing is joining up the Array with a space as a separator. So you can add this to your own code at a top level:

public extension RedisClient {
    /// Get the key value.
    func delete(keys: [String], callback: @escaping redisResponseCallback) {
        self.delete(keys: keys.joined(separator: " "), callback: callback)
    }
}

Then you can call it with:

client.delete(keys: keysReceived){}

Note that this only works in this particular case because, internally, the original method converts the strings in:

delete(keys: "one", "two", "three"){}

to:

["one", "two", "three"]

then to:

"one two three"

I'm doing this manually and passing it the last string like:

delete(keys: "one two three"){}

which becomes:

["one two three"]

and then is joined to:

"one two three"

So the final result is the same when it calls self.sendCommand.

This will most likely not work with other variadic methods since it relies on the method using the joined method internally.

0
3stud1ant3 On

I think you have to use some alternative, because according to documentation

The values passed to a variadic parameter are made available within the function’s body as an array of the appropriate type

So either you can do the work inside the callback of getClient and pass strings comma separated or as you said

I can break the array down to Single strings and delete individually

or you can do something like this:

func deleteKeyForAPI(_ receivedKeys: String...)  {

        //here reveivedKeys is an array...
        print(receivedKeys) //here you can separate keys


}

func yourDeleteKeyFunction(_ receivedKeys: String){
        deleteKeyForAPI(receivedKeys)
}

let myKeys = "s1, s2, s3"
yourDeleteKeyFunction(myKeys)