Imagines que tu sois un développeur qui travaille sur un projet Swift. Tu auras besoin de définir un ensemble de fonctionnalités communes pour plusieurs types de données. Comment pourrais-tu le faire de manière efficace ? Swift a une solution pour cela : les protocoles !

En programmation, un protocole est une spécification abstraite d’un ensemble de méthodes, de propriétés ou de tout autre comportement qu’une classe, une structure ou un objet peut implémenter.

En utilisant des protocoles, il est possible de définir des types personnalisés. Ceux-ci peuvent avoir des fonctionnalités communes, mais qui n’ont pas nécessairement de lien d’héritage direct.

Différence avec une interface

Si tu as déjà fait un peu de programmation, tu dois connaître les interfaces. Eh bien, les protocoles sont assez similaires.

Une interface défini un ensemble de méthodes qui pourront être utilisées par les objets. Mais un protocole peut également définir des propriétés qui devront être implémentées. Il peut aussi modifier les valeurs/références en utilisant le mot clé mutating (car les protocoles peuvent être mis en œuvre par des structures, des énumérations ou des classes). Les protocoles peuvent aussi être combinés.

En détail

On définit un protocole comme une classe, une énumération ou une structure :

protocol FirstProtocol {
    // définir le protocole
}

Lorsque l’on défini des propriétés à un protocole, il convient de définir si la propriété peut être seulement accessible (getter) ou accessible et remplaçable (getter & setter) :

protocol FirstProtocol {
    var firstProperty: Int { get set }
}

Les propriétés de Type (objet) doivent toujours être précédées du mot clé static.

Par exemple : static var Animal: String { get }

Avec une structure

Voici un exemple pour compléter le chapitre sur les structures.

struct SomeStruct: FirstProtocol {
    var firstProperty: Int
}

let example = SomeStruct(firstProperty: 2)
// example.firstProperty donne 2

Avec une classe

Si la classe a une super-classe, elle doit être définie avant le protocole.

class FirstClass: SomeSuperclass, FirstProtocol {
    // définir la classe

    required init(someParameter: Int) {
        // définir l'initialiseur
    }
}

Pour rendre obligatoire la déclaration de l’initialiseur, on utilise required.

En effet, on pourrait avoir besoin de garantir que toutes les sous-classes ou les structures qui implémentent la classe (ou le protocole) doivent implémenter un initialisateur particulier.

Il n’est pas nécessaire d’utiliser required sur les classes qui sont marquées avec le modificateur final, car les classes finales ne peuvent pas être sous-classées (voir documentation officielle).

Déclarer les méthodes

Comme pour les propriétés, il faut toujours préfixer la déclaration de méthode avec le mot clé static, si elle est utilisée par une classe :

protocol SomeProtocol {
    static func someTypeMethod()
}

// ou

protocol RandomNumberGenerator {
    func random() -> Double
}

Traiter les références avec Mutating

Comme évoqué dans la présentation, il est parfois nécessaire qu’une méthode modifie (ou mute) l’instance à laquelle elle appartient. Pour les méthodes d’instance sur les types de valeur (c’est-à-dire les structures et les énumérations), il faudra placer le mot-clé mutating pour indiquer que la méthode est autorisée à modifier l’instance à laquelle elle appartient et toutes les propriétés de cette instance.

protocol Togglable {
    mutating func toggle()
}

enum OnOffSwitch: Togglable {
    case off, on
    mutating func toggle() {
        switch self {
        case .off:
            self = .on
        case .on:
            self = .off
        }
    }
}
var lightSwitch = OnOffSwitch.off
lightSwitch.toggle()
// lightSwitch est maintenant égal à .on

Dans cet exemple, il est évident que nous avons besoin de modifier la valeur de toggle() afin de pouvoir changer la valeur de l’interrupteur de on à off. On utilise donc le mot clé mutating.

Utilisé comme un Type

Cela signifie que tu peux déclarer des variables, constantes ou des arguments de fonction en utilisant un protocole comme type plutôt qu’une classe ou une structure concrète.

protocol Animal {
   var name: String { get }
   func makeSound()
}

class Dog: Animal {
   var name: String
   init(name: String) {
      self.name = name
   }
   func makeSound() {
      print("Woof!")
   }
}

class Cat: Animal {
   var name: String
   init(name: String) {
      self.name = name
   }
   func makeSound() {
      print("Meow!")
   }
}

let dog = Dog(name: "Fido")
let cat = Cat(name: "Fluffy")

var myAnimal: Animal
myAnimal = dog
myAnimal.makeSound() // "Woof!"

myAnimal = cat
myAnimal.makeSound() // "Meow!"

L’utilisation des protocoles permet de rendre notre code plus générique et de faciliter l’extension de notre programme en ajoutant de nouvelles classes qui implémentent le protocole Animal.

Ajouter une extension au protocole

Elle permet d’ajouter des méthodes ou des propriétés à un protocole existant. Cela est utile lorsque nous souhaitons étendre les fonctionnalités d’un protocole sans avoir à modifier sa définition.

protocol MyProtocol {
   func myMethod()
}

extension MyProtocol {
   func myMethod() {
      print("Implémentation par défaut")
   }
}

struct MyStruct: MyProtocol {
   // Pas besoin d'implémenter myMethod
}

class MyClass: MyProtocol {
   // Implémentation de myMethod optionnelle
}

let myStruct = MyStruct()
myStruct.myMethod() // Affiche "Implémentation par défaut"

let myClass = MyClass()
myClass.myMethod() // Affiche "Implémentation par défaut"


J’espère que cet article t’aura donné envie d’essayer les protocoles ou de te réconcilier avec eux. Ce sont des outils très puissants donc n’hésites pas à les prendre en main. J’ai moi-même fait du Java et ils ne me paraissent pas encore si évidents à utiliser… Mais je suis sûre qu’ils deviendront incontournables dans très peu de temps.

Pour un peu plus de documentation, je te laisse le lien de celle d’Apple. Il y a également l’article encore une fois très complet de Paul Hudson de Hacking with Swift.

Dis nous en commentaires comment tu veux utiliser les protocoles !

Si tu as aimé cet article, partage le 🫶