Methods: instance, static, and mutating
Learning intentions
In this lesson, you will learn how methods belong to a type and how that changes the way you design reusable code. Explain what a method is
You will also practise two important method keywords in Swift. Explain static methods mutating methods
What is a method?
A method is a function written inside a type such as a struct, class, or enum, so it can work directly with that type's data and purpose. Methods live inside the model definition
struct Player {
var name: String
var score: Int
func summary() -> String {
return "\(name) has \(score) points."
}
}
What is an open function?
An open function is a standalone function written outside a type, and it can still work with model data when that data is passed in as parameters. Open functions are called directly by name,
struct Player {
var name: String
var score: Int
}
func averageScore(from scores: [Int]) -> Double {
guard !scores.isEmpty else { return 0 }
return Double(scores.reduce(0, +)) / Double(scores.count)
}
func playerSummary(for player: Player) -> String {
return "\(player.name) has \(player.score) points."
}
let scores = [8, 12, 15, 9]
let p = Player(name: "Ari", score: 12)
print(averageScore(from: scores))
print(playerSummary(for: p))
Open functions are useful when behaviour is generic and not clearly owned by one type
How methods can help
The same behaviour can often be moved into a type as methods, which makes usage cleaner because the value provides its own context. Methods reduce repeated parameter passing,
struct ScoreBoard {
var scores: [Int]
func averageScore() -> Double {
guard !scores.isEmpty else { return 0 }
return Double(scores.reduce(0, +)) / Double(scores.count)
}
}
struct Player {
var name: String
var score: Int
func summary() -> String {
return "\(name) has \(score) points."
}
}
let board = ScoreBoard(scores: [8, 12, 15, 9])
let p = Player(name: "Ari", score: 12)
print(board.averageScore())
print(p.summary())
With methods, each model owns behaviour that belongs to it, so your API reads more naturally and scales better as projects grow.
Instance methods
An instance method is the most common method type, and it runs on one specific instance of a type. Call instance methods with dot syntax player.summary()
Instance methods are ideal when behaviour depends on one object's current state
Static methods
A static method belongs to the type itself, not to any single instance, so you call it using the type name. Use TypeName.methodName() syntax static when behaviour does not need per-instance state.
struct ScoreRules {
static func bonus(for streak: Int) -> Int {
return streak >= 3 ? 10 : 0
}
}
let extra = ScoreRules.bonus(for: 4)
Static methods are good for helpers, validation, and factory-style setup logic
Mutating methods
Structs are value types, so methods that change stored properties must be marked with mutating. mutating tells Swift the method will change instance state, score += 1 are allowed inside that method.
struct BankAccount {
var owner: String
var balance: Double
mutating func deposit(_ amount: Double) {
balance += amount
}
}
Without mutating, Swift blocks property changes inside struct methods
Computed properties
Computed properties return a value calculated from other data, rather than storing that value directly in memory. Use them when a value should always stay derived from current state,
struct Player {
var name: String
var score: Int
var description: String {
"\(name) has \(score) points."
}
}
A computed property getter is closure-like because it is a block of code that runs each time the property is accessed. It can read self and build a result on demand, { value in value * 2 }
You can think of it as "method-like syntax with property-style access" print(player.description), () brackets.
Choosing between open functions and methods
Choose a method when behaviour clearly belongs to a model, and choose an open function when behaviour is generic and not owned by one type. Ask: "Does this action belong to this model?"
This decision improves readability and architecture, because related behaviour stays discoverable where developers expect to find it.
Task A: convert open functions to methods
- Create a struct called
Bookwith propertiestitle,author, andpages. - Start by writing an open function
bookSummary(title:author:pages:) -> String. - Then move that behaviour into an instance method
summary() -> StringinsideBook. - Create two
Bookinstances and print both summaries using the method version. - Write one sentence explaining why the method version is clearer for this model.
Task B: static method challenge
- Create a struct called
Temperature. - Add two static methods:
toFahrenheit(celsius: Double) -> DoubletoCelsius(fahrenheit: Double) -> Double
- Call both methods using type syntax (for example
Temperature.toFahrenheit(celsius: 22)). - Print clear output lines for at least three conversions.
- Explain why these are better as
staticmethods than instance methods.
Task C: mutating method challenge
- Create a struct called
Timerwith propertiessecondsandisRunning. - Add a
mutatingmethodstart()that setsisRunningtotrue. - Add a
mutatingmethodtick()that increasessecondsby1only whenisRunningistrue. - Add a
mutatingmethodreset()that setssecondsto0andisRunningtofalse. - Test your struct by calling the methods in sequence and printing values after each step.
Task D: mixed design decision
- Build a struct
Cartwith propertyitemsCountstarting at0. - Add a type-level rule with
static let freeShippingThreshold = 5. - Add a
mutatingmethodaddItem()that incrementsitemsCountby1. - Add an instance method
shippingMessage() -> Stringthat usesitemsCountand returns:
"Free shipping"whenitemsCount >= Cart.freeShippingThreshold"Shipping applies"otherwise
- Add a static helper
qualifiesForFreeShipping(count: Int) -> Boolso the shipping rule can be reused in other places (for example previews, reports, or tests without aCartinstance). - Create one
Cartinstance and calladdItem()in a loop. - After each add, print both
cart.itemsCountandcart.shippingMessage(). - In comments, explain why
addItem()andshippingMessage()are instance behaviour, whilefreeShippingThresholdandqualifiesForFreeShippingare type-level behaviour.
Task E: method to computed property
- Create a struct called
Badgewith propertiesnameandlevel. - Add an instance method
label() -> Stringthat returns text like"Ranger - Level 4". - Create at least one
Badgeinstance and print the method result. - Convert
label()into a computed propertylabel: String. - Print the new computed property and confirm the output is the same.
Extension for Super Players!
Take one extra step to strengthen your design choices with computed properties. Add at least one computed property to each of Tasks A through D,
Summary
Methods help you keep behaviour close to the data it belongs to, while open functions remain useful for shared logic outside specific models. Instance methods work with one value's state, static methods provide type-level behaviour
Designing with these tools makes code easier to understand, test, and maintain
Review
Use these prompts to check your understanding. Explain the difference between an open function and a method static method is the best choice, describe why structs need the mutating keyword for state changes,