Optional chaining en Swift: acceso seguro

Clase 25 de 27Curso de Programación Orientada a Objetos en Swift

Resumen

Aprende a dominar el acceso seguro en Swift con optional chaining. A partir de un modelo con Person, Residence, Room y Address, verás cómo evitar errores al trabajar con propiedades, métodos y subíndices que pueden ser nil. Con ejemplos claros (como el caso de Edgar), entenderás por qué encadenar opcionales hace tu código más robusto y legible.

¿Cómo se relaciona Address con Residence y por qué es optional?

La clase Address no se asocia directamente a Person ni a Room, sino a Residence: la persona vive en una residencia, y las habitaciones pertenecen a esa residencia. Por tanto, la dirección la tiene la residencia.

  • Address se declara en Residence como propiedad opcional: puede existir o no.
  • Se usa el tipo opcional con signo de interrogación.

Ejemplo:

class Residence {
    var address: Address? // Address opcional.
}
  • Beneficio: flexibilidad para modelar residencias sin dirección y evitar errores al acceder.

¿Qué resuelve el optional chaining en propiedades y métodos?

El optional chaining (operador ?) permite acceder de forma segura a propiedades y métodos cuando un valor puede ser nil. Si alguna parte de la cadena es nil, Swift detiene la evaluación sin ejecutar lo siguiente.

¿Cómo asignar una dirección sin arriesgar un crash?

  • Intento seguro de asignación con optional chaining:
Edgar.residence?.address = someAddress

Si residence es nil, no se asigna y el código sigue sin fallar.

  • Evita el force unwrap (!):
Edgar.residence!.address = someAddress // Provocará error si residence es nil.

Con ! asumes que hay valor. Si no lo hay, el programa falla.

¿Swift evalúa funciones solo si puede encadenar?

Sí. Si usas una función para crear la dirección, solo se llamará si el encadenado puede continuar.

func createAddress() -> Address {
    print("La función ha sido llamada")
    var addr = Address()
    addr.buildingNumber = 13
    addr.street = "Calle de Platzi"
    return addr
}

Edgar.residence?.address = createAddress()
// Si residence es nil, no imprime nada: la función no se ejecuta.
  • Idea clave: Swift evalúa de izquierda a derecha y corta en nil.

¿Cómo invocar métodos con optional chaining?

Si un método pertenece a un objeto opcional, se llama con ?. Si el objeto es nil, el método no se ejecuta.

Edgar.residence?.printNumberOfRooms()
// No imprime nada si residence es nil.

También puedes comprobar si se pudo llamar:

if Edgar.residence?.printNumberOfRooms() != nil {
    print("He podido obtener el número de habitaciones.")
} else {
    print("No ha sido posible saber el número de habitaciones.")
}
  • Clave: el resultado de encadenar un método es opcional; si es nil, la llamada no ocurrió.

¿Se puede saber si una asignación se realizó?

Sí. La asignación vía optional chaining devuelve Void? y puedes verificarla:

if (Edgar.residence?.address = createAddress()) != nil {
    print("Ha sido posible configurar la dirección de Edgar.")
} else {
    print("Seguimos sin saber dónde vive Edgar.")
}
  • Útil para confirmar operaciones que dependen de valores opcionales.

¿Cómo encadenar subíndices para leer o escribir rooms?

El optional chaining también funciona con subíndices (subscripts). Si la residencia es nil, ni se consulta ni se asigna.

¿Cómo leer el nombre de la primera habitación?

Con encadenado más subíndice y optional binding:

if let firstRoomName = Edgar.residence?[0].name {
    print("La primera habitación es de \(firstRoomName).")
} else {
    print("La primera habitación no sabemos de quién es.")
}
  • Si residence es nil, entra en el else.

¿Qué pasa al intentar escribir con residencia nil?

Edgar.residence?[0] = Room(name: "Bathroom")
// No se asigna nada si residence es nil.
  • Resultado: no hay cambio porque la cadena se interrumpe en nil.

¿Y cuando por fin hay residencia y rooms?

Cuando creas la residencia, añades habitaciones y la asignas a Edgar, el encadenado sí progresa y todo funciona.

let edgarHouse = Residence()
edgarHouse.rooms.append(Room(name: "Bathroom"))
edgarHouse.rooms.append(Room(name: "Living room"))
edgarHouse.rooms.append(Room(name: "Kitchen"))

Edgar.residence = edgarHouse // Por fin tiene casa.

if let firstRoomName = Edgar.residence?[0].name {
    print("La primera habitación de la casa de Edgar es \(firstRoomName).")
}
  • Ahora el optional chaining accede a la propiedad, al subíndice y al nombre sin bloquearse.

¿Qué habilidades y conceptos te llevas para usar ya?

  • Modelado con opcionales: declarar Address como Address? en Residence para representar ausencia de datos.
  • Acceso seguro con ?: propiedades, métodos y subíndices sin riesgo de crash.
  • Evitar ! salvo certeza absoluta: el force unwrap falla si hay nil.
  • Corto circuito en nil: funciones como createAddress() no se ejecutan si la cadena se detiene.
  • Comprobación de éxito: usar comparaciones con nil tras una asignación encadenada.
  • Lectura y escritura con subíndices: Edgar.residence?[0] para leer o asignar solo cuando procede.
  • Patrones de control: combinar con if, else y también con guard según convenga.

¿Te gustaría ver variantes con guard o patrones más idiomáticos para inicializar residencias y direcciones? Comparte tus dudas y casos reales en los comentarios.