web-dev-qa-db-fra.com

Comment définir des méthodes optionnelles dans le protocole Swift?

Est-ce possible à Swift? Si non, existe-t-il une solution de contournement?

303
Selvin

Puisqu'il y a quelques réponses sur la façon d'utiliser optional modificateur et l'attribut @objc pour définir le protocole d'exigence facultatif, je vais donner un exemple de l'utilisation des extensions de protocole pour définir le protocole facultatif.

Le code ci-dessous est Swift 3. *.

/// Protocol has empty default implementation of the following methods making them optional to implement:
/// `cancel()`
protocol Cancelable {

    /// default implementation is empty.
    func cancel()
}

extension Cancelable {

    func cancel() {}
}

class Plane: Cancelable {
  //Since cancel() have default implementation, that is optional to class Plane
}

let plane = Plane()
plane.cancel()
// Print out *United Airlines can't cancelable*

Veuillez noter que les méthodes d'extension de protocole ne peuvent pas être invoquées par le code Objective-C, et le pire est que l'équipe de Swift ne le réglera pas. https://bugs.Swift.org/browse/SR-492

29
Eddie.Dou

Si vous souhaitez utiliser des méthodes facultatives, vous devez marquez votre protocole avec l'attribut @objc:

@objc protocol MyProtocol {

    @objc optional func doSomething()

}

class MyClass : MyProtocol {

    // no error

}

Sinon, les protocoles Swift se comportent comme des interfaces sur d'autres langages OO, où toutes les méthodes définies dans les interfaces doivent être implémentées.

404
akashivskyy

À partir de Swift 2, il est possible d’ajouter des implémentations par défaut d’un protocole. Cela crée une nouvelle méthode de méthodes facultatives dans les protocoles. 

protocol MyProtocol {
    func doSomethingNonOptionalMethod()
    func doSomethingOptionalMethod()
}

extension MyProtocol {
    func doSomethingOptionalMethod(){ 
        // leaving this empty 
    }
}

Ce n'est pas vraiment un moyen agréable de créer des méthodes de protocole facultatives, mais vous donne la possibilité d'utiliser des structures dans les rappels de protocole.

J'ai écrit un petit résumé ici: https://www.avanderlee.com/Swift-2-0/optional-protocol-methods/

370
Antoine

Les autres réponses impliquent ici de marquer le protocole comme "@objc" ne fonctionnent pas lorsqu’on utilise des types spécifiques à Swift. 

struct Info {
    var height: Int
    var weight: Int
} 

@objc protocol Health {
    func isInfoHealthy(info: Info) -> Bool
} 
//Error "Method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C"

Pour déclarer des protocoles optionnels qui fonctionnent bien avec Swift, déclarez les fonctions en tant que variables au lieu de func. 

protocol Health {
    var isInfoHealthy: (Info) -> (Bool)? { get set }
}

Et puis implémenter le protocole comme suit

class Human: Health {
    var isInfoHealthy: (Info) -> (Bool)? = { info in
        if info.weight < 200 && info.height > 72 {
            return true
        }
        return false
    }
    //Or leave out the implementation and declare it as:  
    //var isInfoHealthy: (Info) -> (Bool)?
}

Vous pouvez ensuite utiliser "?" vérifier si la fonction a été implémentée ou non

func returnEntity() -> Health {
    return Human()
}

var anEntity: Health = returnEntity()

var isHealthy = anEntity.isInfoHealthy(Info(height: 75, weight: 150))? 
//"isHealthy" is true
32
Zag

Dans Swift 3.0

@objc protocol CounterDataSource {
    @objc optional func increment(forCount count: Int) -> Int
    @objc optional var fixedIncrement: Int { get }
}

Cela vous fera gagner du temps.

31
Gautam Sareriya

Voici un exemple concret avec le motif de délégation.

Configurez votre protocole:

@objc protocol MyProtocol:class
{
    func requiredMethod()
    optional func optionalMethod()
}

class MyClass: NSObject
{
    weak var delegate:MyProtocol?

    func callDelegate()
    {
        delegate?.requiredMethod()
        delegate?.optionalMethod?()
    }
}

Définissez le délégué sur une classe et implémentez le protocole. Voir que la méthode optionnelle n'a pas besoin d'être implémentée.

class AnotherClass: NSObject, MyProtocol
{
    init()
    {
        super.init()

        let myInstance = MyClass()
        myInstance.delegate = self
    }

    func requiredMethod()
    {
    }
}

Une chose importante est que la méthode optionnelle est optionnelle et nécessite un "?" en appelant. Mentionnez le deuxième point d'interrogation.

delegate?.optionalMethod?()
31
Raphael
  • Vous devez ajouter le mot clé optional avant chaque méthode. 
  • Notez cependant que pour que cela fonctionne, votre protocole doit être marqué avec l’attribut @objc.
  • Cela implique en outre que ce protocole serait applicable aux classes mais pas aux structures.
26
Mehul Parmar

Une approche purement Swift avec héritage de protocole:

//Required methods
protocol MyProtocol {
    func foo()
}

//Optional methods
protocol MyExtendedProtocol: MyProtocol {
    func bar()
}

class MyClass {
    var delegate: MyProtocol
    func myMethod() {
        (delegate as? MyExtendedProtocol).bar()
    }
}
12
guenis

Pour illustrer les mécanismes de la réponse d'Antoine: 

protocol SomeProtocol {
    func aMethod()
}

extension SomeProtocol {
    func aMethod() {
        print("extensionImplementation")
    }
}

class protocolImplementingObject: SomeProtocol {

}

class protocolImplementingMethodOverridingObject: SomeProtocol {
    func aMethod() {
        print("classImplementation")
    }
}

let noOverride = protocolImplementingObject()
let override = protocolImplementingMethodOverridingObject()

noOverride.aMethod() //prints "extensionImplementation"
override.aMethod() //prints "classImplementation"
10
eLillie

Je pense qu'avant de demander comment vous pouvez implémenter une méthode de protocole optionnelle, vous devriez demander pourquoi vous devriez en implémenter un. 

Si nous considérons les protocoles Swift comme une interface dans la programmation orientée objet classique, les méthodes optionnelles n’ont pas beaucoup de sens et une meilleure solution consisterait peut-être à créer une implémentation par défaut ou à séparer le protocole en un ensemble de avec quelques relations d’héritage entre eux) pour représenter la combinaison possible de méthodes dans le protocole.

Pour en savoir plus, voir https://useyourloaf.com/blog/Swift-optional-protocol-methods/ , qui donne un excellent aperçu de cette question.

6
Eran Marom

Un peu en dehors du sujet de la question initiale, mais cela fonde l'idée d'Antoine et j'ai pensé que cela pourrait aider quelqu'un. 

Vous pouvez également rendre les propriétés calculées facultatives pour les structures avec des extensions de protocole.

Vous pouvez rendre une propriété sur le protocole facultative

protocol SomeProtocol {
    var required: String { get }
    var optional: String? { get }
}

Implémenter la propriété factice calculée dans l'extension de protocole

extension SomeProtocol {
    var optional: String? { return nil }
}

Et maintenant, vous pouvez utiliser des structures qui ont ou non la propriété optionnelle implémentée

struct ConformsWithoutOptional {
    let required: String
}

struct ConformsWithOptional {
    let required: String
    let optional: String?
}

J’ai également expliqué comment utiliser les propriétés facultatives dans les protocoles Swift sur mon blog , que je tiendrai à jour au cas où quelque chose changerait dans les versions de Swift 2.

6
matthewpalmer

si vous voulez le faire en Swift pur, le meilleur moyen est de fournir un élément d'implémentation par défaut si vous renvoyez un type Swift comme par exemple struct avec des types Swift

exemple :

struct magicDatas {
    var damagePoints : Int?
    var manaPoints : Int?
}

protocol magicCastDelegate {
    func castFire() -> magicDatas
    func castIce() -> magicDatas
}

extension magicCastDelegate {
    func castFire() -> magicDatas {
        return magicDatas()
    }

    func castIce() -> magicDatas {
        return magicDatas()
    }
}

alors vous pouvez implémenter le protocole sans définir chaque func

3
p1ckl3

Voici un exemple très simple pour Swift Classes SEULEMENT, et non pour les structures ou les énumérations . Notez que la méthode du protocole étant optionnelle, elle comporte deux niveaux d’enchaînement facultatif. De plus, la classe adoptant le protocole a besoin de l'attribut @objc dans sa déclaration.

@objc protocol CollectionOfDataDelegate{
   optional func indexDidChange(index: Int)
}


@objc class RootView: CollectionOfDataDelegate{

    var data = CollectionOfData()

   init(){
      data.delegate = self
      data.indexIsNow()
   }

  func indexDidChange(index: Int) {
      println("The index is currently: \(index)")
  }

}

class CollectionOfData{
    var index : Int?
    weak var delegate : CollectionOfDataDelegate?

   func indexIsNow(){
      index = 23
      delegate?.indexDidChange?(index!)
    }

 }
3
Blessing Lopes

Comment créer des méthodes de délégation facultatives et obligatoires

@objc protocol InterViewDelegate:class {

    @objc optional func optfunc()  //    This is optional
    func requiredfunc()//     This is required 

}
0
Saurabh Kumar Sharma