Tamarisk
Tamarisk

Reputation: 567

Swift functions accepting tuples

Is it possible to pass in a tuple into a function as long as their types match up?

When I try it, I get a missing argument in parameter error:

var myTuple = ("Text",10,"More Text")

func myFunction(a:String, b:Int, c:String) {
    // etc...
}

myFunction(myTuple)

Upvotes: 16

Views: 14186

Answers (10)

Dale
Dale

Reputation: 3322

Even though this has been removed in later versions of Swift you can still get there although it's not entirely beautiful. The array function map can apply a function to a tuple. So instead of

myFunction(myTuple)

you can do this:

[myTuple].map(myFunction)[0]

Upvotes: 0

SmileBot
SmileBot

Reputation: 19672

The best option for now seems to be to just save it to a compound variable or use the build in dot syntax

    let (val1, val2) = (1, 2)

    
    func f(first: Int, second: Int) { }
    
    f(first: val1, second: val2)

let vals = (1, 2)

f(first: vals.0, second: vals.1)

Upvotes: 1

Amadeu Cavalcante Filho
Amadeu Cavalcante Filho

Reputation: 2398

That feature called implicit tuple splat was removed in swift 3.

You can find more detailed explanation on the removal proposal here

Some suggestions to keep using tuple as an argument is by doing so:

func f1(_ a : (Int, Int)) { ... }
let x = (1, 2)
f1(x)


func f2<T>(_ a : T) -> T { ... }
let x = (1, 2)
f2(x)

Upvotes: 0

Aleksey Gotyanov
Aleksey Gotyanov

Reputation: 550

You can use the following feature: Swift allows you to pass a function (f1) with any number of parameters (but without inout parameters) as a parameter of type (TIn) -> TOut to another function. In this case, TIn will represent a tuple from the parameters of the function f1:

precedencegroup ApplyArgumentPrecedence {
    higherThan: BitwiseShiftPrecedence 
}

infix operator <- :ApplyArgumentPrecedence

func <-<TIn, TOut>(f: ((TIn) -> TOut), arg: TIn) -> TOut {
    return f(arg)
}

func sum(_ a: Int, _ b: Int) -> Int {
  return a + b
}

print(sum <- (40, 2))

Upvotes: 3

In swift 3.0, we should not able to pass the tuple directly to the function.If we did so, it shows the error message as "This type has been removed in swift 3.0"

func sum(x: Int, y: Int) -> Int
return x+y } 

let params = (x: 1, y: 1)
let x = params.0
let y = params.1
sum(x: x, y: y)

Hope it helps you!!

Upvotes: 1

Drew
Drew

Reputation: 739

It was possible, although was deprecated in Swift 2.2:

In Swift 2.1 and earlier it was possible to use a carefully crafted tuple to fill the parameters of a function. So, if you had a function that took two parameters, you could call it with a two-element tuple as long as the tuple had the correct types and element names.

...

This syntax — affectionately called “tuple splat syntax” — is the antithesis of idiomatic Swift’s self-documenting, readable style, and so it’s deprecated in Swift 2.2.

https://swift.org/blog/swift-2-2-new-features/

Upvotes: 13

Obliquely
Obliquely

Reputation: 7072

I came here wanting to know how to pass a tuple as a function parameter. The answers here focus on a different case. I'm not entirely clear what the OP was after.

In any case, here is how to pass a tuple as a parameter. And, for good measure, how to do it variadically.

func acceptTuple(tuple : (Int, String)) {
    print("The Int is: \(tuple.0)")
    print("The String is '\(tuple.1)'")
}

acceptTuple((45, "zebras"))
// Outputs:
// The Int is: 45
// The String is 'zebras'

func acceptTuples(tuples : (Int, String) ...) {
    var index = 0

    // note: you can't use the (index, tuple) pattern in the for loop,
    // the compiler thinks you're trying to unpack the tuple, hence
    /// use of a manual index 

    for tuple in tuples {
        print("[\(index)] - Int is: \(tuple.0)")
        print("[\(index)] - String is '\(tuple.1)'")
        index++
    }
}

acceptTuples((45, "zebras"), (17, "armadillos"), (12, "caterpillars"))

//Outputs
//[0] - Int is: 45
//[0] - String is 'zebras'
//[1] - Int is: 17
//[1] - String is 'armadillos'
//[2] - Int is: 12
//[2] - String is 'caterpillars'

Passing tuples in can be a quick and convenient approach, saving you from having to create wrappers etc. For example, I have a use case where I am passing a set of tokens and parameters to create a game level. Tuples makes this nice and compact:

// function signature
class func makeLevel(target: String, tokens: (TokenType, String)...) -> GameLevel
// The function is in the class Level. TokenType here is an Enum. 
// example use:
let level = Level("Zoo Station", tokens:
            (.Label, "Zebra"),
            (.Bat, "LeftShape"),
            (.RayTube, "HighPowered"),
            (.Bat, "RightShape"),
            (.GravityWell, "4"),
            (.Accelerator, "Alpha"))

Upvotes: 12

Antonio
Antonio

Reputation: 72810

Yes, it's possible under these conditions:

  • the tuple must be immutable
  • the number of values in the tuple, their type, and their order must match the parameters expected by the function
  • named parameters must match external names in the function signature
  • non-named parameters must match parameters without external name in the function signature

So, your code is ok, the only thing you have to do is turning the tuple into an immutable one (i.e. using let and not var):

let myTuple = ("Text", 10, "More Text")

func myFunction(a:String, b:Int, c:String) {
    // etc...
}

myFunction(myTuple)

One more example with external names:

let myTuple = ("Text", paramB: 10, paramC: "More Text")

func myFunction(a:String, paramB b:Int,  paramC c:String) {
    // etc...
}

myFunction(myTuple)

Upvotes: 8

AstroCB
AstroCB

Reputation: 12367

Yes, but that's the wrong structure: you're passing three variables called a, b, and c rather than a tuple with those components.

You need parentheses around the whole thing:

var myTuple = ("Text", 10, "More Text")

func myFunction(a:(x: String, y: Int, z: String)) {
    println(a)
}

myFunction(myTuple)

Upvotes: 2

Michael Voznesensky
Michael Voznesensky

Reputation: 1618

In your tuple, it appears as though you must name them and then refer to them as such:

so your code should be

var myTuple = (val1: "Text", val2: 10, val3: "More Text")

    func myFunction(a:String, b:Int, c:String) {
        // etc...
    }


    myFunction(myTuple.val1, myTuple.val2, myTuple.val3)

The tuple has named values (val1, val2, val3) which you set and then reference, when you pass in myTuple, to the function myFunction(), it appears as though you are just filling 1 of the 3 available arguements - and with the wrong type to boot! This is the equivalent of storing the types in a tuple, then taking them out for a function call. However, if you want a function to actually take a tuple as a parameter, see below:

var myTuple = (val1: "Text", val2: 10, val3: "More Text")

    func tupleFunc(a:(String, Int, String)) {

    }

    tupleFunc(myTuple)

Upvotes: 3

Related Questions