Why to use tuples when we can use array to return multiple values in swift
Tuples are anonymous structs that can be used in many ways, and one of them is to make returning multiple values from a function much easier.
The advantages of using a tuple instead of an array are:
- multiple types can be stored in a tuple, whereas in an array you are restricted to one type only (unless you use
[AnyObject]
) - fixed number of values: you cannot pass less or more parameters than expected, whereas in an array you can put any number of arguments
- strongly typed: if parameters of different types are passed in the wrong positions, the compiler will detect that, whereas using an array that won't happen
- refactoring: if the number of parameters, or their type, change, the compiler will produce a relevant compilation error, whereas with arrays that will pass unnoticed
- named: it's possible to associate a name with each parameter
assignment is easier and more flexible - for example, the return value can be assigned to a tuple:
let tuple = functionReturningTuple()
or all parameters can be automatically extracted and assigned to variables
let (param1, param2, param3) = functionReturningTuple()
and it's possible to ignore some values
let (param1, _, _) = functionReturningTuple()
similarity with function parameters: when a function is called, the parameters you pass are actually a tuple. Example:
// SWIFT 2
func doSomething(number: Int, text: String) {
println("\(number): \(text)")
}
doSomething(1, "one")
// SWIFT 3
func doSomething(number: Int, text: String) {
print("\(number): \(text)")
}
doSomething(number: 1, text: "one")(Deprecated in Swift 2) The function can also be invoked as:
let params = (1, "one")
doSomething(params)
This list is probably not exhaustive, but I think there's enough to make you favor tuples to arrays for returning multiple values
Why use a tuple rather than an array or dictionary in Swift?
A tuple can contain elements of different types. So for example you could declare a tuple containing a String and an Int, while all elements of an array have to be the same type, unless you use AnyObject and type casting.
Return multiple values from a function in swift
Return a tuple:
func getTime() -> (Int, Int, Int) {
...
return ( hour, minute, second)
}
Then it's invoked as:
let (hour, minute, second) = getTime()
or:
let time = getTime()
println("hour: \(time.0)")
Tuples vs NSDictionary when returning multiple value, seem same?
Each variable in Swift
has a specific type, be it tuple
or dictionary
.
In the following tuple
:
let nameAndAge = (name:"Jon", age:10)
nameAndAge
is of type (String, Int)
In dictionary
, the type
is referred by the values it contains,
let dict = ["One": 1, 1: "One"]
dict
is of type [AnyHashable:Any]
In
NSDictionary *dict= @{@"name":@"jon",@"age":[NSNumber numberWithLongLong: age],@"array":[NSArray new]} ... etc;
Although, it is in Objective-C
, in Swift
it will have a type of [String:Any]
i.e. let dict = ["name":"jon", "age":10, "array":[String]]
A dictionary
key-value pair types are the one that satisfies all the elements it contains, i.e. top-most superclass.
How to return multiple values from an if-else or a switch case
As the OP asked for a Measurement
solution, here we go. I borrowed Larme's combinedValue
property.
var combinedValue : Double {
return Double(lengthIn1) + Double(lengthIn2)/100
}
Declare lengthUnit
as a UnitLength
value
let lengthUnit: UnitLength = .meters
The convert method is just one line
func convert(_ value: Double, from: UnitLength, to: UnitLength) -> Double {
return Measurement(value: value, unit: from).converted(to: to).value
}
And the three calculation properties are
var meterCalc: Double {
return convert(combinedValue, from: lengthUnit, to: .meters)
}
var inchCalc: Double {
return convert(combinedValue, from: lengthUnit, to: .inches)
}
var feetCalc: Double {
return convert(combinedValue, from: lengthUnit, to: .feet)
}
Can Swift func() return multiple values to an Objective-C caller?
Tuples (function that returns multiple value) are not supported in Objective-C but you can use block for that.
- (void)hilo:(int)holeSize prompt:(int)prompt callback:(void (^)(ballType : Int, ballColor : Int))result {
...
}
[self hilo:(int)holeSize prompt:(int)prompt callback:^(ballType : Int, ballColor : Int) {
....
}];
In Swift, is there a way to to assign elements of array to multiple variables?
In the list of patterns available, there is no "array pattern" or anything like that, so you can't pattern match on arrays. However, what you can do is:
let array = [1,2,3]
let (a, b, c) = (array[0], array[1], array[2])
This will throw an error at runtime if the array has less than 3 elements.
Can I Declare and initialize multiple variables in one line with a tuple?
func getImagesTuple(from array: Array<String>) -> (UIImage, UIImage, UIImage) {
(UIImage(named: array[0])!, UIImage(named: array[1])!, UIImage(named: array[2])!)
}
let (imageOne, imageTwo, imageThree) = getImagesTuple(from: ["imageOne", "imageTwo", "imageThree"])
Why Swift Tuples can be compared only when the number of elements is less than or equal to 6?
Background: Tuples aren't Equatable
Swift's tuples aren't Equatable
, and they actually can't be (for now). It's impossible to write something like:
extension (T1, T2): Equatable { // Invalid
// ...
}
This is because Swift's tuples are structural types: Their identity is derived from their structure. Your (Int, String)
is the same as my (Int, String)
.
You can contrast this from nominal types, whose identity is solely based off their name (well, and the name of the module that defines them), and whose structure is irrelevant. An enum E1 { case a, b }
is different from an enum E2 { case a, b }
, despite being structurally equivalent.
In Swift, only nominal types can conform to protocols (like Equatble
), which precludes tuples from being able to participate.
...but ==
operators exist
Despite this, ==
operators for comparing tuples are provided by the standard library. (But remember, since there is still no conformance to Equatable
, you can't pass a tuple to a function where an Equatable type is expected, e.g. func f<T: Equatable>(input: T)
.)
One ==
operator has to be manually be defined for every tuple arity, like:
public func == <A: Equatable, B: Equatable, >(lhs: (A,B ), rhs: (A,B )) -> Bool { ... }
public func == <A: Equatable, B: Equatable, C: Equatable, >(lhs: (A,B,C ), rhs: (A,B,C )) -> Bool { ... }
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable, >(lhs: (A,B,C,D ), rhs: (A,B,C,D )) -> Bool { ... }
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable, E: Equatable, >(lhs: (A,B,C,D,E ), rhs: (A,B,C,D,E )) -> Bool { ... }
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable, E: Equatable, F: Equatable>(lhs: (A,B,C,D,E,F), rhs: (A,B,C,D,E,F)) -> Bool { ... }
Of course, this would be really tedious to write-out by hand. Instead, it's written using GYB ("Generate your Boilerplate"), a light-weight Python templating tool. It allows the library authors to implement ==
using just:
% for arity in range(2,7):
% typeParams = [chr(ord("A") + i) for i in range(arity)]
% tupleT = "({})".format(",".join(typeParams))
% equatableTypeParams = ", ".join(["{}: Equatable".format(c) for c in typeParams])
// ...
@inlinable // trivial-implementation
public func == <${equatableTypeParams}>(lhs: ${tupleT}, rhs: ${tupleT}) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
${", ".join("lhs.{}".format(i) for i in range(1, arity))}
) == (
${", ".join("rhs.{}".format(i) for i in range(1, arity))}
)
}
Which then gets expanded out by GYB to:
@inlinable // trivial-implementation
public func == <A: Equatable, B: Equatable>(lhs: (A,B), rhs: (A,B)) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
lhs.1
) == (
rhs.1
)
}
@inlinable // trivial-implementation
public func == <A: Equatable, B: Equatable, C: Equatable>(lhs: (A,B,C), rhs: (A,B,C)) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
lhs.1, lhs.2
) == (
rhs.1, rhs.2
)
}
@inlinable // trivial-implementation
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable>(lhs: (A,B,C,D), rhs: (A,B,C,D)) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
lhs.1, lhs.2, lhs.3
) == (
rhs.1, rhs.2, rhs.3
)
}
@inlinable // trivial-implementation
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable, E: Equatable>(lhs: (A,B,C,D,E), rhs: (A,B,C,D,E)) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
lhs.1, lhs.2, lhs.3, lhs.4
) == (
rhs.1, rhs.2, rhs.3, rhs.4
)
}
@inlinable // trivial-implementation
public func == <A: Equatable, B: Equatable, C: Equatable, D: Equatable, E: Equatable, F: Equatable>(lhs: (A,B,C,D,E,F), rhs: (A,B,C,D,E,F)) -> Bool {
guard lhs.0 == rhs.0 else { return false }
/*tail*/ return (
lhs.1, lhs.2, lhs.3, lhs.4, lhs.5
) == (
rhs.1, rhs.2, rhs.3, rhs.4, rhs.5
)
}
Even though they automated this boilerplate and could theoretically change for arity in range(2,7):
to for arity in range(2,999):
, there is still a cost: All of these implementations have to be compiled and produce machine code that ends up bloating the standard library. Thus, there's still a need for a cutoff. The library authors chose 6, though I don't know how they settled on that number in particular.
Future
There's two ways this might improve in the future:
There is a Swift Evolution pitch (not yet implemented, so there's no official proposal yet) to introduce Variadic generics, which explicitly mentions this as one of the motivating examples:
Finally, tuples have always held a special place in the Swift language, but working with arbitrary tuples remains a challenge today. In particular, there is no way to extend tuples, and so clients like the Swift Standard Library must take a similarly boilerplate-heavy approach and define special overloads at each arity for the comparison operators. There, the Standard Library chooses to artificially limit its overload set to tuples of length between 2 and 7, with each additional overload placing ever more strain on the type checker. Of particular note: This proposal lays the ground work for non-nominal conformances, but syntax for such conformances are out of scope.
This proposed language feature would allow one to write:
public func == <T...>(lhs: T..., rhs: T...) where T: Equatable -> Bool {
for (l, r) in zip(lhs, rhs) {
guard l == r else { return false }
}
return true
}Which would be a general-purpose
==
operator that can handle tuples or any arity.There is also interest in potentially supporting non-nominal conformances, allowing structural types like Tuples to conform to protocols (like
Equatable
).That would allow one to something like:
extension<T...> (T...): Equatable where T: Equatable {
public static func == (lhs: Self, rhs: Self) -> Bool {
for (l, r) in zip(lhs, rhs) {
guard l == r else { return false }
}
return true
}
}
Related Topics
Local and Push Notifications in iOS Version Compatible
Uifont - How to Get System Thin Font
iOS - Ensure Execution on Main Thread
Show Current Location and Update Location in Mkmapview in Swift
iOS 13 Uibarbuttonitem Not Clickable and Overlapping Uinavigationbars When Using Uisearchcontroller
Storyboard Reference in Xcode, Where Should We Use It
How to Generate a Barcode from a String in Swift
Application Executable Is Missing a Required Architecture Armv6
Facebook Graph API Get Request - Should Contain "Fields" Parameter (Swift, Facebook Sdk V4.5.1)
Tracking Multiple (20+) Locations with iOS Geofencing
Xcode - Symbol(S) Not Found for Architecture X86_64 (iOS Lib)
Ios: Capture Image from Front Facing Camera
Steps to Create and Edit a Plist File in Xcode
How to Open the Imagepicker in Swiftui