Swift structs are like structures in other programming languages, used to store multiple values. But they have superpowers in Swift and in most cases, you can use them instead of class.
Suppose you want to use functions in a data type. In other programming languages, like Java or C++, you need to use classes. But in Swift, you can accomplish the same task with struct. In fact, if you don't need complex things like inheritance and overloading, you can generally use struct.
We’re going to write our code in the IDE (integrated development environment) of XCode. We will not create a complete project here, but we’ll use a playground to write our code. We’ve done the playground setup from our earlier post, titledExploring Ranges in Swift: Useful Tips and Tricks.
With following the setup steps, we created a playground called StructVsClass.
What's the Difference Between a Struct and a Class in Swift?
In most programming languages, there are a lot of differences between structs and classes. But in Swift, structs can perform almost all tasks of classes, and structs are preferred in some places. Let’s look into that next.
Structs require no initialization, although we can provide it. But with class, it’s mandatory. If we don't provide initialization for structs, then Swift adds it to the struct code during the compilation phase.
In the below example, we have a struct called Mobile that contains two string values: brand and model. Outside the struct, we’ve created two instances of the string values, and we’re passing the brand and model. And lastly, we print the instances of johnPhone and maryPhone.
The code works completely fine, even without initialization.
In the same code, we give the initialization, and it doesn't make any difference.
Now, in the below example, we have a class called Computer that contains two values: name and ram. We have initialized it with the init() function.
Outside the class, we created two instance of name and ram and pass them. Lastly, we print the details from the instances of johnComp and maryComp.
In the above code, we removed the initialization code. It results in errors, as shown in the screenshot below.
Copy by Reference Vs. Value
In classes, everything is a reference. So when we want to copy an instance, it copies by reference. Now, the problem is that any changes to this new copy will reflect in the original instance.
We’ve updated our class example to have a new instance called davidComp and made it equal to maryComp. After that, we added the name and ram values to it.
Now, both the name and ram of davidComp and maryComp changed. A change in the original instance is not at all a desired feature. It happens because maryComp stores a reference (memory address) of the values. Then, when we assign davidComp to maryComp, it refers to the same address.
When we do the same thing in a struct, it creates a new variable. In our struct example, we added an instance called davidPhone and made it equal to maryPhone. After that, we added the brand and model values to it.
Now, when we check the values of maryPhone and davidPhone, we find the original values of maryPhone unchanged. This happens because, in struct, each variable (or instance) contains the value itself.
Variables and Constants
In structs, when we assign one instance to another, it needed to be variable. You create variables using the var keyword in Swift.
In the previous code, we changed the var to a let and we got the error that it can’t assign a property to a constant, as shown in the screenshot below. Also, notice that it gives us a solution for this: change the let to a var.
As seen earlier, with classes, we have no such problem using let to assign one instance to another.
Advanced Features in Class
Just like in other object-oriented programming languages, Swift classes also have advanced features. We’ll look at the features of inheritance and method overriding. These features are not available in struct.
Classes in Swift can inherit. Inheritance is a property in object-oriented programming, in which the child class can take on some of the properties of a parent class.
In our earlier code, we added the class Laptop, which is inherited from the class Computer. Next, we use the name and ram variables from the parent class Computer in Laptop. Notice that we need to have super.init() inside the init method Laptop.
We also have a function called details, which prints both name and ram.
Again, a class-exclusive feature that’s not available in structs. With method overriding, a class can override the parent class method. All other object-oriented languages have this feature available.
However, in Swift, we have to add the keyword override in the child class. In our earlier example, we had a function with the name details in the parent class Computer. We already had the function with details in Laptop class. Now we have to add the keyword override or else, it will give an error.
We’ve created the instance johnComp of the Computer class. When we call the details() function with it, it calls it from the Computer class. Similarly, we have an instance rohitLaptop of the class Laptop. Now, when we call the details() function with it, it calls it from the Laptop class.
Should You Use a Struct or a Class in Swift?
From the above discussion, you know that structs in Swift can perform all the same functions as classes. In fact, some of the functionality, like call by value, are needed features. According to the official Swift documentation, you should use always struct unless you need features like inheritance and method overriding.
Are Structs Faster Than Classes in Swift?
Structs are actually faster than classes in Swift, which is one of the reasons Swift recommends using structs. So, why are structs faster than classes in Swift?
As you saw earlier, structs use values while classes use references. Sorting and maintaining references is more complex and time-consuming than sorting and maintaining values. You can read about this in detail in this post.
When you use struct frequently in your code instead of classes, it improves your app’s performance. Performance is a major concern in large enterprise apps that have a huge code base.
You should always test your apps to give the users the best experience. Swift has a built-in XCTest framework to test your apps. You can learn about testing in detail from our earlier post, titled SwiftSwitch Statements: Your Questions Answered.
As per the post, you need to create a file called TestRunner in the sources folder. This file basically contains the boilerplate code required for testing.
Next, in the StructVsClass file, we create a class called StructvsClassTests. Inside it, we have the function testShouldPass(), and inside that, we have the earlier struct, Mobile. We created an instance of johnPhone and passed it the required variables. Then we use XCTAssertEqual() to check ifjohnPhone.brand is Apple.
The rest of the code is boilerplate code required by XCTest. On running the file, we see that the test is successful.
As you can see from the above, to write a simple test you need to write a lot of boilerplate code. Tests like button clicks can get complicated pretty quickly. To avoid all this pain we can use the no-code platform, Waldo. In Waldo, you only need to give the APK or APP file and then interact like a normal user. Then you’re done with the test, with the test reports even sent to your email.
In this post, you learned about Swift struct and class. You learned the difference between them and that struct is superior in most cases.
You also did a bit of testing with the built-in Swift framework XCTest, and found that it’s better to do testing with Waldo.