Les fonctions sont des morceaux de code autonomes qui exécutent une tâche spécifique. Vous donnez à une fonction un nom qui identifie ce qu'elle fait, et ce nom est utilisé pour "appeler" la fonction.

Définir une fonction Swift

Chaque fonction en Swift a un type, composé des types de paramètres de la fonction et du type de retour. Vous pouvez utiliser ce type comme n'importe quel autre type dans Swift, ce qui facilite le passage de fonctions en tant que paramètres à d'autres fonctions et le renvoi de fonctions à partir de fonctions. Les fonctions peuvent également être écrites dans d'autres fonctions pour encapsuler des fonctionnalités utiles dans une portée de fonction imbriquée.

Il est important de comprendre la syntaxe des fonctions en Swift pour pouvoir les utiliser correctement dans votre code. Voici un exemple simple de définition de fonction :


func saluer(nom: String) -> String {
  return "Bonjour, \(nom)!"
}

Dans cet exemple, nous définissons une fonction saluer qui prend un argument en entrée nommé nom de type String et renvoie une valeur de type String. Lorsque la fonction est appelée, elle renvoie le message de bienvenue formé avec le nom fourni.

Appeler une fonction

Pour appeler une fonction, il suffit de spécifier le nom de la fonction suivi des arguments nécessaires entre parenthèses. Par exemple :


let message = saluer(nom: "Jean")
print(message)
// Bonjour, Jean!

Nous appelons la fonction en spécifiant son nom, saluer, suivi de l'argument nom: "Jean" entre parenthèses. La valeur de retour de la fonction est assignée à la constante message et affichée à l'aide de la fonction print.

Il est important de s'assurer que les arguments fournis lors de l'appel de la fonction correspondent à ceux attendus par la fonction, en termes de type et de nombre. Sinon, une erreur peut se produire lors de l'exécution du code.

Les paramètres

Comme nous venons de le voir, lorsque vous définissez une fonction, vous pouvez si vous le souhaitez définir une ou plusieurs valeurs nommées et typées que la fonction prend en entrée, appelées paramètres.

Vous pouvez définir en option un type de valeur que la fonction transmettra en sortie lorsqu'elle sera terminée, appelé son type de retour.


func greet(person: String) -> String {
    let greeting = "Bonjour, " + person + "!"
    return greeting
}

print(greet(person: "Anne"))
// "Bonjour, Anne!"

Fonction sans paramètre


func sayHelloWorld() -> String {
    return "Bonjour à tous"
}
print(sayHelloWorld())   
// "Bonjour à tous"

La définition d'une fonction a toujours besoin de parenthèses après le nom de la fonction, même si elle ne prend aucun paramètre. Le nom de la fonction est également suivi d'une paire de parenthèses vides lorsque la fonction est appelée.

Fonction avec plusieurs paramètres


func greet(person: String, alreadyGreeted: Bool) -> String {
    if alreadyGreeted {
        ...
    } else {
        ...
    }
}
print(greet(person: "Tom", alreadyGreeted: true))

Les fonctions peuvent avoir plusieurs paramètres d'entrée, qui sont écrits entre les parenthèses de la fonction, séparés par des virgules.

Valeur de retour

Les fonctions n'ont nécessairement à définir un type de retour. Voici une version de la fonction greet(person:), qui "affiche" sa propre valeur String plutôt que de la renvoyer :


func greet(person: String) {
    print("Hello, \(person)!")
}
greet(person: "Maxime")  
// Cela va afficher : "Hello, Maxime!"

Comme il n'est pas nécessaire de renvoyer une valeur, la définition de la fonction n'inclut pas la flèche de retour ( ->) ou un type de retour.

Pour entrée dans les détails, la fonction greet(person:) renvoie toujours une valeur, même si aucune valeur de retour n'est définie. Les fonctions sans type de retour défini renvoient une valeur spéciale de type Void. Il s'agit simplement d'un tuple vide, qui s'écrit ().

Vous pouvez utiliser les tuples comme type de retour pour une fonction afin de renvoyer plusieurs valeurs dans le cadre d'une valeur de retour composé.


func minMax(array: [Int]) -> (min: Int, max: Int) {
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1.. currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}


La fonction minMax(array:) renvoie un tuple contenant deux valeurs Int. Ces valeurs sont étiquetées min et max sont donc accessibles par leur nom lors de l'interrogation de la valeur de retour de la fonction.


let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("min is \(bounds.min) and max is \(bounds.max)")
// Prints "min is -6 and max is 109"

Retour de tuples facultatifs

Si le type de tuple à renvoyer à partir d'une fonction a le potentiel de n'avoir "aucune valeur" pour le tuple entier, vous pouvez utiliser un type de retour de tuple facultatif ou optionnel pour refléter le fait que le tuple entier peut être nil.

Ne pas confondre un tuple contenant des types facultatifs ou optionnels (Int?, Int?), avec un type de tuple facultatif ou optionnel, le tuple entier est facultatif, pas seulement chaque valeur individuelle dans le tuple (Int, Int)?.

func minMax(array: [Int]) -> (min: Int, max: Int)? {
    if array.isEmpty { return nil }
    var currentMin = array[0]
    var currentMax = array[0]
    for value in array[1.. currentMax {
            currentMax = value
        }
    }
    return (currentMin, currentMax)
}


Vous pouvez utiliser une liaison facultative pour vérifier si cette version de la fonction minMax(array:) renvoie une valeur de tuple réelle ou nil :


if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
    print("min : \(bounds.min) and max : \(bounds.max)")
}   // "min : -6 and max : 109"

Fonction avec retour implicite

Si le corps entier de la fonction est une seule expression, la fonction renvoie implicitement cette expression. Toute fonction que vous écrivez sur une seule ligne peut omettre le return.


func greeting(for person: String) -> String {
    "Bonjour, " + person + "!"
}
// Equivaut à :
func anotherGreeting(for person: String) -> String {
    return "Bonjour, " + person + "!"
}

Les étiquettes d'arguments et paramètres

Chaque paramètre de fonction possède à la fois une étiquette d'argument et un nom de paramètre. L'étiquette d'argument est utilisée lors de l'appel de la fonction; chaque argument est écrit avec son étiquette d'argument devant lui. Le nom du paramètre est utilisé dans l'implémentation de la fonction. Par défaut, les paramètres utilisent leur nom de paramètre comme étiquette d'argument.

Vous écrivez une étiquette d'argument avant le nom du paramètre, séparé par un espace :


func greet(person: String, from hometown: String) -> String {
    return "Bonjour \(person)!  Vous venez de \(hometown)."
}
print(greet(person: "Michel", from: "Lille"))
// "Bonjour Michel!  Vous venez de Lille."
L'utilisation d'étiquettes d'arguments peut permettre à une fonction d'être appelée d'une manière expressive, semblable à une phrase, tout en fournissant un corps de fonction lisible et clair dans l'intention.

Omettre les étiquettes d'arguments

Si vous ne voulez pas d'étiquette d'argument pour un paramètre, écrivez un trait de soulignement _ (underscore) au lieu d'une étiquette d'argument explicite pour ce paramètre.


func someFunction(_ firstParameterName: Int, secondParameterName: Int) {
    // firstParameterName et secondParameterName
    // font références aux valeurs d'argument 
    // des premier et deuxième paramètres
}
someFunction(1, secondParameterName: 2)

Si un paramètre a une étiquette d'argument, l'argument doit être étiqueté lorsque vous appelez la fonction.

Valeur par défaut des paramètres

Vous pouvez définir une valeur par défaut pour n'importe quel paramètre d'une fonction en attribuant une valeur au paramètre après le type de ce paramètre. Si une valeur par défaut est définie, vous n'êtes pas obliger d'inclure ce paramètre dans l'appel de la fonction.


func someFunction(parameterSansDefault: Int, parameterWithDefault: Int = 12) {
    // Si tu oublis d'indiquer un deuxième argument lors de l'appel de cette fonction
    // la valeur du paramètre parameterWithDefault sera égale à 12
}

someFunction(parameterSansDefault: 3, parameterWithDefault: 6) 
// parameterWithDefault est égale à 6

someFunction(parameterSansDefault: 4) 
// parameterWithDefault est égale à 12

Placez les paramètres qui n'ont pas de valeurs par défaut au début de la liste des paramètres d'une fonction, avant les paramètres qui ont des valeurs par défaut. Les paramètres qui n'ont pas de valeurs par défaut sont généralement plus importants pour la signification de la fonction - les écrire en premier permet de reconnaître plus facilement que la même fonction est appelée, que des paramètres par défaut soient omis ou non.

Paramètre variadique

Un paramètre variadique accepte zéro ou plusieurs valeurs d'un type spécifié. Vous utilisez un paramètre variadique pour spécifier que le paramètre peut recevoir un nombre variable de valeurs d'entrées lorsque la fonction est appelée. Ecrivez des paramètres variadiques en insérant trois caractères de point ( ...) après le nom du type de paramètre.

Les valeurs passées à un paramètre variadique sont rendues disponibles dans le corps de la fonction sous la forme d'un tableau Array du type approprié.


func arithmeticMean(_ numbers: Double...) -> Double {
    var total: Double = 0
    for number in numbers {
        total += number
    }
    return total / Double(numbers.count)
}

arithmeticMean(1, 2, 3, 4, 5)  
// la moyenne est de 3.0

Paramètre InOut

Les paramètres de fonction sont des constantes par défaut. Tenter de modifier la valeur d'un paramètre de fonction à partir du corps de cette fonction entraîne une erreur de compilation.

Cela signifie que vous ne pouvez pas modifier la valeur d'un paramètre par erreur. Si vous souhaitez qu'une fonction modifie la valeur d'un paramètre et que vous souhaitez que ces modifications persistent une fois l'appel de fonction terminé, définissez ce paramètre comme paramètre inout.

Vous écrivez un paramètre entrée-sortie en plaçant le mot- clé inout juste avant le type d'un paramètre. Un paramètre in-out (entrée-sortie) a une valeur qui est transmise à la fonction, qui est modifiée par la fonction et qui est renvoyée hors de la fonction pour remplacer la valeur d'origine.


func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}
// Échange les valeurs de a en b, et de b en a
Un paramètre inout a une valeur qui est transmise à la fonction, est modifiée par la fonction, et est chargée par la fonction de remplacer la valeur d'origine.

Vous placez une esperluette & directement avant le nom d'une variable lorsque vous la transmettez comme argument à un paramètre inout (entrée-sortie), pour indiquer qu'elle peut être modifiée par la fonction.


var someInt = 3, anotherInt = 107

swapTwoInts(&someInt, &anotherInt)

print("someInt = \(someInt), et anotherInt = \(anotherInt)")
// someInt = 107, et anotherInt= 3

Types de fonctions


func addTwoInts(_ a: Int, _ b: Int) -> Int {
    return a + b
}

Le type de cette fonction est : (Int, Int) -> Int, qui se traduit par : "Une fonction avec deux paramètres de type Int et qui renvoie une valeur de type Int"


func printHelloWorld() {
    print("hello, world")
}

Le type de cette fonction est () -> Void, ou "une fonction qui n'a pas de paramètre et qui renvoie Void."

Vous utilisez des types de fonctions comme tous les autres types dans Swift. Par exemple, vous pouvez définir une constante ou une variable comme étant un type de fonction et affecter une fonction appropriée à cette variable :


var mathFunction: (Int, Int) -> Int = addTwoInts

Définissez une variable appelée mathFunction, qui a un type de fonction qui prend deux valeurs Int et renvoie une valeur Int. Définissez cette nouvelle variable pour faire référence à la fonction appelée addTwoInts.

Vous pouvez maintenant appeler la fonction attribuée avec le nom mathFunction.


print("Resultat : \(mathFunction(2, 3))")
// "Resultat : 5"

Comme pour tout autre type, vous pouvez laisser à Swift le soin de déduire le type de fonction lorsque vous affectez une fonction à une constante ou une variable :


let anotherMathFunction = addTwoInts
// anotherMathFunction est supposé être de type (Int, Int) -> Int

Types de fonction en tant que types de paramètres

On peut utiliser un type de fonction tel qu'un type de paramètre pour une autre fonction :


func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
    print("Resultat: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)
// "Resultat: 8"

Cet exemple définit une fonction appelée printMathResult(_:_:_:), qui a trois paramètres. Le premier paramètre est appelé mathFunction, et est de type (Int, Int) -> Int. Vous pouvez passer n'importe quelle fonction de ce type comme argument pour ce premier paramètre.

Les deuxième et troisième paramètres sont appelés a et b, et sont tous deux de type Int. Celles-ci sont utilisées comme les deux valeurs d'entrée pour la fonction mathématique fournie.

Types de fonction comme types de retour

Vous pouvez utiliser un type de fonction comme type de retour d'une autre fonction. Pour ce faire, écrivez un type de fonction complet immédiatement après la flèche de retour ( ->) de la fonction renvoyée.

L'exemple suivant définit deux fonctions simples appelées stepForward(_:) et stepBackward(_:). La fonction stepForward(_:) renvoie une valeur un de plus que sa valeur d'entrée et la fonction stepBackward(_:) renvoie une valeur de moins un que sa valeur d'entrée. Les deux fonctions ont un type de (Int) -> Int


// Incrémentation : +1
func stepForward(_ input: Int) -> Int {
    return input + 1
}

// Décrémentation : -1
func stepBackward(_ input: Int) -> Int {
    return input - 1
}

La fonction chooseStepFunction(backward:) a pour type de retour (Int) -> Int . La fonction chooseStepFunction(backward:) renvoie la fonction stepForward(_:) ou la fonction stepBackward(_:) basée sur un paramètre booléen appelé backward.


func chooseStepFunction(backward: Bool) -> (Int) -> Int {
    return backward ? stepBackward : stepForward
}

Vous pouvez maintenant utiliser chooseStepFunction(backward:) pour obtenir une fonction qui va avancer dans un sens ou dans l'autre :


var currentValue = 3
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero fait maintenant référence à la fonction stepBackward()

L'exemple ci-dessus détermine si une étape positive ou négative est nécessaire pour que currentValue se rapproche progressivement de zéro. currentValue a une valeur initiale de 3.


print("Compte à rebours :")
// Compte à rebours :
while currentValue != 0 {
    print("\(currentValue)... ")
    currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// 3...
// 2...
// 1...
// zero!

Fonction imbriquée ou nested

Toutes les fonctions que vous avez rencontré précédemment sont des fonctions globales, qui sont définies à une portée globale. Vous pouvez également définir des fonctions à l'intérieur du corps d'autres fonctions, appelées fonctions imbriquées ou nested .

Les fonctions imbriquées sont cachées du monde extérieur par défaut, mais peuvent toujours être appelées et utilisées par leur fonction englobante. Une fonction englobante peut également renvoyer l'une de ses fonctions imbriquées pour permettre à la fonction imbriquée d'être utilisée dans une autre portée.


func chooseStepFunction(backward: Bool) -> (Int) -> Int {
    func stepForward(input: Int) -> Int { return input + 1 }
    func stepBackward(input: Int) -> Int { return input - 1 }
    return backward ? stepBackward : stepForward
}
var currentValue = -4
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero = fonction imbriquée stepForward()
while currentValue != 0 {
    print("\(currentValue)... ")
    currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// -4..., -3..., -2..., -1..., zero!

La condition guard

Une instruction guard est utilisée pour transférer le contrôle du programme hors d'une portée si une ou plusieurs conditions ne sont pas remplies. La clause Else est obligatoire lors d'une condition guard.

L'instruction guard en Swift est utilisée pour éviter les valeurs nulles (ou optionnelles) dans un bloc de code. Elle permet de vérifier si une condition est vraie, et si ce n'est pas le cas, de quitter immédiatement le bloc de code en cours d'exécution. Cela peut être utile pour gérer les erreurs ou les cas d'utilisation où une valeur doit être présente pour que le code puisse continuer à s'exécuter.

La syntaxe de base de l'instruction guard est la suivante :


guard condition else {
    // code à exécuter si la condition est fausse
    return
}

La condition peut être n'importe quelle expression qui renvoie un booléen. Par exemple :


func authenticate(username: String?, password: String?) {
    guard let username = username, let password = password else {
        print("Veuillez entrer un nom d'utilisateur et un mot de passe valides.")
        return
    }
    // code pour l'authentification
}

Dans cet exemple, si username ou password est nil, le message "Veuillez entrer un nom d'utilisateur et un mot de passe valides." sera affiché et la fonction quittera. Sinon, le code pour l'authentification sera exécuté.

Il est également possible de définir une constante ou une variable à partir de la valeur optionnelle pour l'utiliser dans le bloc de code :


func calculateSum(numbers: [Int]?) -> Int {
    guard let numbers = numbers, !numbers.isEmpty else {
        return 0
    }
    return numbers.reduce(0, +)
}

Dans cet exemple, si numbers est nil ou vide, la fonction retourne 0 sans exécuter le reste du code. Sinon, elle utilise la valeur déballée de numbers pour calculer la somme des éléments du tableau.

Il est également possible d'utiliser guard let avec des structures de contrôle plus complexes pour vérifier plusieurs conditions.


func printGrade(score: Int) {
    guard score >= 90 else {
        print("Désolé, vous avez échoué.")
        return
    }
    guard score > 100 else {
        print("Score impossible.")
        return
    }
    print("Félicitations, vous avez réussi avec un score de \(score).")
}

Dans cet exemple, si le score est inférieur à 90, le message "Désolé, vous avez échoué." sera affiché et la fonction quittera. Si le score est supérieur à 100, le message "Score impossible." sera affiché.

A ce stade du tutoriel, vous n'avez peut-être pas tout compris concernant la condition GUARD. Ce n'est pas grave, il s'agit simplement d'un principe qu'il est bon de connaitre. Nous vous détaillerons son fonctionnement des les chapitres suivant.

Return

L'instruction return en Swift permet de retourner une valeur d'une fonction et d'arrêter son exécution. Une fois atteinte, le reste du code dans la fonction n'est plus exécuté et le contrôle est renvoyé au code appelant la fonction.


func addTwoNumbers(a: Int, b: Int) -> Int {
    return a + b
}

let result = addTwoNumbers(a: 2, b: 3)
print(result)
// Cela va afficher : 5

Dans ce code, la fonction addTwoNumbers prend en entrée deux nombres entiers et retourne leur somme. Lorsque l'instruction return a + b est atteinte, la fonction s'arrête et renvoie la somme des nombres à la constante result. La valeur de result sera alors imprimée à l'écran, soit 5. Nous allons découvrir les fonctions dans le prochain chapitre.

Condition Guard

Une instruction guard, comme une instruction if, exécute des instructions en fonction de la valeur booléenne d'une expression. Vous utilisez une instruction guard pour exiger qu'une condition soit vraie pour que le code après l'instruction guard soit exécuté. Contrairement à une instruction if, une instruction guard a toujours une clause else.


func greet(person: [String: String]) {
    guard let name = person["name"] else {
        return
    }

    print("Bonjour \(name)!")

    guard let location = person["location"] else {
        print("J'espere qu'il fait beau vers chez toi.")
        return
    }

    print("J'espere qu'il fait beau à \(location).")
}

greet(person: ["name": "Jean"])
// "Bonjour Jean!"
// "J'espere qu'il fait beau vers chez toi."
greet(person: ["name": "Jeanne", "location": "Toulouse"])
// "Bonjour Jeanne!"
// "J'espere qu'il fait beau à Toulouse."

L'instruction return permet de quitter la fonction et de ne pas exécuter le code qui suit à l'interieur de celle-ci. Dans l'exemple ci-dessus, si la constante name ne peut être créée, alors la fonction s'arrête et la lecture du code reprend là ou elle a été appelée.Il en est de même pour la constante location. Cela permet de quitter la fonction à la lecture de la constante location (si elle existe) et de ne pas executer la derniere ligne de code qui afficherait une deuxieme phrase en rapport avec la localisation.