swift

Methods are the functions defined within classes, structures and enumerations.

class Counter {

    var count = 0

    

    func increase() {

        count += 1

    }

    

    func increase(of value: Int) {

        count += value

    }

    

    func reset() {

        count = 0

    }

}

let counter = Counter()

counter.increase()

counter.increase(of: 5)

print(counter.count) //It prints 6

The Counter class has three methods. The interesting thing is that there are two methods of the same name, the fact that they have different parameters (the first does not take parameters, the second is an integer value) makes them different.

The second function has the parameter named "of value" and you have to note that within the function it is used "value" when the function is instantiated it is used "to". It may seem confusing, actually choosing the appropriate parameter names gives you an easily readable code. The Swift manual devotes a chapter (Function Argument Labels and Parameter Names) just to teach how to name functions and parameters in a convenient way.

The Counter class modifies the property count by its own methods. If we want to do the same with structures or enumerations we would argue with the statement that structures and enumerations are value types, once the instance is created, the type is copied to the instance and any method can no longer change the value of the property. For this reason, you need to use a different syntax to warn the compiler to handle in particular ways the copies of the values.

struct Counter {

    var count = 0

    

    mutating func increase() {

        count += 1

    }

    

    mutating func increase(of value: Int) {

        count += value

    }

    

    mutating func reset() {

        count = 0

    }

}

var counter = Counter()

counter.increase()

counter.increase(of: 5)

print(counter.count) //It prints 6

This is the same code that was previously used with the class. It is worth noting the presence of the word mutating in front of functions that change the value of one's own property, and the constant counter instance has become variable.

The following structure is an example to show the use of type methods. It is supposed to want to save the level reached by a player. Players may be different and share some game data. In particular, the game unlocks new levels only if at least one of the players has reached the level to be able to do so. This sharing between various players or between different instances of the structure involves the need to specify some properties and some methods such as static.  

struct MarkLevel {

    static var unlockedMaxLevel = 1

    var currentLevel = 1

    

    static func unlock(_ level: Int) {

        if level > unlockedMaxLevel {

            unlockedMaxLevel = level

        }

    }

    

    static func unlocked(_ level: Int) -> Bool {

        return level <= unockedMaxLevel

    }

    

    @discardableResult

    

    mutating func progress(at level: Int) -> Bool {

        if MarkLevel.unlocked(level) {

            currentLevel = level

            return true

        }

        else {

            return false

        }

    }

}

 

Type methods are recognizable because they are defined by the word static.

These methods access or modify the property value of (static) type therefore must be static. The reason is simple to understand, non-static methods can be used by structure instances, but as we said earlier (in Chapter II of properties), static properties must not be accessible by instances: the method would be a way to go beyond this bond.

The structure has unlockedMaxLevel as a static property and its value is shared between multiple instances of the structure, currentLevel is referring to each instance (player).

The unlock function does nothing but update the value of the unlockedMaxLevel property with the new level. The unlocked function returns a boolean value indicating whether the level passed as a parameter is unlocked or not; this is done by comparing the passed level with the unlocked level, the comparison returns true or false.

In these two functions, note that the first label of the parameter is the '_' character, meaning that it is not necessary to specify the parameter name during the call (see the next code).

The line that starts with the "@" indicates a compiler attribute. It tells to the compiler not to issue warining in case you do not use the result returned by the function that follows.

The function progress updates the current level of the player if the level has been unlocked.

In the next class we will see the use of the newly created structure:

class Player {

    var level = MarkLevel()

    let playerName : String

    

    init(name: String) {

        playerName = name

    }

    

    func finish(level: Int) {

        MarkLevel.unlock(level + 1)

        self.level.progress(at: level + 1)

    }

}

var player = Player(name: "Paolo")

player.finish(level: 1)

print("Your level is: \(MarkLevel.unlockedMaxLevel)")

//It prints: Your level is: 2

var player2 = Player(name: "Carlo")

if player2.level.progress(at: 6) {

    print("Now my level is 6")

}

else {

    print("Level locked") //It prints this one

}

In the following figure I'm trying to represent graphically what I wrote in code above. The Player class has two properties: namePlayer and level.

This class is instantiated two times with player and player2, during the initialization a name is given to each player.

The level property of each player is a MarkLevel structure that encloses player level data. The peculiarity is that the two structures share unlockedMaxLevel that links the two players, in fact the second player can not play a level that has not yet been unlocked either by him or by the other player.

This "communion of goods" is made possible by the static variables (type properties) of the structure.

The finish method of Player class deserves clarification. This feature is invoked by the player Paolo to complete the current level and move on to the next. The function invokes the static method of the structure MarkLevel.unlock (level + 1) to unlock the next level, which can only be accessed directly from the structure type because it acts on the shared variable between the various instances.

method