Menu
Coddy logo textTech

Inizializzatori che possono fallire

Fa parte della sezione Programmazione orientata agli oggetti del percorso Swift di Coddy. Lezione 20 di 57.

Alcuni valori non possono essere creati a partire da ogni input. Un inizializzatore fallibile, scritto init?, restituisce nil invece di un'istanza quando l'input non è valido, quindi non può mai esistere un'istanza non valida:

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)

Output:

42
-1

Il risultato di un inizializzatore fallibile è un optional, quindi chi lo richiama deve estrarne il valore, di solito con if let o 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)")
    }
}

Output:

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

Anche i tipi propri di Swift usano la stessa idea: Int("42") e Double("x") sono inizializzatori fallibili, così come init(rawValue:) di un enum. Un inizializzatore fallibile può chiamarli e propagare il fallimento:

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)

Output:

true
false

Usa init? quando un valore mancante è un’informazione sufficiente. Usa un inizializzatore che genera un errore, init(...) throws, quando chi chiama dovrebbe sapere quale regola non è stata rispettata:

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

Sfida

Facile

Completa la struct Rgb, che memorizza un colore come red, green e blue da 0 a 255. Il suo inizializzatore fallibile init?(hex:) legge testo come #FF8000: restituisce nil a meno che il testo non sia # seguito esattamente da sei cifre esadecimali. Usa Int(text, radix: 16) per ogni coppia. Aggiungi una proprietà calcolata label che restituisce rgb(255, 128, 0).

Il codice fornito legge un colore per riga e stampa l'etichetta oppure invalid: text.

Il tuo codice va in Rgb.swift. main.swift contiene il codice di input/output fornito e non può essere modificato.

Provalo tu

// Codice di input/output fornito: lascialo così com'è
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 iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Playground Swift