El lenguaje de programación 
Swift tiene solo cuatro años, pero ya se está convirtiendo en el principal lenguaje de desarrollo para iOS. Desarrollado para la versión 5.0, Swift se ha convertido en un lenguaje complejo y poderoso que cumple con un paradigma tanto orientado a objetos como funcional. Y con cada nueva versión, agrega aún más funciones.
¿Pero qué tan bien conoces a Swift? En este artículo, encontrará preguntas de muestra para una entrevista de Swift.
Puede usar estas preguntas para entrevistar a los candidatos para evaluar sus conocimientos o puede evaluar los suyos. Si no sabe la respuesta, no se preocupe: hay una respuesta para cada pregunta.
Las preguntas se dividen en tres grupos:
- Principiante : para principiantes. Leyó un par de libros y aplicó Swift en sus propias aplicaciones.
- Intermedio : adecuado para aquellos que están realmente interesados en el idioma. Ya has leído mucho al respecto y a menudo experimentas.
- Avanzado : adecuado para los desarrolladores más avanzados: aquellos a quienes les gusta meterse en la jungla de la sintaxis y usar técnicas avanzadas.
Hay dos tipos de preguntas para cada nivel:
- escrito : adecuado para probar por correo electrónico, ya que sugieren escribir código.
- oral : se puede usar cuando se habla por teléfono o en persona, ya que hay suficiente respuesta en palabras.
A medida que lea este artículo, mantenga abierto el 
patio de recreo para poder consultar el código de la pregunta. Todas las respuestas se han probado en 
Xcode 10.2 y 
Swift 5 .
- Principiante
 - preguntas escritas- Pregunta 1- Considere el siguiente código: - struct Tutorial { var difficulty: Int = 1 } var tutorial1 = Tutorial() var tutorial2 = tutorial1 tutorial2.difficulty = 2
 - ¿Cuáles son los valores de  tutorial1.difficulty-  y  tutorial2.difficulty-  ? ¿Habría alguna diferencia si el  Tutorial-  fuera una clase? Por qué - la respuesta- la dificultad tutorial1.d es 1 y la dificultad tutorial2.d es 2. - En Swift, las estructuras son tipos de valor. Se copian, no se hace referencia. La siguiente línea copia el  tutorial1-  y lo asigna al  tutorial2-  : -  - var tutorial2 = tutorial1
 - Los cambios en el  tutorial2-  no afectan el  tutorial1-  . - Si Tutorial fuera una clase,  tutorial1.difficulty-  y  tutorial2.difficulty-  serían iguales a 2. Las clases en Swift son tipos de referencia. Cuando cambie la propiedad tutorial1, verá el mismo cambio para el tutorial2, y viceversa. 
 
 - Pregunta 2- Usted declaró  view1-  con  var-  y  view2-  con  let-  . ¿Cuál es la diferencia y se compila la última línea? -  - import UIKit var view1 = UIView() view1.alpha = 0.5 let view2 = UIView() view2.alpha = 0.5 
 - la respuesta- Sí, se compila la última línea.  view1-  es una variable y puede asignar su valor a una nueva instancia de UIView. Con  let-  , solo puede asignar un valor una vez, por lo que el siguiente código no se compila: -  - view2 = view1 
 - Sin embargo, UIView es una clase con semántica referencial, por lo que puede cambiar las propiedades de view2, lo que significa que el código se  compilará-  . 
 
 - Pregunta 3- Este código ordena la matriz alfabéticamente. Simplifique el cierre tanto como sea posible. -  - var animals = ["fish", "cat", "chicken", "dog"] animals.sort { (one: String, two: String) -> Bool in return one < two } print(animals)
 - la respuesta- Swift  determina automáticamente el-  tipo de parámetros de cierre y el tipo de retorno, por lo que puede  eliminarlos-  : -  - animals.sort { (one, two) in return one < two }
 - Puede reemplazar los nombres de los parámetros con la notación  $ i-  : -  - animals.sort { return $0 < $1 }
 - Los cierres que consisten en una sola declaración pueden no contener la palabra clave  return-  . El valor de la última declaración ejecutada se convierte en el resultado de retorno del cierre: -  - animals.sort { $0 < $1 }
 - Finalmente, dado que Swift sabe que los elementos de la matriz se ajustan al protocolo  Equatable-  , simplemente puede escribir: -  - animals.sort(by: <)
 - Upd:  hummingbirddj-  simplificó aún más: - En este caso, puedes acortar aún más:-  -  animals.sort()
 - Ordena ascendente, funciona para tipos que implementan Comparable.
 
 
 - Pregunta 4- Este código crea dos clases:  Dirección-  y  Persona-  . También se crean dos instancias de la clase  Persona-  (  Ray-  y  Brian-  ). -  - class Address { var fullAddress: String var city: String init(fullAddress: String, city: String) { self.fullAddress = fullAddress self.city = city } } class Person { var name: String var address: Address init(name: String, address: Address) { self.name = name self.address = address } } var headquarters = Address(fullAddress: "123 Tutorial Street", city: "Appletown") var ray = Person(name: "Ray", address: headquarters) var brian = Person(name: "Brian", address: headquarters)
 - Supongamos que  Brian se ha-  mudado a una nueva dirección y desea actualizar su registro de la siguiente manera: -  - brian.address.fullAddress = "148 Tutorial Street"
 - Esto compila y se ejecuta sin errores. Pero, si verifica ahora la dirección de  Ray-  , verá que él también se  "mudó"-  . - ¿Qué pasó aquí y cómo podemos solucionarlo? - la respuesta- La dirección es una clase y tiene semántica de referencia . Por lo tanto, la sede es la misma instancia de la clase compartida por Ray y Brian. Cambiar la sede cambiará la dirección de ambos.
 Para solucionar esto, puede crear una nueva instancia de la clase Address y asignarla a Brian, o declarar Address como una estructura en lugar de una clase .
 
 
 
 
 
 - preguntas orales- Pregunta 1- ¿Qué es  opcional-  y qué problemas resuelven? - la respuesta- opcional permite que una variable de cualquier tipo presente una situación " sin valor ". En Objective-C, "sin valor" solo estaba disponible en los tipos de referencia que utilizan el valor especial nulo . Los tipos de valor, como int o float , no tenían esta capacidad.
 Swift ha extendido el concepto de "sin valor" a los tipos de valor. La variable opcional puede contener un valor o nulo , lo que indica la ausencia de un valor.
 
 
 
 - Pregunta 2- Enumere brevemente las principales diferencias entre  estructura-  y  clase-  . - la respuesta- Las clases admiten herencia, pero las estructuras no.
 Las clases son un tipo de referencia, las estructuras son un tipo de valor.
 
 
 
 - Pregunta 3- ¿Qué son los  genéricos-  y para qué sirven? - la respuesta- En Swift, puede usar  genéricos-  en clases, estructuras y enumeraciones. Los genéricos-  solucionan el problema de la duplicación de código. Si tiene un método que acepta parámetros de un tipo, a veces tiene que duplicar el código para trabajar con parámetros de otro tipo. - Por ejemplo, en este código, la segunda función es un "clon" de la primera, excepto que tiene parámetros de cadena, no enteros. -  - func areIntEqual(_ x: Int, _ y: Int) -> Bool { return x == y } func areStringsEqual(_ x: String, _ y: String) -> Bool { return x == y } areStringsEqual("ray", "ray") 
 - Usando genéricos, combina dos funciones en una y al mismo tiempo garantiza la seguridad de los tipos: -  - func areTheyEqual<T: Equatable>(_ x: T, _ y: T) -> Bool { return x == y } areTheyEqual("ray", "ray") areTheyEqual(1, 1)
 - Como está probando la igualdad, está restringiendo los tipos a aquellos que se ajustan al protocolo  Equatable-  . Este código proporciona el resultado deseado y evita la transferencia de parámetros del tipo incorrecto. 
 
 - Pregunta 4- En algunos casos, no será posible evitar las opciones implícitamente  desenvueltas-  . Cuando y por que - la respuesta- Las razones más comunes para usar opciones no envueltas implícitamente son: - cuando no puede inicializar una propiedad que no es nula en el momento de la creación. Un ejemplo típico es la salida en Interface Builder, que siempre se inicializa después de su propietario. En este caso especial, si todo está configurado correctamente en Interface Builder, tiene la garantía de que la salida no es nula antes de usarla.
- para resolver el problema del bucle de referencias fuertes , cuando dos instancias de clases se refieren entre sí y se requiere una referencia no nula a otra instancia. En este caso, marca el enlace en un lado como no propietario , y en el otro lado usa la expansión opcional implícita.
 
 
 - Pregunta 5- ¿Cuáles son algunas formas de implementar opcional? Califíquelos en términos de seguridad. -  - var x : String? = "Test"
 - Sugerencia: solo 7 formas. - la respuesta- El desenvolvimiento forzado no-  es seguro. -  - let a: String = x!
 La implementación implícita al declarar una variable-  no es segura. -  - var a = x!
 La encuadernación opcional-  es segura. -  - if let a = x { print("x was successfully unwrapped and is = \(a)") }
 El encadenamiento opcional-  es seguro. -  - let a = x?.count
 El operador de fusión nula-  es seguro. -  - let a = x ?? ""
 - La declaración de la  Guardia-  es segura. -  - guard let a = x else { return }
 Patrón opcional-  - seguro. -  - if case let a? = x { print(a) }
 
 
 
 
 
 
- Intermedio
 - preguntas escritas- Pregunta 1- ¿Cuál es la diferencia entre  nil-  y  .none-  ? - la respuesta- No hay diferencia,  Opcional.none-  (brevemente  .none-  ) y  nil son-  equivalentes. - De hecho, la siguiente declaración devolverá  verdadero-  : -  - nil == .none
 - Usar  nil es-  más generalmente aceptado y recomendado. 
 
 - Pregunta 2- Aquí hay un modelo de termómetro en forma de clase y estructura. El compilador se queja de la última línea. ¿Qué hay de malo allí? -  - public class ThermometerClass { private(set) var temperature: Double = 0.0 public func registerTemperature(_ temperature: Double) { self.temperature = temperature } } let thermometerClass = ThermometerClass() thermometerClass.registerTemperature(56.0) public struct ThermometerStruct { private(set) var temperature: Double = 0.0 public mutating func registerTemperature(_ temperature: Double) { self.temperature = temperature } } let thermometerStruct = ThermometerStruct() thermometerStruct.registerTemperature(56.0)
 - la respuesta- ThermometerStruct se declara correctamente con una función de mutación para cambiar la variable interna. El compilador se queja de que está llamando al método registerTemperature de la instancia que se creó con let , por lo que esta instancia es inmutable. Cambiar let a var arreglará el error de compilación.
 
 En las estructuras, debe marcar los métodos que modifican las variables internas como mutantes , pero no puede invocar estos métodos utilizando una instancia inmutable.
 
 
 
 - Pregunta 3- ¿Qué generará este código y por qué? -  - var thing = "cars" let closure = { [thing] in print("I love \(thing)") } thing = "airplanes" closure()
 - la respuesta- Se imprimirá: me encantan los autos. La lista de captura creará una copia de la variable cuando se declare el cierre. Esto significa que la variable capturada no cambiará su valor, incluso después de asignar un nuevo valor. - Si omite la lista de captura en el cierre, el compilador usará el enlace, no la copia. La llamada de cierre reflejará el cambio en la variable: -  - var thing = "cars" let closure = { print("I love \(thing)") } thing = "airplanes" closure() 
 
 
 - Pregunta 4- Esta es una función que cuenta el número de valores únicos en una matriz: -  - func countUniques<T: Comparable>(_ array: Array<T>) -> Int { let sorted = array.sorted() let initial: (T?, Int) = (.none, 0) let reduced = sorted.reduce(initial) { ($1, $0.0 == $1 ? $0.1 : $0.1 + 1) } return reduced.1 }
 - Utiliza ordenado, por lo que solo usa tipos que se ajustan al protocolo comparable. - Puedes llamarlo así: -  - countUniques([1, 2, 3, 3]) 
 - Vuelva a escribir esta función como una extensión de matriz para que pueda usarla así: -  - [1, 2, 3, 3].countUniques() 
 - nota del traductor- Algo demasiado doloroso es una función monstruosa. Por qué no -  - func countUniques<T: Hashable>(_ array: Array<T>) -> Int { return Set(array).count }
 
 - la respuesta-  - extension Array where Element: Comparable { func countUniques() -> Int { let sortedValues = sorted() let initial: (Element?, Int) = (.none, 0) let reduced = sortedValues.reduce(initial) { ($1, $0.0 == $1 ? $0.1 : $0.1 + 1) } return reduced.1 } }
 
 
 - Pregunta 5- Aquí hay una función que divide dos dobles opcionales. Hay tres condiciones que deben cumplirse: - el dividendo no debe ser nulo
- el divisor no debe ser nulo
- el divisor no debe ser 0
 -  - func divide(_ dividend: Double?, by divisor: Double?) -> Double? { if dividend == nil { return nil } if divisor == nil { return nil } if divisor == 0 { return nil } return dividend! / divisor! }
 - Vuelva a escribir esta función utilizando la declaración de  protección-  y no utilizando el desenvolvimiento forzado. - la respuesta- La declaración de  guardia-  introducida en Swift 2.0 proporciona una salida si no se cumple la condición. Aquí hay un ejemplo: -  - guard dividend != nil else { return nil }
 - También puede usar la declaración de protección para el  enlace opcional-  , después de lo cual la variable expandida estará disponible  fuera de la declaración de protección-  : -  - guard let dividend = dividend else { return .none }
 - Entonces puedes reescribir la función de esta manera: -  - func divide(_ dividend: Double?, by divisor: Double?) -> Double? { guard let dividend = dividend else { return nil } guard let divisor = divisor else { return nil } guard divisor != 0 else { return nil } return dividend / divisor }
 - Preste atención a la ausencia de desempaque forzado, ya que ya desempaquetamos el dividendo y el divisor y los colocamos en variables inmutables no opcionales. - También tenga en cuenta que el resultado de las opciones desempaquetadas en la declaración de protección también está disponible fuera de la declaración de protección. - Puede simplificar aún más la función agrupando declaraciones de guardia: -  - func divide(_ dividend: Double?, by divisor: Double?) -> Double? { guard let dividend = dividend, let divisor = divisor, divisor != 0 else { return nil } return dividend / divisor }
 
 
 - Pregunta 6- Reescribe el método de la pregunta 5 usando la  declaración if let-  . - la respuesta- La instrucción if let le permite desempaquetar opciones y usar este valor dentro de este bloque de código. Fuera de él, estos valores no estarán disponibles. -  - func divide(_ dividend: Double?, by divisor: Double?) -> Double? { if let dividend = dividend, let divisor = divisor, divisor != 0 { return dividend / divisor } else { return nil } }
 
 
 
 
 - preguntas orales- Pregunta 1- En Objective-C, declaras una constante como esta: -  - const int number = 0;
 - Y así en Swift: -  - let number = 0
 - Cual es la diferencia - la respuesta- En  Objective-C, una-  constante se inicializa  en tiempo de compilación con un-  valor que debería conocerse en este punto. - Un valor inmutable creado con  let-  es una constante definida  en tiempo de ejecución-  . Puede inicializarlo con una expresión estática o dinámica. Por lo tanto, podemos hacer esto: -  - let higherNumber = number + 5
 - Tenga en cuenta que dicha asignación solo se puede hacer una vez. 
 
 - Pregunta 2- Para declarar una propiedad o función estática para los tipos de valor, se utiliza el modificador  estático-  . Aquí hay un ejemplo para la estructura: -  - struct Sun { static func illuminate() {} }
 - Y para las clases es posible usar modificadores  estáticos-  o de  clase-  . El resultado es el mismo, pero hay una diferencia. Descríbelo - la respuesta- static-  hace que una propiedad o función sea estática y  no se superponga-  . El uso de la clase  anulará una-  propiedad o función. - Aquí el compilador jurará ante un intento de anular  illuminate ()-  : -  - class Star { class func spin() {} static func illuminate() {} } class Sun : Star { override class func spin() { super.spin() } 
 
 
 
 - Pregunta 3- ¿Es posible agregar una  propiedad almacenada-  a un tipo usando la  extensión-  ? ¿Cómo o por qué no? - la respuesta- No, esto no es posible. Podemos usar la extensión para agregar un nuevo comportamiento a un tipo existente, pero no podemos cambiar el tipo en sí o su interfaz. Para almacenar la nueva propiedad almacenada, necesitamos memoria adicional, y la extensión no puede hacer esto.
 
 
 
 - Pregunta 4- ¿Qué es un protocolo en Swift? - la respuesta- Un protocolo es un tipo que define un esquema de métodos, propiedades, etc. Una clase, estructura o enumeración puede tomar un protocolo para implementar todo esto. El protocolo en sí no implementa la funcionalidad, pero la define.
 
 
 
 
 
 
- Avanzado
 - preguntas escritas- Pregunta 1- Supongamos que tenemos una estructura que define el modelo de un termómetro: -  - public struct Thermometer { public var temperature: Double public init(temperature: Double) { self.temperature = temperature } }
 - Para crear una instancia, escribimos: -  - var t: Thermometer = Thermometer(temperature:56.8)
 - Pero algo como esto sería mucho más conveniente: -  - var thermometer: Thermometer = 56.8
 - ¿Es esto posible? Como? - la respuesta- Swift define protocolos que le permiten inicializar un tipo usando literales por asignación. Aplicar el protocolo apropiado y proporcionar un inicializador público permitirá la inicialización usando literales. En el caso del termómetro, implementamos  ExpressibleByFloatLiteral-  : -  - extension Thermometer: ExpressibleByFloatLiteral { public init(floatLiteral value: FloatLiteralType) { self.init(temperature: value) } }
 - Ahora podemos crear una instancia como esta: -  - var thermometer: Thermometer = 56.8
 
 
 - Pregunta 2- Swift tiene un conjunto de operadores predefinidos para operaciones aritméticas y lógicas. También le permite crear sus propios operadores, tanto unarios como binarios. - Defina e implemente su propio operador de exponenciación (^^) de acuerdo con los siguientes requisitos: - toma dos int como parámetros
- devuelve el resultado de elevar el primer parámetro a la potencia del segundo
- procesa correctamente el orden de las operaciones algebraicas
- ignora posibles errores de desbordamiento
 - la respuesta- La creación de un nuevo operador se lleva a cabo en dos etapas: anuncio e implementación. - La declaración utiliza la palabra clave del  operador-  para especificar el tipo (unario o binario), para especificar la secuencia de caracteres del nuevo operador, su asociatividad y la precedencia de la ejecución. - Aquí el operador es ^^ y su tipo es infijo (binario). La asociatividad es correcta. - Swift no tiene una antigüedad predefinida para la exponenciación. En álgebra, la exponenciación debe calcularse antes de la multiplicación / división. Por lo tanto, creamos un orden de ejecución personalizado colocando la exponenciación más alta que la multiplicación. - Este anuncio: -  - precedencegroup ExponentPrecedence { higherThan: MultiplicationPrecedence associativity: right } infix operator ^^: ExponentPrecedence
 - Esta es la implementación: -  - func ^^(base: Int, exponent: Int) -> Int { let l = Double(base) let r = Double(exponent) let p = pow(l, r) return Int(p) }
 
 
 - Pregunta 3- El siguiente código define la estructura de  Pizza-  y el protocolo  Pizzeria-  con una extensión para la implementación predeterminada del método  makeMargherita ()-  : -  - struct Pizza { let ingredients: [String] } protocol Pizzeria { func makePizza(_ ingredients: [String]) -> Pizza func makeMargherita() -> Pizza } extension Pizzeria { func makeMargherita() -> Pizza { return makePizza(["tomato", "mozzarella"]) } }
 - Ahora definimos el restaurante  Lombardis-  : -  - struct Lombardis: Pizzeria { func makePizza(_ ingredients: [String]) -> Pizza { return Pizza(ingredients: ingredients) } func makeMargherita() -> Pizza { return makePizza(["tomato", "basil", "mozzarella"]) } }
 - El siguiente código crea dos instancias de  Lombardis-  . ¿Cuál de ellos hace margarita con albahaca? -  - let lombardis1: Pizzeria = Lombardis() let lombardis2: Lombardis = Lombardis() lombardis1.makeMargherita() lombardis2.makeMargherita()
 - la respuesta- En ambos. El protocolo  Pizzeria-  declara el método  makeMargherita ()-  y proporciona una implementación predeterminada. La implementación de  Lombardis-  anula el método predeterminado. Como declaramos el método en el protocolo en dos lugares, se llamará a la implementación correcta. - Pero, ¿qué  pasa-  si el protocolo no declara el método  makeMargherita ()-  , y la extensión aún proporciona la implementación predeterminada, como esta: -  - protocol Pizzeria { func makePizza(_ ingredients: [String]) -> Pizza } extension Pizzeria { func makeMargherita() -> Pizza { return makePizza(["tomato", "mozzarella"]) } }
 - En este caso, solo lombardis2 tendría pizza con albahaca, mientras que lombardis1 no tendría pizza, porque usaría el método definido en extensión. 
 
 - Pregunta 4- El siguiente código no se compila. ¿Puedes explicar qué le pasa? Sugerir soluciones al problema. -  - struct Kitten { } func showKitten(kitten: Kitten?) { guard let k = kitten else { print("There is no kitten") } print(k) }
 - Sugerencia: hay tres formas de corregir el error. - la respuesta- El bloque  else-  de la declaración de  guardia-  requiere una opción de salida, ya sea usando  return-  , lanzando una excepción o llamando a  @noreturn-  . La solución más simple es agregar una  declaración de devolución-  . -  - func showKitten(kitten: Kitten?) { guard let k = kitten else { print("There is no kitten") return } print(k) }
 - Esta solución arrojará una excepción: -  - enum KittenError: Error { case NoKitten } struct Kitten { } func showKitten(kitten: Kitten?) throws { guard let k = kitten else { print("There is no kitten") throw KittenError.NoKitten } print(k) } try showKitten(kitten: nil)
 - Finalmente, aquí está la llamada a  fatalError ()-  , la función  @noreturn-  . -  - struct Kitten { } func showKitten(kitten: Kitten?) { guard let k = kitten else { print("There is no kitten") fatalError() } print(k) }
 
 
 
 
 - preguntas orales- Pregunta 1- ¿Son los cierres un tipo de referencia o tipo de valor? - la respuesta- Los cierres son un tipo de referencia. Si asigna un cierre a una variable y luego lo copia a otra variable, copie el enlace al mismo cierre y su lista de captura.
 
 
 
 - Pregunta 2- Utiliza el tipo  UInt-  para almacenar un entero sin signo. Implementa un inicializador para convertir de un todo con un signo: -  - init(_ value: Int)
 - Sin embargo, el siguiente código no se compila si especifica un valor negativo: -  - let myNegative = UInt(-1)
 - Los enteros con signo, por definición, no pueden ser negativos. Sin embargo, es posible utilizar la representación de un número negativo en la memoria para traducirlo a sin signo. - ¿Cómo puedo convertir un entero negativo a UInt mientras mantengo su representación en la memoria? - la respuesta- Hay un inicializador para esto: -  - UInt(bitPattern: Int)
 - Y uso: -  - let myNegative = UInt(bitPattern: -1)
 
 
 - Pregunta 3- ¿Describir referencias circulares en Swift? ¿Cómo se pueden arreglar? - la respuesta- Las referencias circulares se producen cuando dos instancias contienen una referencia fuerte entre sí, lo que conduce a una pérdida de memoria debido al hecho de que ninguna de estas instancias puede liberarse. Una instancia no se puede liberar mientras todavía haya fuertes referencias a ella, pero una instancia contiene a la otra.
 
 Esto se puede resolver reemplazando el enlace en un lado especificando la palabra clave débil o no propiedad .
 
 
 
 - Pregunta 4- Swift te permite crear enumeraciones recursivas. Aquí hay un ejemplo de dicha enumeración que contiene una variante de nodo con dos tipos asociativos, T y List: -  - enum List<T> { case node(T, List<T>) }
 - Habrá un error de compilación. ¿Qué extrañamos? - la respuesta- Olvidamos la palabra clave  indirecta-  , que permite opciones de enumeración recursiva similares: -  - enum List<T> { indirect case node(T, List<T>) }