Swift Has Tuple
It is a very common use case where we want to return two values from a method, which can be either of the same data types or can be of different data types. Typically within this case, we would either create various local variables if the method is of the same class and set those variables from the method and consume them in the place where needed or we could create a struct and return that struct object from the method.
This approach works fine but is it worth defining a new struct every time when we return value(s) from a method. As great as that may sound consider this then if you have several methods which also return value(s) but each method returns the value(s) of different data types. I suppose in said case you could create a separate struct for each of the methods but it isn’t practical and comes on just terrible programming.
Got a Solution?
As the title of this article should have implied Swift has a type called Tuple which is used to group various values in a single compound value. Storing or returning two values of the same or different data types is child play for a tuple. here can or cannot be any relation between both the values.
Using Tuple
It is initialized in two ways:
- unnamed variables
- where the names are not defined within the tuple and use their position to access it
var tuple = ("football", "basketball")
print(tuple.0)
print(tuple.1)
- named variables
- where the names are defined within the tuple and use the names that were defined for them
var tuple = (atk : 2500, def : 2100) print(tuple.atk) print(tuple.def)
- Published in iOS, Programming Languages, Swift
“Structs vs Classes” and using them
This is because it plays a significant role in choosing the right abstraction mechanism keeping both performance and modeling in mind. As you can only imagine this topic is simply the tip of the iceberg. Many advanced concepts and questions of classes and structs in swift. With that being, I have broken this topic into two parts.
Structs & Classes — What are they?
Consider them like templates or guidebook that contains variables and methods used to all object of the said kind. In short, they assist in keeping the code organized for future maintenance and reusability.
Class features over structs
- Inheritance
- inherit the characteristics of another class
- Type-casting
- checks and interprets the type of a class instance at runtime
- Deinitializers
- enable an instance of a class to free up any resources it has assigned
- Reference Count
- allows multiple references to a class instance
Value & reference types
- Value types
- each instance keeps an independent copy of its data
- example: structs, enums, or tuples
- changing one instance will have no effect on the other
- each instance keeps an independent copy of its data
- reference type
- Instances share a single copy of the data.
- changing data in one instance will change the data for all instance pointing to the same instance
- example: classes
Deciding to class or struct
when deciding on a new model we, as developers, should carefully contemplate the use cases of the model. With that stated, you should decide between structs and classes.
using classes:
class Network {
var url:String
init(url:String) {
self.url = url
}
}
var req = Network(url: "https://daurislittle.com")
var newReq = req
newReq.url = "https://facebook.com"
if req === newReq {
print(req.url)
print(newReq.url)
}
- comparing instance identity with “===” makes sense
- Creating a shared and mutable state
- if your intent is to share the state among the threads and variables use classes
using structs:
You will note the code below compare the value of the Network URLs and not the memory addresses.
struct Network {
var url:String
}
extension Network:Equatable{}
let req = Network(url: "https://daurislittle.com")
let newReq = Network(url: "https://facebook.com")
if req == newReq {
print("Both the request contains the same address")
}
- comparing instance data with “==” makes sense
- data needs to be compared and the memory locations of these data are not important
Now you can see that both the requests have different URLs as each request have a different copy of the URLs
struct Network {
var url:String
}
extension Network:Equatable{}
var req = Network(url: "https://daurislittle.com")
var newReq = req
newReq.url = "https://facebook.com"
print(req.url) //https://daurislittle.com
print(newReq.url) //https://facebook.com
Now you can see that both the requests have different URLs as each request have a different copy of the URLs
- Data can be used in code across multiple threads
- when passing and copying value types in a multi-threaded environment we can be sure that each context will have a separate unique copy which will not impact the others
- This helps avoid a lot of unintentional bugs
- when passing and copying value types in a multi-threaded environment we can be sure that each context will have a separate unique copy which will not impact the others
Memory Allocation w/structs and classes
structs are allocated in the stack memory. References of the class objects can be created on the stack but all the properties of the class’s object will be kept in the heap
Reference counting
As stated above classes support heap allocations they need to maintain reference counts for allocation and deallocating objects. On the other hand, structs do not need reference counting. However, if structs contain references then they would incur twice the number of reference counting overhead as compared to a class.
Method Dispatching
Classes use method dispatch but if a class is marked as “final“, the compiler will use static dispatch. Stucts uses “static” dispatch.
Memberwise initializers
Memberwise initializers are those initializers which the compiler generates automatically for structs. We can initialize a struct’s object using these initializers even though we have not provided any custom initializers.
enum RequestType {
case get, post, delete
}
struct Network {
val url:String
var type:RequestType
//Memberwise init generated by the compiler
}
var req = Network(url:"https://daurislittle.com", type: .get)
Note: order arguments in these initializers are decided by the order of the declared properties in struct.
However, as a developer we can write any custom initializer, the compiler now though will not generate the memberwise initializer. You can use both generated memberwise and custom initializers, just need to add the custom initializer within the extension of the struct.
enum RequestType {
case get, post, delete
}
struct Network {
val url:String
var type:RequestType //Memberwise init generated by the compiler
}
extension Network {
initi(URL:String) {
self = Network.init(url: url, type: .get)
}
}
var req = Network.init(url:"https://daurislittle.com")
Keyword “mutating” and when to use it
When changing the property of any struct, using the keyword “mutating” before the “func” keyword when defining a method. This is because the self parameter that’s implicitly passed into every method is immutable by default.
extension Network {
mutating func updateUrl(url:String) {
self.url = url
}
}
//self is passed as a var
The “mutating” keyword allows the compiler to decide which methods can’t be called on let constants. If we try to call a mutating method on a “let” constants. If we try to call the mutating method on a let variable, the compiler shows an error.
- Published in blog, iOS, Programming Languages, Swift


