Skip to content

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:

swift
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:

swift
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 seconds

Method 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:

swift
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:

swift
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:

swift
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:

swift
let fixedPos = Position(x: 4.0, y: 4.0)
fixedPos.shift(byX: 1.0, byY: 1.0)
// Error: Can't mutate a constant

Assigning to self Within a Mutating Method

Mutating methods can replace self with a new instance entirely.

Alternative version of the above:

swift
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:

swift
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 .off

This 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:

swift
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.

swift
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:

swift
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:

swift
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.

Released under the MIT License.