DEV Community

Cover image for Passing Variadic Parameters in Swift
MarcoEidinger
MarcoEidinger

Posted on • Originally published at blog.eidinger.info

Passing Variadic Parameters in Swift

As a Swift developer you might have seen three period characters (...) after a function parameter’s type name.

func handle(_ numbers: Double...) { }
Enter fullscreen mode Exit fullscreen mode

This is a variadic parameter. According to the Swift documentation

A variadic parameter accepts zero or more values of a specified type. You use a variadic parameter to specify that the parameter can be passed a varying number of input values when the function is called.

The values passed to a variadic parameter are made available within the function’s body as an array of the appropriate type. In our example, you will access numbers as a constant array of type [Double].

func handle(_ numbers: Double...) {
  // numbers is of type `[Double]`
  for number in numbers {
    // number is of type `Double`
  }
}
Enter fullscreen mode Exit fullscreen mode

Is it possible to forward the variadic parameter to another function that has a variadic parameter of the same type? Let's try it:

func publicFacingAPI(_ numbers: Double...) {
    handle(numbers) // ERROR: Cannot pass array of type `[Double]` as variadic arguments of type `Double`
}
Enter fullscreen mode Exit fullscreen mode

It appears Swift doesn't like the idea. The most straightforward approach is to keep publicFacingAPI with the variadic parameter and change the argument type of the inner function to [Double].


There is another approach (which I do NOT recommend) by using Any as type of the variadic parameter. Because a variadic parameter of type Any can be forwarded to another function with variadic parameter of type Any 🤪.

The obvious drawback is that you will lose helpful type information. But there is another serious implication. The forwarded parameter will become a nested array whenever you forward it.
If, at some point, the variadic parameter is declared optional then it gets even more difficult. Here is an extreme example.

callFunctionWithAnyVariadicParameter(1, 2, 3, Optional<Any>(nil), 5)

func callFunctionWithAnyVariadicParameter(_ p: Any...) {
    forwardAnyVariadicParameter(p)
}

func forwardAnyVariadicParameter(_ p: Any...) {
    forwardAnyVariadicParameterAgain(p)
}

func forwardAnyVariadicParameterAgain(_ p: Any...) {
    forwardAnyVariadicParameterAgainAndAgain(p)
}

func forwardAnyVariadicParameterAgainAndAgain(_ p: Any?) {
    handle(p)
}

func handle(_ p: Any...)  {
    print(p) // [Optional([[[1, 2, 3, nil, 5]]])]
}
Enter fullscreen mode Exit fullscreen mode

You might think this is extreme. I agree 😊.
You might think is not realistic. Unfortunately, I found this in a real project I was working on 😔. There were (some) reasons why this was done and I had no time to refactor it.

To make life easier and deconstruct a nested type into a simple array and filtering out the nil values I wrote an extension on Array.

func handle(_ p: Any...)  {
    print(p.flatCompactMapForVariadicParameters()) // [1, 2, 3, 5]
}
Enter fullscreen mode Exit fullscreen mode

I hope you found this interesting and please use variadic parameters carefully 😉

For the curious here is the source code of the extension

Top comments (0)