Underlying Type for Tuple in Swift

Underlying type for Tuple in Swift

Declaring

let x: Tuple

doesn't make sense for two reasons:

  • the tuple cardinality is missing (see below)
  • the parametric types are missing

It's pretty much as declaring

let x: Array

which is forbidden as well (the parametric type is missing)

The difference here is also that Tuple is not a type per se. A tuple type is defined by both the types of the elements it holds and their number.

So a Tuple type doesn't make sense, rather you will have several types Tuple2, Tuple3 and so on. Each TupleX has have X type parameters, which will define the concrete type once provided.

In swift the compiler creates this types for you (I have no references for that, but it's the only reasonable assumption I can make), probably up to a finite number (which I still haven't found). The naming of this types doesn't look to be explicit, so you won't find a Tuple2, but instead you'll have a ($T1, $T2) type.

To sum it up, declaring a Tuple parameter is meaningless for several reasons, but you can surely have something like

var x: (Int, String)

or

var y: Optional<(Int, String)>

and so on.

To further clarify my point, here's why Tuple2 and Tuple3 need to be two different types:

class Tuple2<T1,T2> {
init (_ e1: T1, _ e2: T2) {}
}

class Tuple3<T1,T2,T3> {
init (_ e1: T1, _ e2: T2, _ e3: T3) {}
}

Tuple2(1, "string")
Tuple3(1, "string", ["an", "array"])

This is probably similar to what the compiler generates, and you can easily see that each TupleX takes a different number of type parameters, hence we need to define a type for each X.

How to detect that parameter is a tuple of two arbitrary types?

You can use Swift's baby introspection methods to get at this:

func isTuple(b: Any) -> Bool {
return reflect(b).disposition == MirrorDisposition.Tuple
}

Note that reflect is largely undocumented and may only be there as support for the playground / debugger, but as far as I know this is the only way to do this.


To achieve this you need to drill down into what reflect() gives you, which is a struct that conforms to MirrorType, which I call a reflection, for lack of a better term. You can subscript the reflection of a tuple to get reflections of the tuples members, and then get the value back out as Any. At that point you can use optional binding to safely rediscover the underlying type:

func process(value: Any) {
println("Any \(value)")
}

func process(value: String) {
println("String \(value)")
}

func processTuple(b: Any) -> Bool {
let isTuple = reflect(b).disposition == MirrorDisposition.Tuple

let r = reflect(b)
for i in 0..<r.count {
println(r[i].0) // string holding tuple part name: ".0", ".1", etc
println(r[i].1.value) // the value of that tuple part: "aa", 1.2

process(r[i].1.value) // calls process(Any)
if let val = r[i].1.value as? String {
process(val) // calls process(String)
}
}

return isTuple
}

let myString = "aa"
let myDouble = 1.2
processTuple((myString, myDouble)) //returns false

Output:

.0
aa
Any aa
String aa
.1
1.2
Any 1.2

What is the limit (if any) to the tuple cardinality in Swift?

In the current version of Xcode 6 Beta, compilation fails with tuples of arity larger than 1948 (the swift executable exits with code 254; there isn't a specific warning or error).

Flatten TupleViews using SwiftUI

One way to fix this is to use the type eraser AnyView:

var body: some View {
switch shape {
case .oneCircle:
return AnyView(ZStack {
Circle().fill(Color.red)
})
case .twoCircles:
return AnyView(ZStack {
Circle().fill(Color.green)
Circle().fill(Color.blue)
})
}
}

UPDATE

I add the following to answer the commenters who are asking why this is needed.

One commenter says

ZStack is still a View, right?

Actually, no. ZStack by itself is not a View. ZStack<SomeConcreteView> is a View.

The declaration of ZStack looks like this:

public struct ZStack<Content> : View where Content : View

ZStack is generic. That means that ZStack by itself is not a type. It is a “type constructor”.

The idea of a type constructor is not usually discussed in the Swift community. A type constructor is, essentially, a function that runs at compile time. The function takes one or more types as arguments and returns a type.

ZStack is a type constructor that takes one argument. If you ‘call’ ZStack repeatedly with different arguments, it returns different answers. This is what Robert Gummesson shows in his question:

This happens because the first ZStack is this type:

ZStack<ShapeView<Circle, Color>>

and the second is this type:

ZStack<TupleView<(ShapeView<Circle, Color>, ShapeView<Circle, Color>)>>

In the first case, the program ‘calls’ ZStack with the argument ShapeView<Circle, Color> and gets back a type as the answer. In the second case, the program ‘calls’ ZStack with a different argument, TupleView<(ShapeView<Circle, Color>, ShapeView<Circle, Color>)>, and so it gets back a different type as the answer.

The declaration var body: some View says that the body method returns a specific, concrete type (to be deduced by the compiler) that conforms to the View protocol. Since the two ‘calls’ to ZStack return different concrete types, we must find a way to convert them both to a single common type. That is the purpose of AnyView. Note that AnyView is not generic, which is to say, it is not a type constructor. It is just a plain type.

How to enumerate an enum with String type?

Swift 4.2+

Starting with Swift 4.2 (with Xcode 10), just add protocol conformance to CaseIterable to benefit from allCases. To add this protocol conformance, you simply need to write somewhere:

extension Suit: CaseIterable {}

If the enum is your own, you may specify the conformance directly in the declaration:

enum Suit: String, CaseIterable { case spades = "♠"; case hearts = "♥"; case diamonds = "♦"; case clubs = "♣" }

Then the following code will print all possible values:

Suit.allCases.forEach {
print($0.rawValue)
}


Compatibility with earlier Swift versions (3.x and 4.x)

If you need to support Swift 3.x or 4.0, you may mimic the Swift 4.2 implementation by adding the following code:

#if !swift(>=4.2)
public protocol CaseIterable {
associatedtype AllCases: Collection where AllCases.Element == Self
static var allCases: AllCases { get }
}
extension CaseIterable where Self: Hashable {
static var allCases: [Self] {
return [Self](AnySequence { () -> AnyIterator<Self> in
var raw = 0
var first: Self?
return AnyIterator {
let current = withUnsafeBytes(of: &raw) { $0.load(as: Self.self) }
if raw == 0 {
first = current
} else if current == first {
return nil
}
raw += 1
return current
}
})
}
}
#endif

Model Objects In Swift

A Tuple can be created using the type alias like this

typealias Tuple = (String,Int,Double)

A Tuple Array

typealias TupleArray = [Tuple]

A Dictionary from String to Tuple Arrays

typealias DictionaryOfTuples = [String : TupleArray]

or

typealias DictionaryOfTuples = [String : [Tuple]] 

Can be used like this

var array1:[Tuple] = [("1",1,1.0),("1",1,1.0)]
var array2 :[Tuple] = [("1",1,1.0),("1",1,1.0)]

var single :Tuple = array1[0]

var dictionary1 :DictionaryOfTuples = ["A" : array1, "B" : array2]
var dictionary2 :DictionaryOfTuples = ["C" : array1, "D" : array2]

var final_array = [dictionary1,dictionary2]

How to check for Enum types in Swift?

enum Things {
case Thing1
case Thing2
}

let something:Any = Things.Thing1

something.dynamicType == Things.self // true

update based on discussion ..

protocol P {}
enum Things:P {
case Thing1
case Thing2
}
enum Things2:P{
case Things21
}

let something:Any = Things.Thing1
something.dynamicType == Things.self // true
if let p = something as? P {
print(true)
}

let somethingelse: Any = Things2.Things21
if let p = somethingelse as? P {
print(true)
}


Related Topics



Leave a reply



Submit