web-dev-qa-db-fra.com

Comment ajouter une extension de chaîne optionnelle?

Vous pouvez créer une extension de chaîne comme ceci:

extension String {
   func someFunc -> Bool { ... }
}

mais que faire si vous voulez qu'il s'applique à une chaîne facultative?

var optionalString :String? = ""
optionalString!.someFunc() /* String? does not have a member someFunc */
47
vol7ron

Dans Swift 3.1, vous pouvez également ajouter une extension à des valeurs facultatives:

extension Optional where Wrapped == String {
  var isBlank: Bool {
    return self?.isBlank ?? true
  }
}
121

Extensions sur Optional qui retournent une String

À partir de Swift 3, vous ne pouvez pas contraindre directement une méthode d’extension à une variable String facultative. Vous pouvez obtenir un résultat équivalent avec des protocoles, comme l'explique la réponse de Daniel Shin.

Vous pouvez cependant créer une méthode d'extension sur un type facultatif Facultatif et j'ai trouvé des méthodes utiles qui ont une valeur de retour String. Ces extensions sont utiles pour consigner des valeurs dans la console. J'ai utilisé asStringOrEmpty () sur un String facultatif lorsque je souhaite remplacer un nil possible par une chaîne vide.

extension Optional {
    func asStringOrEmpty() -> String {
        switch self {
            case .some(let value):
                return String(describing: value)
            case _:
                return ""
        }
    }

    func asStringOrNilText() -> String {
        switch self {
            case .some(let value):
                return String(describing: value)
            case _:
                return "(nil)"
        }
    }
}

Exemple d'utilisation:

var booleanValue: Bool?
var stringValue: String?
var intValue: Int?

print("booleanValue: \(booleanValue.asStringOrNilText())")
print("stringValue: \(stringValue.asStringOrNilText())")
print("intValue: \(intValue.asStringOrNilText())")

booleanValue = true
stringValue = "text!"
intValue = 41

print("booleanValue: \(booleanValue.asStringOrNilText())")
print("stringValue: \(stringValue.asStringOrNilText())")
print("intValue: \(intValue.asStringOrNilText())")

Sortie de la console:

booleanValue: (nil)
stringValue: (nil)
intValue: (nil)

booleanValue: true
stringValue: text!
intValue: 41

Optional différent du pointeur nul

Ces extensions illustrent le fait qu'une Optional est différente d'un pointeur nil. Optional est une enum d'un type spécifié (Wrapped) indiquant qu'elle contient ou non une valeur. Vous pouvez écrire une extension sur le "conteneur" Optional même s'il ne contient pas de valeur.

Extrait de la déclaration facultative de Swift

enum Optional<Wrapped> : ExpressibleByNilLiteral {

    /// The absence of a value.
    case none

    /// The presence of a value, stored as `Wrapped`.
    case some(Wrapped)

    ...
}

Dans le code, l'absence de valeur est généralement écrite à l'aide du littéral nil plutôt que du cas d'énumération explicite .none.

6
Mobile Dan
extension Optional where Wrapped == String {
var isNil: Bool {
    return self == nil
}

La réponse ci-dessus (écrite par @Vlad Hatko) fonctionne bien, mais dans Swift 4, certains problèmes ont été résolus.

3
dheeru

Mise à jour: Pour une solution de contournement qui fonctionne avec Swift 2 et supérieur, voir Réponse de Daniel Shin


Une chaîne facultative n'est pas en elle-même un type et vous ne pouvez donc pas créer une extension sur un type facultatif. Dans Swift, une Optional est simplement une énumération (plus un peu de sucre syntaxique) qui peut être soit None, soit Some qui englobe une valeur. Pour utiliser votre méthode String, vous devez décompresser votre optionalString. Vous pouvez facilement utiliser le chaînage optionnel pour réaliser ceci:

optionalString?.someFunc()

Si optionalString n'est pas nil, someFunc sera appelé. Une autre solution (moins concise) consiste à utiliser une liaison facultative pour déterminer si optionalString a ou non une valeur avant d'essayer d'appeler la méthode:

if let string = optionalString {
    string.someFunc()    // `string` is now of type `String` (not `String?`)
}

Dans votre exemple tiré des commentaires ci-dessous, vous n'avez pas besoin d'imbriquer plusieurs instructions if. Vous pouvez vérifier si la chaîne facultative est une chaîne vide dans une seule variable if:

if optionalString?.isEmpty == true {
    doSomething()
}

Cela fonctionne parce que l'expression optionalString?.isEmpty renvoie un booléen facultatif (c'est-à-dire true, false ou nil). Donc, doSomething() ne sera appelé que si optionalString est nonnil, et _ si cette chaîne est vide.

Une autre alternative serait:

if let string = optionalString where string.isEmpty {
    doSomethingWithEmptyString(string)
}
2
Stuart

Dans Swift 4.1, j'avais une erreur de compilation Optional is ambiguous for type lookup in this context. Pour résoudre ce problème, vous devez explicitement ajouter l'espace de noms Swift au type:

extension Swift.Optional where Wrapped == String {
    var isBlank: Bool {
        return self?.isBlank ?? true
    }
}
2
caukajun

Depuis Xcode 9.3, vous pouvez utiliser cette légère modification de la réponse de @ Vladyslav:

extension Optional where Wrapped == String {

    var isEmpty: Bool {
        return self?.isEmpty ?? true
    }

}
2
zero3nna

trouvé une astuce Swift 3

class A{
    var name:String!;
    init(_ name:String?){
        self.name = name;
    }
}

extension Optional where Wrapped == String {
    func compareText(_ other:String?)->Bool{
        switch (self,other){
        case let(a?,b?):
            return a < b;
        case (nil,_):
            return true;
        default:
            return false;
        }
    }
}

let words:[A] = [A("a"),A(nil),A("b"),A("c"),A(nil)];

// let sorted = words.sorted{ 0.name.compareText($1.name) }
// trick
let sorted = words.sorted{ ($0.name as String?).compareText($1.name) }

print(sorted.map{$0.name});
1
john07