Menu
Coddy logo textTech

Initialiseurs pouvant échouer

Fait partie de la section Programmation orientée objet du Journey Swift de Coddy. Leçon 20 sur 57.

Certaines valeurs ne peuvent pas être construites à partir de toutes les entrées. Un initialiseur pouvant échouer, écrit init?, renvoie nil au lieu d’une instance lorsque l’entrée n’est pas valide, de sorte qu’aucune instance invalide ne puisse jamais exister :

struct Percentage {
    let value: Int

    init?(_ value: Int) {
        guard value >= 0, value <= 100 else { return nil }
        self.value = value
    }
}

print(Percentage(42)?.value ?? -1)
print(Percentage(150)?.value ?? -1)

Résultat :

42
-1

Le résultat d’un initialiseur susceptible d’échouer est une valeur optionnelle ; l’appelant doit donc la déballer, généralement avec if let ou guard let :

struct Email {
    let address: String

    init?(_ text: String) {
        let parts = text.split(separator: "@")
        guard parts.count == 2, parts[1].contains(".") else { return nil }
        address = text.lowercased()
    }
}

for text in ["Ada@Mail.com", "not-an-email"] {
    if let email = Email(text) {
        print("valid: \(email.address)")
    } else {
        print("invalid: \(text)")
    }
}

Sortie :

valid: ada@mail.com
invalid: not-an-email

Les propres types de Swift utilisent la même idée : Int("42") et Double("x") sont des initialiseurs pouvant échouer, tout comme init(rawValue:) d'une énumération. Un initialiseur pouvant échouer peut les appeler et transmettre l'échec :

struct Coordinate {
    let lat: Double
    let lon: Double

    init?(text: String) {
        let parts = text.split(separator: ",")
        guard parts.count == 2,
              let lat = Double(parts[0]), let lon = Double(parts[1]),
              abs(lat) <= 90, abs(lon) <= 180 else { return nil }
        self.lat = lat
        self.lon = lon
    }
}

print(Coordinate(text: "51.5,-0.1") != nil)
print(Coordinate(text: "95,10") != nil)

Résultat :

true
false

Utilisez init? lorsqu’une valeur manquante constitue une information suffisante. Utilisez un initialiseur lançant une erreur, init(...) throws, lorsque l’appelant doit savoir quelle règle a échoué :

struct Username {
    let text: String
    init(_ text: String) throws {
        guard text.count >= 3 else { throw NameError.tooShort }
        guard text.allSatisfy({ $0.isLetter }) else { throw NameError.badCharacters }
        self.text = text
    }
}
challenge icon

Défi

Facile

Complétez la structure Rgb, qui stocke une couleur sous la forme de red, green et blue compris entre 0 et 255. Son initialiseur pouvant échouer init?(hex:) lit un texte tel que #FF8000 : il renvoie nil sauf si le texte est constitué de # suivi exactement de six chiffres hexadécimaux. Utilisez Int(text, radix: 16) pour chaque paire. Ajoutez une label calculée qui renvoie rgb(255, 128, 0).

Le code fourni lit une couleur par ligne et affiche le libellé ou invalid: text.

Votre code doit être placé dans Rgb.swift. main.swift contient le code d’entrée/sortie fourni et ne peut pas être modifié.

Essayez vous-même

// Code d'entrée/sortie fourni : le garder tel quel
var input: [String] = []
while let line = readLine() { input.append(line) }
for line in input {
    if let color = Rgb(hex: line) {
        print(color.label)
    } else {
        print("invalid: \(line)")
    }
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation orientée objet

Entraînez-vous par vous-même : Playground Swift