Swift si se deja evaluar con éxito en Opcional (nulo)

Tengo un object personalizado llamado Field. Básicamente lo uso para definir un solo campo en un formulario.

class Field { var name: String var value: Any? // initializers here... } 

Cuando el usuario envía el formulario, valido cada uno de los objects de Field para asegurarse de que contengan valores válidos. Algunos campos no son necesarios, por lo que a veces deliberadamente establezco nil a la propiedad de value como esta:

 field.value = nil 

Esto parece plantear un problema cuando uso un if-let para determinar si un campo es nulo o no.

 if let value = field.value { // The field has a value, ignore it... } else { // Add field.name to the missing fields array. Later, show the // missing fields in a dialog. } 

Puse puntos de interrupción en if-else anterior y cuando field.value se ha establecido deliberadamente en nil, pasa por el bloque if-let, no el else. Sin embargo, para los campos cuyo field.value dejé sin inicializar y no asignado, el progtwig va al bloque else.

Intenté imprimir field.value y value dentro del bloque if-let:

 if let value = field.value { NSLog("field.value: \(field.value), value: \(value)") } 

Y esto es lo que obtengo:

field.value: Opcional (nulo), valor: cero

Entonces pensé que tal vez con opciones, una cosa no se inicializa y otra que tiene el valor de cero. Pero incluso agregar otro si dentro del if-let no hará feliz al comstackdor:

 if let value = field.value { if value == nil { // Cannot invoke '==' with an argument list of type '(Any, NilLiteralConvertible)' } } 

¿Cómo obtengo esto? Solo quiero comprobar si el campo field.value es nil .

Creo que esto es porque Any? permite cualquier valor y Optional.None se está interpretando como simplemente otro valor, ya que Optional es una enumeración.

AnyObject? no debería poder hacer esto ya que solo puede contener Optional.Some([any class object]) , que no permite el caso Optional.Some(Optional) con el valor Optional.None . Optional.None .

Esto es muy confuso, incluso para hablar. El punto es: ¿intentar AnyObject? en lugar de Any? y mira si eso funciona.


Más al punto, uno de los comentarios de Matt menciona que la razón por la que quiere usar Any es para una selección que podría ser un campo para ingresar text o un campo destinado a seleccionar un object Core Data.

Lo más lógico que hay que hacer en este caso es utilizar una enumeración con valores asociados , básicamente lo mismo que una unión labelda / discriminada . A continuación, se explica cómo declarar, asignar y usar dicha enumeración:

 enum DataSelection { case CoreDataObject(NSManagedObject) case StringField(String) } var selection : DataSelection? selection = .CoreDataObject(someManagedObject) if let sel = selection { // if there's a selection at all switch sel { case .CoreDataObject(let coreDataObj): // do what you will with coreDataObj case .StringField(let string): // do what you will with string } } 

Usando una enumeración como esta, no hay necesidad de preocuparse de qué cosas podrían estar escondidas dentro de eso Any? . Hay dos casos y están documentados. Y, por supuesto, la variable de selección puede ser opcional sin preocupaciones.

¿Hay un consejo para replace mi Any? escriba con una enumeración pero no pude sacar este error de mi cabeza. Cambiar mi enfoque no cambia el hecho de que algo está mal con mi actual y tuve que averiguar cómo llegué a una salida Optional(nil) .

Pude reproducir el error escribiendo el siguiente controller de vista en un nuevo proyecto de vista única. Observe la firma init .

 import UIKit class Field { var name: String = "Name" var value: Any? init(_ name: String, _ value: Any) { self.name = name self.value = value } } class AppState { var currentValue: Field? } class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let f = Field("Amount", AppState().currentValue) NSLog("\(f.value)") } } 

En resumen, estaba pasando un valor nulo ( AppState().currentValue ) a un inicializador que acepta Any y lo asigna a una propiedad cuyo tipo es Any? . Lo curioso aquí es si pasé directamente nil cambio, el comstackdor se quejará:

 let f = Field("Amount", nil) // Type 'Any' does not conform to protocol 'NilLiteralConvertible' 

Parece que en algún lugar del path, Swift envuelve el valor nil de AppState().currentValue en un opcional, por lo tanto, Optional(nil) .