ios - objective - Cómo obtener el valor asociado de una enumeración rápida, independientemente del caso de enumeración
objective c documentation (3)
Con Swift 2 es posible obtener el valor asociado mediante la reflexión.
Para facilitarlo, simplemente agregue el siguiente código a su proyecto y amplíe su enumeración con el protocolo asociado a EVA.
public protocol EVAssociated {
}
public extension EVAssociated {
public var associated: (label:String, value: Any?) {
get {
let mirror = Mirror(reflecting: self)
if let associated = mirror.children.first {
return (associated.label!, associated.value)
}
print("WARNING: Enum option of /(self) does not have an associated value")
return ("/(self)", nil)
}
}
}
Luego puedes acceder al valor asociado con un código como este:
class EVReflectionTests: XCTestCase {
func testEnumAssociatedValues() {
let parameters:[EVAssociated] = [usersParameters.number(19),
usersParameters.authors_only(false)]
let y = WordPressRequestConvertible.MeLikes("XX", Dictionary(associated: parameters))
// Now just extract the label and associated values from this enum
let label = y.associated.label
let (token, param) = y.associated.value as! (String, [String:Any]?)
XCTAssertEqual("MeLikes", label, "The label of the enum should be MeLikes")
XCTAssertEqual("XX", token, "The token associated value of the enum should be XX")
XCTAssertEqual(19, param?["number"] as? Int, "The number param associated value of the enum should be 19")
XCTAssertEqual(false, param?["authors_only"] as? Bool, "The authors_only param associated value of the enum should be false")
print("/(label) = {token = /(token), params = /(param)")
}
}
// See http://github.com/evermeer/EVWordPressAPI for a full functional usage of associated values
enum WordPressRequestConvertible: EVAssociated {
case Users(String, Dictionary<String, Any>?)
case Suggest(String, Dictionary<String, Any>?)
case Me(String, Dictionary<String, Any>?)
case MeLikes(String, Dictionary<String, Any>?)
case Shortcodes(String, Dictionary<String, Any>?)
}
public enum usersParameters: EVAssociated {
case context(String)
case http_envelope(Bool)
case pretty(Bool)
case meta(String)
case fields(String)
case callback(String)
case number(Int)
case offset(Int)
case order(String)
case order_by(String)
case authors_only(Bool)
case type(String)
}
El código anterior ahora está disponible como cocoapod susbspec en https://github.com/evermeer/Stuff#enum También tiene otra extensión de enumeración agradable para enumerar todos los valores de enumeración.
Tengo un objeto FormField que tiene dos propiedades: un name
String y un value
que puede aceptar cualquier tipo, ¡por lo tanto, lo he hecho Any!
. Sin embargo, me han dicho en una pregunta separada que use una enumeración con valores asociados en lugar de Any!
.
enum Value {
case Text(String!)
case CoreDataObject(NSManagedObject!)
}
class FormField {
var name: String
var value: Value?
// initializers...
}
Sin embargo, este enfoque hace que sea terriblemente detallado verificar la nulidad. Si quisiera mostrar una vista de alerta para todos los campos que faltan en el formulario, tendré que repetir una comprobación nula para cada caso en una declaración de cambio:
for field in self.fields {
if let value = field.value {
switch value {
case .Text(let text):
if text == nil {
missingFields.append(field.name)
}
case .CoreDataObject(let object):
if object == nil {
missingFields.append(field.name)
}
}
}
}
¿Hay una forma más corta de acceder al valor asociado de la enumeración, independientemente del tipo? Si hago FormField.value un Any!
El código anterior sería tan fácil como:
for field in self.fields {
if field.value == nil {
missingFields.append(field.name)
}
}
Definir un método isMissing()
dentro de la enum
: escríbelo una vez y solo una vez. Entonces obtienes casi exactamente lo que prefieres:
for field in self.fields {
if field.value.isMissing() {
missingFields.append(field.name)
}
}
Se vería algo como esto (del intérprete Swift):
1> class Foo {}
>
2> enum Value {
3. case One(Foo!)
4. case Two(Foo!)
5.
6. func isMissing () -> Bool {
7. switch self {
8. case let .One(foo): return foo == nil
9. case let .Two(foo): return foo == nil
10. }
11. }
12. }
13> let aVal = Value.One(nil)
aVal: Value = One {
One = nil
}
14> aVal.isMissing()
$R0: Bool = true
Si los valores asociados fueran del mismo tipo para todos los casos de enumeración, el siguiente enfoque podría ayudar.
enum Value {
case text(NSString!), two(NSString!), three(NSString!) // This could be any other type including AnyClass
}
// Emulating "fields" datastruct for demo purposes (as if we had struct with properties).
typealias Field = (en: Value, fieldName: String)
let fields: [Field] = [(.text(nil),"f1"), (.two(nil), "f2"), (.three("Hey"), "f3")] // this is analog of "fields"
let arrayOfFieldNamesWithEmptyEnums: [String] = fields.compactMap({
switch $0.en {
case let .text(foo), let .two(foo), let .three(foo): if foo == nil { return $0.fieldName } else { return nil }}
})
print("arrayOfFieldNamesWithEmptyEnums /(arrayOfFieldNamesWithEmptyEnums)")
Muchas otras cosas se pueden obtener de manera similar.
let arrayOfEnumsWithoutValues: [Value] = fields.compactMap({
switch $0.en {
case let .text(foo), let .two(foo), let .three(foo): if foo == nil { return $0.en } else { return nil }}
})
print("arrayOfEnumsWithoutValues /(arrayOfEnumsWithoutValues)")
// just to check ourselves
if let index = arrayOfEnumsWithoutValues.index(where: { if case .two = $0 { return true }; return false }) {
print(".two found at index /(index)")
}