Marco Eidinger
Swifty Tech by Marco Eidinger

Swifty Tech by Marco Eidinger

Passing Variadic Parameters around in Swift

Passing Variadic Parameters around in Swift

Marco Eidinger's photo
Marco Eidinger
·Feb 7, 2021·

4 min read

Subscribe to my newsletter and never miss my upcoming articles

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

func handle(_ numbers: Double...) { }

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`
  }
}

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`
}

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]]])]
}

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]
}

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

For the curious here is the source code of the extension

Did you find this article valuable?

Support Marco Eidinger by becoming a sponsor. Any amount is appreciated!

See recent sponsors Learn more about Hashnode Sponsors
 
Share this