160
votes

In The Swift Programming Language, it says:

Functions can also take a variable number of arguments, collecting them into an array.

  func sumOf(numbers: Int...) -> Int {
      ...
  }

When I call such a function with a comma-separated list of numbers (`sumOf(1, 2, 3, 4), they are made available as an array inside the function.

Question: what if I already have an array of numbers that I want to pass to this function?

let numbers = [1, 2, 3, 4]
sumOf(numbers)

This fails with a compiler error, “Could not find an overload for '__conversion' that accepts the supplied arguments”. Is there a way to turn an existing array into a list of elements that I can pass to a variadic function?

7
Why not just configure the function to take an integer array instead?Mick MacCallum
Sure, but I may not be the author of the function and may not be able to (or want to) change it.Ole Begemann
Ach @OleBegemann here I am, not being able to change the function and reading your question... Still now solution... greetings to Berlinphilipp

7 Answers

102
votes

Splatting is not in the language yet, as confirmed by the devs. Workaround for now is to use an overload or wait if you cannot add overloads.

69
votes

Here's a work around that I found. I know it's not exactly what you want, but it seems to be working.

Step 1: Declare the function you'd like with an array instead of variadic arguments:

func sumOf(numbers: [Int]) -> Int {
    var total = 0
    for i in numbers {
        total += i
    }
    return total
}

Step 2: Call this from within your variadic function:

func sumOf(numbers: Int...) -> Int {
    return sumOf(numbers)
}

Step 3: Call Either Way:

var variadicSum = sumOf(1, 2, 3, 4, 5)
var arraySum = sumOf([1, 2, 3, 4, 5])

It seems strange, but it is working in my tests. Let me know if this causes unforeseen problems for anyone. Swift seems to be able to separate the difference between the two calls with the same function name.

Also, with this method if Apple updates the language as @manojid's answer suggests, you'll only need to update these functions. Otherwise, you'll have to go through and do a lot of renaming.

19
votes

You can cast the function:

typealias Function = [Int] -> Int
let sumOfArray = unsafeBitCast(sumOf, Function.self)
sumOfArray([1, 2, 3])
16
votes

You can use a helper function as such:

func sumOf (numbers : [Int])  -> Int { return numbers.reduce(0, combine: +) }
func sumOf (numbers : Int...) -> Int { return sumOf (numbers) }
2
votes

I know this response does not answer your exact question, but I feel its worth noting. I too was starting to play with Swift and immediately ran into a similar question. Manojlds answer is better for your question, I agree, but again, another workaround I came up with. I do happen to like Logan's better too.

In my case I just wanted to pass an array:

func sumOf(numbers: Array<Int>) -> Int {
    var sum = 0
    for number in numbers {
        sum += number
    }
    return sum
}

var someNums = [8,7,2,9,12]
sumOf(someNums)
sumOf([10, 15, 20])

Just wanted to share, in case anyone else was thinking like me. Most of the time I would prefer pass the array like this, but I don't think the "Swiftly" yet. :)

1
votes

I did this (Wrapper + Identity Mapping):

func addBarButtonItems(types: REWEBarButtonItemType...) {
    addBarButtonItems(types: types.map { $0 })
}

func addBarButtonItems(types: [REWEBarButtonItemType]) {
    // actual implementation
}
0
votes

Swift 5

This is an approach with @dynamicCallable feature that allows to avoid overloading or unsafeBitCast but you should make a specific struct to call:

@dynamicCallable
struct SumOf {
    func dynamicallyCall(withArguments args: [Int]) -> Int {
        return args.reduce(0, +)
    }
}

let sum = SumOf()

// Use a dynamic method call.
sum(1, 2, 3) // 6

// Call the underlying method directly.
sum.dynamicallyCall(withArguments: [1, 2, 3]) // 6