Methods
Methods are functions tied to a specific type, like classes, structs, or enums. They handle tasks related to instances of that type or the type as a whole.
Instance methods work on specific objects, helping access or change their data or perform related actions. Type methods, on the other hand, belong to the type itself and are similar to static functions in other languages.
A key feature in Swift is that structs and enums can have methods, unlike in some older languages where only classes could.
Instance Methods
Instance methods are functions attached to objects of a class, struct, or enum. They help manage the object's data or behavior and use the same syntax as regular functions.
You define them inside the type's curly braces. They can access other methods and properties of the type automatically and are called only on an existing object.
Here's a simple example with a Timer class that tracks elapsed time:
class Timer {
var seconds = 0
func tick() {
seconds += 1
}
func add(seconds amount: Int) {
seconds += amount
}
func clear() {
seconds = 0
}
}The Timer class has three instance methods:
tick()adds 1 second.add(seconds:)adds a given number of seconds.clear()sets seconds back to zero.
It also has a variable property seconds to store the current time.
Call these methods using dot notation:
let myTimer = Timer()
// Starts at 0 seconds
myTimer.tick()
// Now at 1 second
myTimer.add(seconds: 10)
// Now at 11 seconds
myTimer.clear()
// Back to 0 secondsMethod parameters can have internal names (used inside the method) and external labels (used when calling), just like functions.
The self Property
Every object has a built-in self property that refers to itself. Use it inside methods to point to the current object.
For example, the tick() method could be:
func tick() {
self.seconds += 1
}You often don't need to write self explicitly—Swift assumes property or method names refer to the object's own unless there's a conflict.
A conflict happens if a parameter has the same name as a property. Then, use self to clarify:
struct Position {
var x = 0.0, y = 0.0
func isRightOf(x: Double) -> Bool {
return self.x > x
}
}
let myPos = Position(x: 5.0, y: 2.0)
if myPos.isRightOf(x: 0.0) {
print("This position is right of x = 0.0")
}
// Prints "This position is right of x = 0.0"Without self, Swift would think both x uses mean the parameter.
Modifying Value Types from Within Instance Methods
Structs and enums are value types, so their properties can't be changed inside methods by default.
To allow changes, mark the method as mutating. This lets it update properties or even replace the whole object, with changes sticking after the method ends.
Example:
struct Position {
var x = 0.0, y = 0.0
mutating func shift(byX deltaX: Double, byY deltaY: Double) {
x += deltaX
y += deltaY
}
}
var myPos = Position(x: 2.0, y: 3.0)
myPos.shift(byX: 1.0, byY: 4.0)
print("Now at (\(myPos.x), \(myPos.y))")
// Prints "Now at (3.0, 7.0)"The mutating keyword allows property changes.
You can't call mutating methods on constants, since their values can't change:
let fixedPos = Position(x: 4.0, y: 4.0)
fixedPos.shift(byX: 1.0, byY: 1.0)
// Error: Can't mutate a constantAssigning to self Within a Mutating Method
Mutating methods can replace self with a new instance entirely.
Alternative version of the above:
struct Position {
var x = 0.0, y = 0.0
mutating func shift(byX deltaX: Double, byY deltaY: Double) {
self = Position(x: x + deltaX, y: y + deltaY)
}
}This creates a new position and assigns it to self.
For enums, mutating methods can switch to a different case:
enum LightSwitch {
case off, dim, bright
mutating func toggle() {
switch self {
case .off:
self = .dim
case .dim:
self = .bright
case .bright:
self = .off
}
}
}
var roomLight = LightSwitch.dim
roomLight.toggle()
// Now .bright
roomLight.toggle()
// Now .offThis cycles through states each call.
Type Methods
Type methods are called on the type itself, not objects. Mark them with static (or class for classes, allowing overrides in subclasses).
Call them with dot notation on the type name:
class Utility {
class func logMessage() {
// Implementation here
}
}
Utility.logMessage()Inside type methods, self refers to the type. Unqualified names point to other type-level items without prefixes.
Example: A GameProgress struct tracks unlocked stages in a game for multiple players.
struct GameProgress {
static var maxUnlockedStage = 1
var currentStage = 1
static func open(_ stage: Int) {
if stage > maxUnlockedStage { maxUnlockedStage = stage }
}
static func isOpen(_ stage: Int) -> Bool {
return stage <= maxUnlockedStage
}
@discardableResult
mutating func proceed(to stage: Int) -> Bool {
if GameProgress.isOpen(stage) {
currentStage = stage
return true
} else {
return false
}
}
}GameProgress uses type properties and methods for shared unlocks, plus an instance method for player progress.
Pair it with a Gamer class:
class Gamer {
var progress = GameProgress()
let gamerName: String
func finish(stage: Int) {
GameProgress.open(stage + 1)
progress.proceed(to: stage + 1)
}
init(name: String) {
gamerName = name
}
}Usage:
var gamer = Gamer(name: "Alex")
gamer.finish(stage: 1)
print("Max unlocked stage now: \(GameProgress.maxUnlockedStage)")
// Prints "Max unlocked stage now: 2"
gamer = Gamer(name: "Jordan")
if gamer.progress.proceed(to: 5) {
print("Gamer at stage 5")
} else {
print("Stage 5 not unlocked yet")
}
// Prints "Stage 5 not unlocked yet"This shows how type methods manage global state.