Menu
Coddy logo textTech

Inicializadores que pueden fallar

Parte de la sección Programación orientada a objetos del Journey de Swift de Coddy. Lección 20 de 57.

Algunos valores no se pueden construir a partir de cualquier entrada. Un inicializador fallible, escrito como init?, devuelve nil en lugar de una instancia cuando la entrada no es válida, por lo que nunca puede existir una instancia no válida:

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)

Salida:

42
-1

El resultado de un inicializador que puede fallar es opcional, por lo que quien lo llama debe desempaquetarlo, normalmente 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)")
    }
}

Salida:

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

Los propios tipos de Swift utilizan la misma idea: Int("42") y Double("x") son inicializadores que pueden fallar, al igual que el init(rawValue:) de un enumerado. Un inicializador que puede fallar puede llamarlos y transmitir el fallo:

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)

Salida:

true
false

Usa init? cuando un valor faltante sea información suficiente. Usa un inicializador que lance errores, init(...) throws, cuando el llamador deba saber qué regla falló:

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

Desafío

Fácil

Completa la estructura Rgb, que almacena un color como red, green y blue de 0 a 255. Su inicializador que puede fallar init?(hex:) lee texto como #FF8000: devuelve nil a menos que el texto sea # seguido exactamente de seis dígitos hexadecimales. Usa Int(text, radix: 16) para cada par. Añade una propiedad calculada label que devuelva rgb(255, 128, 0).

El código proporcionado lee un color por línea e imprime la etiqueta o invalid: text.

Tu código va en Rgb.swift. main.swift contiene el código de entrada/salida proporcionado y no se puede editar.

Pruébalo tú mismo

// Código de entrada/salida suministrado: déjalo como está
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 iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación orientada a objetos

Practica por tu cuenta: Playground de Swift