Swift Class Properties and Methods Explained with Examples
Swift classes become useful when they can store data and perform actions. That is exactly what properties and methods provide. In this article, you will learn how class properties and methods work in Swift, how to write them correctly, when to use each kind, and how to avoid common mistakes that often confuse beginners.
Quick answer: In Swift, class properties store values that belong to an instance or to the class itself, and methods define behaviors that instances or the class can perform. Use properties for data, instance methods for actions on one object, and type methods for behavior tied to the class rather than a specific instance.
Difficulty: Beginner
Helpful to know first: You'll understand this better if you know basic Swift syntax, how variables and constants work, and what a class instance is.
1. What Is Swift Class Properties and Methods?
In Swift, a class can contain both properties and methods. Together, they define what an object knows and what it can do.
- Properties store data, such as a name, age, price, or status.
- Methods define actions, such as printing a description, updating a value, or checking a condition.
- Instance properties and instance methods belong to a specific object created from a class.
- Type properties and type methods belong to the class itself, not to one individual instance.
- Classes usually combine both so each object can hold its own state and perform related behavior.
For example, a Car class might store its brand and speed as properties, and provide methods like accelerate() and stop().
A useful rule is: if it is information the object needs to remember, it is probably a property. If it is something the object should do, it is probably a method.
2. Why Swift Class Properties and Methods Matters
Properties and methods are at the center of object-oriented Swift code. Without them, a class would just be an empty type declaration.
They matter because they let you group related data and behavior into one place. This makes code easier to read, reuse, and maintain. Instead of passing many separate values and functions around your program, you can create a class that handles its own state and logic.
Real-world uses include:
- A User class storing a username and email, with methods for updating profile details.
- A BankAccount class storing a balance, with methods for deposit and withdrawal.
- A Timer class storing elapsed time, with methods to start, pause, and reset.
- A Product class storing price and stock, with methods to apply discounts or mark items as sold out.
You should use properties when data needs to remain attached to an object over time. You should use methods when logic belongs naturally to that object. If a function does not depend on a class's state at all, it may belong elsewhere instead of as a method.
3. Basic Syntax or Core Idea
Let us start with a minimal class that contains both properties and methods.
Declaring stored properties
This example defines a class with two stored properties. Stored properties keep actual values in memory.
class Dog {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}The class has two properties, name and age. The initializer gives them values when a new dog is created.
Adding an instance method
Now add a method that uses the stored properties.
class Dog {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
func describe() {
print("\(name) is \(age) years old.")
}
}The describe() method reads the instance properties and prints a message. Methods can read properties, change them, or call other methods.
Using the class
Here is what creating and using an instance looks like.
let pet = Dog(name: "Milo", age: 3)
pet.describe()This creates a Dog object and calls its method. The output would be Milo is 3 years old.
Notice that classes use self inside methods and initializers to refer to the current instance. You often need it when a parameter name is the same as a property name.
4. Step-by-Step Examples
Example 1: Reading and updating stored properties
This example shows the most basic use of class properties: storing values that can later be read or changed.
class Book {
var title: String
var pages: Int
init(title: String, pages: Int) {
self.title = title
self.pages = pages
}
}
let book = Book(title: "Swift Basics", pages: 250)
print(book.title)
book.pages = 275
print(book.pages)The properties belong to the instance, so each book can have different values. Because they are declared with var, they can be changed after initialization.
Example 2: Using a computed property
Not every property stores a value directly. A computed property calculates its value when accessed.
class Rectangle {
var width: Double
var height: Double
init(width: Double, height: Double) {
self.width = width
self.height = height
}
var area: Double {
return width * height
}
}
let shape = Rectangle(width: 4.0, height: 2.5)
print(shape.area)The area property is not stored separately. Instead, it is calculated from width and height whenever it is read.
Example 3: A method that changes properties
Methods often update an object's internal state.
class Counter {
var count: Int = 0
func increment() {
count += 1
}
func reset() {
count = 0
}
}
let counter = Counter()
counter.increment()
counter.increment()
print(counter.count)
counter.reset()
print(counter.count)Classes are reference types, so methods can modify instance properties even if the instance itself was assigned to a constant with let. The constant reference cannot point to another object, but the object's mutable properties can still change.
Example 4: Type properties and type methods
Sometimes data or behavior belongs to the class itself rather than to each instance.
class Player {
static var maxScore: Int = 100
var name: String
init(name: String) {
self.name = name
}
static func showRules() {
print("Reach \(maxScore) points to win.")
}
}
Player.showRules()
print(Player.maxScore)maxScore and showRules() belong to Player itself. You access them through the class name, not through an instance.
5. Practical Use Cases
- Modeling users, products, messages, files, or accounts where each object needs its own stored data.
- Encapsulating business logic, such as validating input or updating balances, inside methods instead of scattering logic through your codebase.
- Using computed properties for values derived from other stored values, such as total price, full name, or area.
- Tracking app-wide settings or shared limits with type properties.
- Creating utility methods on a class for behavior related to the type itself, such as counters, default values, or factory-style helpers.
- Keeping an object's data and behavior together so the type is easier to understand and test.
6. Common Mistakes
Mistake 1: Forgetting to initialize stored properties
Every stored property in a class must have a value before initialization finishes, unless it is optional or has a default value.
Problem: This class declares a non-optional stored property but never gives it a value, so Swift reports that the class has no initializers or that the property is not initialized.
class Student {
var name: String
}Fix: Give the property a default value or initialize it inside an initializer.
class Student {
var name: String
init(name: String) {
self.name = name
}
}The corrected version works because name is guaranteed to have a value when a Student instance is created.
Mistake 2: Confusing a property with a method
Beginners often try to call a property like a function or read a method as if it were a property.
Problem: fullName is declared as a computed property, so adding parentheses makes Swift treat it like a method call and causes a compile-time error.
class Person {
var firstName = "Ana"
var lastName = "Lee"
var fullName: String {
return firstName + " " + lastName
}
}
let user = Person()
print(user.fullName())Fix: Access a property without parentheses.
let user = Person()
print(user.fullName)The corrected version works because computed properties are read like stored values, even though they calculate their result.
Mistake 3: Forgetting self during initialization when names overlap
Inside an initializer, parameter names often match property names. In that case, you need self to refer to the property on the current instance.
Problem: This code assigns the parameter to itself instead of assigning the parameter to the instance property, so the property remains uninitialized and Swift reports an initialization error.
class Movie {
var title: String
init(title: String) {
title = title
}
}Fix: Use self.title for the property and title for the parameter.
class Movie {
var title: String
init(title: String) {
self.title = title
}
}The corrected version works because the instance property now receives the incoming value properly.
Mistake 4: Trying to access an instance property from a type method
Type methods do not belong to one specific object, so they cannot directly use instance properties.
Problem: This code tries to read name inside a static method, but there is no instance available, so Swift reports that an instance member cannot be used on a type.
class Account {
var name = "Guest"
static func showName() {
print(name)
}
}Fix: Either make the property a type property too, or use an instance method when the behavior depends on instance data.
class Account {
var name = "Guest"
func showName() {
print(name)
}
}The corrected version works because instance methods run with a specific object and can access that object's properties.
7. Best Practices
Practice 1: Use properties for state and methods for behavior
A class is easier to understand when properties represent data and methods represent actions. Mixing these roles makes code harder to read.
// Less clear
class Lamp {
var isOn = false
var switchState: String {
if isOn {
return "Turn off"
}
return "Turn on"
}
}
// Better separation
class Lamp {
var isOn = false
func toggle() {
isOn = !isOn
}
} The better version keeps state in a property and the action of changing it in a method.
Practice 2: Prefer computed properties for derived values
If a value can always be calculated from other properties, a computed property is often better than storing a separate copy that can become outdated.
// Less preferred: duplicated state
class Invoice {
var subtotal: Double = 0
var tax: Double = 0
var total: Double = 0
}
// Preferred: total is derived
class Invoice {
var subtotal: Double = 0
var tax: Double = 0
var total: Double {
return subtotal + tax
}
}This reduces bugs because total is always up to date when read.
Practice 3: Use type members only for truly shared data
Type properties and methods are useful, but they should represent class-level facts, shared counters, or app-wide defaults. Do not use them when each object needs its own value.
// Shared setting for all instances
class GameSettings {
static var defaultLives = 3
}
// Per-instance data belongs on the instance
class GamePlayer {
var name: String
var lives: Int
init(name: String) {
self.name = name
self.lives = GameSettings.defaultLives
}
}This keeps shared configuration separate from per-player state.
8. Limitations and Edge Cases
- Classes are reference types, so two constants can still point to the same object. Changing a property through one reference changes what the other sees.
- A let constant for a class instance does not make its var properties immutable. This often surprises beginners.
- Computed properties recalculate their value whenever they are read, so avoid expensive work there unless that behavior is intentional.
- Type methods marked static cannot be overridden in subclasses. When inheritance matters, Swift also supports class type methods.
- Stored properties must be initialized before you use self fully in an initializer.
- If a property should not be changed from outside the class, consider access control such as private(set) or private, even though that is a broader design topic.
- A computed property without a setter is read-only, so trying to assign to it will produce a compile-time error.
- If a method depends only on input values and not on instance state, it may be better as a separate function or a type method instead of an instance method.
9. Practical Mini Project
This small project models a bank account. It uses stored properties for account data, a computed property for a formatted summary, instance methods for account actions, and a type property to track how many accounts have been created.
class BankAccount {
static var totalAccounts = 0
let owner: String
private(set) var balance: Double
var summary: String {
return "Owner: \(owner), Balance: $\(balance)"
}
init(owner: String, balance: Double) {
self.owner = owner
self.balance = balance
BankAccount.totalAccounts += 1
}
func deposit(amount: Double) {
if amount > 0 {
balance += amount
}
}
func withdraw(amount: Double) {
if amount > 0 && amount <= balance {
balance -= amount
}
}
func printSummary() {
print(summary)
}
}
let account = BankAccount(owner: "Nina", balance: 500.0)
account.deposit(amount: 150.0)
account.withdraw(amount: 100.0)
account.printSummary()
print(BankAccount.totalAccounts)This example shows how properties and methods work together in a realistic class. The account stores data, computes a summary from that data, performs actions through methods, and tracks a shared class-level value with a type property.
10. Key Points
- Properties store data that belongs to a class instance or to the class itself.
- Methods define actions that instances or the class can perform.
- Stored properties keep actual values, while computed properties calculate values on demand.
- Instance methods can access instance properties because they run on a specific object.
- Type properties and type methods belong to the class and are accessed with the class name.
- Stored properties must be initialized before an instance is fully created.
- Use self when property names and parameter names overlap.
- Classes are reference types, so property changes affect all references to the same instance.
11. Practice Exercise
Build a TemperatureSensor class that practices the most important ideas from this article.
- Create a stored property named location of type String.
- Create a stored property named celsius of type Double.
- Create a computed property named fahrenheit that converts the Celsius value.
- Add a method named updateTemperature(to:) that changes the Celsius value.
- Add a method named display() that prints the location and both temperatures.
Expected output: A line showing the sensor location, the Celsius value, and the converted Fahrenheit value after an update.
Hint: Use the formula (celsius * 9 / 5) + 32 inside the computed property.
class TemperatureSensor {
var location: String
var celsius: Double
var fahrenheit: Double {
return (celsius * 9 / 5) + 32
}
init(location: String, celsius: Double) {
self.location = location
self.celsius = celsius
}
func updateTemperature(to newValue: Double) {
celsius = newValue
}
func display() {
print("\(location): \(celsius)°C / \(fahrenheit)°F")
}
}
let sensor = TemperatureSensor(location: "Office", celsius: 22.0)
sensor.display()
sensor.updateTemperature(to: 25.0)
sensor.display()12. Final Summary
Swift class properties and methods are the building blocks that make classes useful. Properties hold the data an object needs, while methods define the actions and logic that operate on that data. Once you understand the difference between stored properties, computed properties, instance methods, and type methods, you can design classes that are much clearer and more maintainable.
In this article, you saw the core syntax, several realistic examples, common beginner mistakes, and a small project that combined the ideas in context. A strong next step is to learn about Swift class initializers, access control, and inheritance, because those topics build directly on how properties and methods work inside classes.