Understanding Structures, Classes, and Objects in Swift
Course: iOS App Development Training
Topic: Understanding Structures, Classes, and Objects in Swift
Course Link: iOS App Development Training at JustAcademy
Demo Link: Register for a Course Demo
JustAcademy Homepage: Visit JustAcademy
1. Introduction
Structures and classes are fundamental building blocks of Swift programming. They allow developers to create custom data types that combine properties and methods. Structures are value types, while classes are reference types. An instance of a class is traditionally called an object, although Swift commonly uses the term instance for both classes and structures.
In iOS development, these concepts help organize application data, represent users and products, manage shared resources, and build reusable components.
Learning Objectives
- Understand types, instances, structures, classes, and objects.
- Create custom structures and classes in Swift.
- Use properties, methods, and initializers.
- Understand value semantics and reference semantics.
- Learn inheritance, encapsulation, and object identity.
- Choose between structures and classes for common iOS use cases.
- Apply these concepts in practical Swift programs.
2. Understanding Types and Instances
A type defines the properties and behavior that its instances can have. An instance is a specific value created from that type.
struct Student {
var name: String
var age: Int
}
let studentOne = Student(name: "Aarav", age: 20)
let studentTwo = Student(name: "Meera", age: 22)
print(studentOne.name) // Aarav
print(studentTwo.name) // Meera
Here, Student is the type, and studentOne and studentTwo are instances. Each instance stores its own values.
3. Understanding Structures in Swift
3.1 What Is a Structure?
A structure is a custom value type declared with the struct keyword. Structures are suitable for representing independent data such as coordinates, measurements, settings, and model records.
3.2 Creating a Structure
struct Product {
var name: String
var price: Double
var category: String
}
let product = Product(
name: "Headphones",
price: 2499.0,
category: "Electronics"
)
print(product.name)
print(product.price)
print(product.category)
Swift can provide a memberwise initializer for structures, allowing values to be supplied for stored properties when an instance is created.
3.3 Stored and Computed Properties
Stored properties keep values as part of an instance. Computed properties calculate a value when accessed.
struct Rectangle {
var width: Double
var height: Double
var area: Double {
width * height
}
}
let rectangle = Rectangle(width: 10, height: 5)
print(rectangle.area) // 50.0
The width and height properties store data, while area calculates its result from those properties.
3.4 Modifying Structure Properties
Use var for a structure instance when its mutable properties need to change. A structure instance declared with let cannot have its variable properties modified.
struct UserProfile {
var username: String
var city: String
}
var profile = UserProfile(username: "swift_dev", city: "Mumbai")
profile.city = "Pune"
print(profile.city) // Pune
3.5 Methods in Structures
Methods define the behavior of a type. A structure method that modifies its instance must use the mutating keyword.
struct Counter {
var value = 0
mutating func increment() {
value += 1
}
mutating func reset() {
value = 0
}
}
var counter = Counter()
counter.increment()
counter.increment()
print(counter.value) // 2
3.6 Initializers in Structures
An initializer sets up the initial state of an instance. A custom initializer can validate values or apply specific initialization rules.
struct BankAccount {
var owner: String
var balance: Double
init(owner: String, openingBalance: Double) {
self.owner = owner
self.balance = max(0, openingBalance)
}
}
let account = BankAccount(owner: "Riya", openingBalance: 5000)
print(account.balance) // 5000.0
The self keyword refers to the current instance. It helps distinguish instance properties from initializer parameters.
4. Understanding Classes in Swift
4.1 What Is a Class?
A class is a custom reference type declared with the class keyword. Classes support inheritance and reference identity. Multiple variables can refer to the same class instance.
4.2 Creating a Class
class Car {
var brand: String
var model: String
init(brand: String, model: String) {
self.brand = brand
self.model = model
}
func displayDetails() {
print("\(brand) \(model)")
}
}
let car = Car(brand: "Toyota", model: "Camry")
car.displayDetails()
This class defines two properties, an initializer, and a method. The initializer ensures that the brand and model are supplied when the instance is created.
4.3 Properties and Methods in Classes
class Employee {
var name: String
var monthlySalary: Double
var annualSalary: Double {
monthlySalary * 12
}
init(name: String, monthlySalary: Double) {
self.name = name
self.monthlySalary = monthlySalary
}
func displaySalary() {
print("Annual salary: \(annualSalary)")
}
}
let employee = Employee(name: "Kabir", monthlySalary: 45000)
employee.displaySalary()
Like structures, classes can contain stored properties, computed properties, property observers, and methods.
4.4 Understanding Reference Behavior
Assigning a class instance to another variable copies the reference, not the underlying object. Both variables can access the same instance.
class Person {
var name: String
init(name: String) {
self.name = name
}
}
let personOne = Person(name: "Aarav")
let personTwo = personOne
personTwo.name = "Meera"
print(personOne.name) // Meera
print(personTwo.name) // Meera
Both references point to the same object, so the change made through personTwo is visible through personOne.
5. Understanding Objects
5.1 What Is an Object?
An object is traditionally an instance of a class. It combines state and behavior. State is represented by properties, while behavior is represented by methods.
For example, a vehicle object can store its current speed and provide methods to accelerate or brake.
class Vehicle {
var speed: Int = 0
func accelerate() {
speed += 10
}
func brake() {
speed = max(0, speed - 10)
}
}
let vehicle = Vehicle()
vehicle.accelerate()
vehicle.accelerate()
print(vehicle.speed) // 20
vehicle.brake()
print(vehicle.speed) // 10
5.2 Main Object-Oriented Programming Principles
| Principle |
Meaning |
Example |
| Encapsulation |
Combining data and behavior while controlling access. |
Keeping a password property private. |
| Abstraction |
Exposing essential operations while hiding implementation details. |
Providing a simple login method. |
| Inheritance |
Creating a class based on another class. |
An electric car inheriting from a vehicle class. |
| Polymorphism |
Allowing different types to provide different implementations of shared behavior. |
Overriding a superclass method. |
Structures can also support encapsulation and abstraction. Swift protocols provide another way to share behavior across different types without requiring class inheritance.
6. Structures vs Classes
| Feature |
Structure |
Class |
| Keyword |
struct |
class |
| Type category |
Value type |
Reference type |
| Assignment |
Produces independent value behavior |
Copies a reference to the same instance |
| Inheritance |
Cannot inherit from another class or structure |
Can inherit from a superclass |
| Mutating methods |
Required to modify the instance's value |
Not required for ordinary property mutation |
| Identity operators |
Not used for structure identity |
=== and !== |
| Memberwise initializer |
Provided in applicable cases |
Not automatically provided |
| Deinitializer |
Not supported |
Supported |
| Typical uses |
Coordinates, settings, value models |
Shared resources, identity-based entities, class hierarchies |
7. Value Types and Reference Types
7.1 Value Semantics
When a structure is assigned to another variable, the new value behaves independently. Changing the copy does not modify the original.
struct Point {
var x: Int
var y: Int
}
var pointA = Point(x: 10, y: 20)
var pointB = pointA
pointB.x = 100
print(pointA.x) // 10
print(pointB.x) // 100
7.2 Reference Semantics
Class instances have reference semantics. Different variables can refer to the same instance and observe changes made through any of those references.
class Settings {
var theme: String
init(theme: String) {
self.theme = theme
}
}
let settingsA = Settings(theme: "Light")
let settingsB = settingsA
settingsB.theme = "Dark"
print(settingsA.theme) // Dark
7.3 Comparing Class Identity
The === operator checks whether two class references point to the same instance. The !== operator checks whether they point to different instances.
class Device {
var name: String
init(name: String) {
self.name = name
}
}
let deviceA = Device(name: "iPhone")
let deviceB = deviceA
let deviceC = Device(name: "iPhone")
print(deviceA === deviceB) // true
print(deviceA === deviceC) // false
Although deviceA and deviceC have the same property value, they are separate objects. Identity is different from value equality, which is commonly checked using == when the type supports equality.
8. Inheritance in Swift Classes
Inheritance lets a subclass reuse and extend properties and methods from a superclass.
class Animal {
var name: String
init(name: String) {
self.name = name
}
func makeSound() {
print("The animal makes a sound")
}
}
class Dog: Animal {
override func makeSound() {
print("Woof!")
}
func fetch() {
print("\(name) is fetching the ball")
}
}
let dog = Dog(name: "Bruno")
dog.makeSound()
dog.fetch()
The Dog class inherits the name property from Animal and overrides the makeSound() method.
8.1 Using the override Keyword
The override keyword indicates that a subclass provides a new implementation of an inherited overridable member.
class Notification {
func send() {
print("Sending notification")
}
}
class EmailNotification: Notification {
override func send() {
print("Sending email notification")
}
}
let notification = EmailNotification()
notification.send()
8.2 Using super
The super keyword allows a subclass to call an inherited implementation when appropriate.
class BaseView {
func render() {
print("Rendering base view")
}
}
class ProfileView: BaseView {
override func render() {
super.render()
print("Rendering profile details")
}
}
let view = ProfileView()
view.render()
9. Initializers and Deinitializers
9.1 Initializers
An initializer, declared with init, establishes an instance's initial state. Structures can receive a memberwise initializer, while classes do not automatically receive the same kind of memberwise initializer.
class Member {
var name: String
var membershipLevel: String
init(name: String, membershipLevel: String) {
self.name = name
self.membershipLevel = membershipLevel
}
convenience init(name: String) {
self.init(name: name, membershipLevel: "Basic")
}
}
let member = Member(name: "Riya")
print(member.membershipLevel) // Basic
A convenience initializer offers an alternative way to create an instance and delegates to another initializer in the same class.
9.2 Deinitializers
A class can define a deinit method that runs when its instance is being deallocated. Structures do not support deinitializers.
class FileSession {
init() {
print("Session started")
}
deinit {
print("Session ended")
}
}
var session: FileSession? = FileSession()
session = nil
In this example, setting the optional reference to nil releases that reference. When no strong references remain, the instance can be deallocated and its deinitializer can run.
10. Encapsulation and Access Control
Swift provides access control to limit which parts of a program can read or modify properties and methods. Common access levels include private, fileprivate, internal, public, and open.
struct UserAccount {
private var password: String
private(set) var username: String
init(username: String, password: String) {
self.username = username
self.password = password
}
func validatePassword(_ input: String) -> Bool {
password == input
}
mutating func updateUsername(_ newUsername: String) {
username = newUsername
}
}
var account = UserAccount(
username: "swift_user",
password: "secure123"
)
print(account.validatePassword("secure123")) // true
account.updateUsername("ios_user")
print(account.username) // ios_user
The password is private, so it cannot be accessed directly from outside the type. The private(set) modifier allows reading the username while restricting direct modification through its setter.
11. Choosing Between Structures and Classes in iOS
Swift generally recommends using structures by default and choosing classes when reference identity, inheritance, or class-based interoperability is necessary.
| Scenario |
Recommended Type |
Reason |
| Coordinates and measurements |
Structure |
Data behaves as an independent value. |
| App configuration |
Structure |
Copies can be changed independently. |
| Server response model |
Structure |
Local data can be represented as a value. |
| Shared connection manager |
Class |
Reference identity and shared lifecycle can matter. |
| Subclassable base component |
Class |
Supports class inheritance. |
| Objective-C interoperability requiring a class |
Class |
Some framework APIs expect class-based types. |
12. Practical Project: User Profile Manager
Objective: Use a structure to represent user information and a class to manage a simple application session.
import Foundation
struct UserProfile {
var name: String
var city: String
var age: Int
func displayProfile() {
print("Name: \(name)")
print("City: \(city)")
print("Age: \(age)")
}
}
class UserSession {
var isLoggedIn = false
var username: String?
func login(username: String) {
self.username = username
isLoggedIn = true
print("\(username) logged in")
}
func logout() {
isLoggedIn = false
username = nil
print("User logged out")
}
}
var profile = UserProfile(
name: "Meera",
city: "Mumbai",
age: 24
)
profile.displayProfile()
let session = UserSession()
session.login(username: profile.name)
print(session.isLoggedIn) // true
session.logout()
print(session.isLoggedIn) // false
Project Explanation
- UserProfile: Stores a user's name, city, and age as a value type.
- displayProfile(): Displays the stored profile information.
- UserSession: Represents session state using a reference type.
- login(): Updates the username and login status.
- logout(): Clears the session state.
This is a learning example. Real applications should implement authentication and session management using appropriate secure mechanisms.
13. Common Mistakes and Best Practices
| Mistake |
Recommended Practice |
| Using classes for every model |
Prefer structures for independent value-based data. |
Forgetting mutating in a structure method |
Mark methods that modify the structure's value with mutating. |
| Expecting structures to support inheritance |
Use protocol conformance or a class when class inheritance is required. |
| Confusing identity with equality |
Use === for class identity and == for value equality where implemented. |
| Sharing mutable objects unintentionally |
Manage references and object ownership deliberately. |
| Ignoring initialization requirements |
Use initializers that establish valid state before an instance is used. |
| Creating strong reference cycles |
Understand Automatic Reference Counting (ARC) and use weak or unowned references when appropriate. |
14. Practice Exercises
- Create a
Book structure with title, author, and price properties. Add a method to display its details.
- Create a
BankAccount class with deposit and withdrawal methods.
- Copy a structure instance, modify the copy, and confirm that the original remains unchanged.
- Assign a class instance to two variables and verify that both references observe the same property changes.
- Create a base
Shape class and subclasses such as Circle and Rectangle.
- Write a class with a designated initializer and a convenience initializer.
- Decide whether an app configuration, a network connection, and a product model should be structures or classes. Explain why.
15. Interview Questions and Answers
Q1. What is a structure in Swift?
A structure is a custom value type declared with struct. It can contain properties, methods, initializers, and protocol conformance.
Q2. What is a class in Swift?
A class is a custom reference type declared with class. It supports inheritance, identity, and deinitialization.
Q3. What is an object?
An object is traditionally an instance of a class. Swift also uses the general term instance for values created from structures and classes.
Q4. What is the difference between value types and reference types?
Value types behave as independent values when assigned or passed around. Reference types allow multiple references to point to the same instance.
Q5. Why is mutating used in structures?
The mutating keyword allows a structure method to modify its instance's stored properties or replace the value of self.
Q6. Can structures inherit from other structures?
No. Structures cannot inherit from classes or other structures, but they can adopt protocols and use extensions.
Q7. What is the difference between == and ===?
== checks equality when the type implements it. === checks whether two class references point to the same instance.
Q8. What is an initializer?
An initializer prepares an instance by setting its initial property values and satisfying initialization requirements.
Q9. What is a convenience initializer?
A convenience initializer is a secondary class initializer that delegates to another initializer in the same class.
Q10. What is a deinitializer?
A deinitializer, declared using deinit, runs when a class instance is being deallocated.
Q11. When should a class be preferred over a structure?
Use a class when shared identity, inheritance, deinitialization, or class-based framework interoperability is required.
Q12. Can a constant class reference modify an object's properties?
Yes. A constant reference cannot be reassigned, but the referenced object's mutable properties can still be changed when access rules permit it.
16. Quick Revision Checklist
- Understand the difference between a type and an instance.
- Use
struct for value types and class for reference types.
- Learn stored properties, computed properties, and methods.
- Use initializers to establish valid instance state.
- Remember
mutating for structure methods that modify the instance.
- Understand value copying versus reference sharing.
- Use inheritance and
override when class hierarchies are appropriate.
- Distinguish identity from equality.
- Prefer structures for independent data and classes when reference semantics are needed.
- Practice these concepts in small Swift programs.
17. Conclusion
Structures, classes, and objects are important concepts in Swift and iOS development. Structures model independent values, while classes provide reference identity and inheritance. Both can group related data and behavior into reusable types.
Understanding their differences helps developers create maintainable data models, manage state safely, and work effectively with Apple frameworks. Practice with small Swift projects to develop confidence in choosing the right type for each requirement.
18. Learning Resources