• 30 hours
  • Medium

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 11/2/23

Téléchargez les questions

Nous avons maintenant une structure Question et nous allons donc pouvoir créer notre classe Game. Contrairement à la structure Question, cette classe va contenir beaucoup de logique. C'est la raison pour laquelle, nous allons choisir une classe plutôt qu'une structure.

Exercice : Création d’une classe Game

Et on commence très fort ce chapitre avec déjà un exercice ! En effet, vous savez très bien créer des classes, donc je vous propose de la créer sans moi dans cet exercice. Faites-le vraiment sinon vous risquez d'être perdus lorsqu'on devra utiliser notre classe Game.

Une fois l'exercice terminé, vous pouvez télécharger le fichier Game.swift et le glisser dans votre modèle (c'est la même opération que ce qu'on a fait avec la police Balham).

QuestionManager

Notre quizz va charger ses questions depuis internet. Plus précisément, nous allons charger nos questions depuis la base de données de questions gratuites : Open Triva Database. Les requêtes réseau ne sont pas au programme de ce chapitre donc je l'ai fait pour vous. Vous pouvez donc télécharger le fichier : QuestionManager.swift. Vous pouvez ensuite glisser ce fichier dans votre modèle.

Ce fichier est un peu compliqué et nous n'allons pas rentrer dans le détail de ce qu'il fait. Sachez seulement qu'il récupère 10 questions dans la base de données, les formatte et les renvoie. Il le fait via la seule méthode publique de cette classe qui a pour signature :

get(completionHandler: @escaping ([Question]) -> ())

Et c'est cette méthode que nous allons utiliser pour finaliser la méthode refresh de notre classe Game.

Doucement mon garçon. Tu veux vraiment qu'on utilise cette méthode ?

Oui, pourquoi ?

Tu as vu la tête du machin ?!!

Bon OK, ça peut paraître un peu impressionnant, mais rassurez-vous. À la fin de chapitre, cela n'aura plus de secret pour vous.

D'ailleurs vous en comprenez déjà une bonne partie. Nous avons une fonction qui s'appelle get et qui admet un paramètre completionHandler.

Il nous reste à voir ensemble ce que veut dire : @escaping ([Question]) -> (). Mettons le @escaping de côté pour le moment et intéressons-nous à ([Question]) -> ().

Le type fonction

Ceci : ([Question]) -> () est un type un peu particulier : le type fonction. Oui, les fonctions sont un type. De toute façon, tout est type en Swift !

Le type fonction a une syntaxe un peu particulière. On écrit d'abord entre parenthèses, les types des paramètres puis une flèche puis le type de retour. Prenons quelques exemples de fonctions et tâchons de trouver leur type.

func double(a: Int) -> Int { (...) } // (Int) -> Int
func multiplie(a: Int, b: Int) -> Int { (...) } // (Int, Int) -> Int
func envoyer(message: String) -> Bool { (...) } // (String) -> Bool
func annuler() { (...) } // () -> ()
func annuler() { (...) } // () -> Void
func saluer(personne: String) // (String) -> ()

Ce n'est donc pas plus compliqué que ça ! Donc si on fait le travail inverse : le type ([Question]) -> () décrit une fonction qui prend en paramètre un tableau de questions et qui ne renvoie pas de valeur.

Le type question est donc un type et par conséquent il peut être utilisé n'importe où : comme type d'une variable, comme paramètre ou valeur de retour d'une fonction, comme type d'un tableau etc. Je vous propose de jouer un peu avec dans le Playground. Copiez les 4 fonctions suivantes :

func ajouteDeux(x: Int) -> Int {
    return x + 2
}

func multiplieParTrois(x: Int) -> Int {
    return x * 3
}

func soustraitQuatre(x: Int) -> Int {
    return x - 4
}

func multiplieParDeux(x: Int) -> Int {
    return x * 2
}

Ces quatre fonctions sont du même type : (Int) -> Int. Donc on peut créer un tableau qui les regroupe toutes :

var mesFonctions = [ajouteDeux(x:), multiplieParTrois(x:), soustraitQuatre(x:), multiplieParDeux(x:)]

Ce tableau est donc du type [(Int) -> Int]. Maintenant je peux, par exemple, faire une boucle for pour parcourir mon tableau :

var a = 2
for maFonction in mesFonctions {
    a = maFonction(a)
}

À chaque tour du tableau, c'est une fonction différente qui est utilisée et a vaut 16 à la fin.

Une autre façon de renvoyer une valeur de retour

Il y a une question que vous ne m'avez pas encore posée !

Pourquoi la fonction get de QuestionManager a une fonction en paramètre ?

Exactement ! Vous êtes impressionnant ;) !

Le rôle de la fonction get est juste de renvoyer un tableau de questions téléchargées sur internet.

Pourquoi elle ne ressemble pas juste à : func get() -> [Question]?

Excellente question ! En fait, cette fonction est un peu particulière, car on ne sait pas quand la valeur de retour va arriver. Cela va dépendre de la qualité du réseau et du temps de réponse du serveur etc. Autrement dit, dès qu'on va chercher des ressources sur internet, on est obligé d'attendre la réponse. Or une fonction avec valeur de retour s'exécute instantanément et donc les questions n'ont pas le temps d'être chargées.

Du coup à la place, on donne à l'utilisateur de la méthode get la possibilité d'exécuter une fonction lorsque le chargement des questions est terminé. On passe alors en paramètre de cette fonction à exécuter les questions que nous venons de charger.

Donc nous allons créer une méthode dans notre classe Game que l'on va appeler receiveQuestion dont le rôle va être de gérer les questions reçues. Cette fonction va être du type ([Question]) -> () :

private func receiveQuestions(_ questions: [Question]) {
    self.questions = questions
    state = .ongoing
}

Lorsqu'on reçoit les questions, on fait donc deux choses :

  1. Affecter les questions à la propriété questions de Game.

  2. Passer la propriété state de Game à la valeur ongoing. Les questions sont chargées, la partie peut reprendre.

Maintenant nous allons pouvoir passer cette nouvelle fonction en paramètre de la fonction get. Et nous allons faire cela dans notre méthode refresh.

func refresh() {
    score = 0
    currentIndex = 0
    state = .over

    QuestionManager.shared.get(completionHandler: receiveQuestions)
}

Ici, on appelle donc la méthode get du QuestionManager et on lui passe en paramètre la méthode receiveQuestions. Cela signifie que lorsque les questions sont chargées, la méthode receiveQuestions est appelée.

let game = Game()
game.refresh()

Ensuite, en ajoutant un print(questions) dans la méthode receiveQuestions, vous pouvez vérifier que tout fonctionne bien en lançant l'application.

Vous voyez ? Nous avons réussi à utiliser cette méthode get. Ce n'était pas si dur finalement ! Notre méthode refresh est maintenant complète et permet de remettre à zéro les paramètres de la partie, de charger les questions et de relancer la partie.

Exercice

1/ Trouvez les types des fonctions suivantes

func ajouterDeux(a: Int) -> Int { (...) }
func additioner(a: Int, b: Int) -> Int { (...) }
func envoyerMail(message: String, destinataire: String) -> Bool { (...) }
func cocherLaCase(aCoché: Bool) { (...) }
func verrouiller() { (...) }
func composerNumero(_ numero: Int) { (...) }

Vous pouvez trouver la correction ici.

2/ Utiliser les types fonctions

Dans cet exercice, on cherche à calculer la somme d'un tableau d'entier selon les règles suivantes :

  • si le nombre est pair, on le divise par deux avant de l'aditionner aux autres

  • si le nombre est impair, on ajoute un puis on le divise par deux avant de l'additioner aux autres

Le code suivant vous est déjà fourni :

func diviserNombrePairParDeux(x: Int) -> Int {
    return x / 2
}

func diviserNombreImpairParDeux(x: Int) -> Int {
    return (x + 1) / 2
}

func obtenirDivision(x: Int) -> (Int) -> (Int) {
    // complétez cette fonction
}

let tableau = [2, 12, 3, 14, 76, 19, 7, 22]
var somme = 0

for nombre in tableau {
    // complétez cette boucle
}

Vous devez compléter la fonction obtenirDivision. Cette fonction renvoie une des deux fonctions au dessus en fonction de la parité de son paramètre x. Vous noterez que le type de retour de cette fonction corresponds bien au type des deux fonctions du dessus. Ensuite, vous devez utiliser la fonction obtenirDivision pour compléter la boucle.

Une fois l'exercice terminé, vous pouvez aller lire la correction ici.

En résumé

  • Les fonctions sont des types, on appelle cela le type fonction.

  • Le type fonction a pour syntaxe : (TypeParam1, TypeParam1, TypeParam1) -> TypeRetour.

  • On peut utiliser les types fonction pour passer des fonctions en paramètre d'autres fonctions notamment.

Example of certificate of achievement
Example of certificate of achievement