Casting Any Optional

I am working on a set of classes representing entities and their properties, which can then dynamically create an editor table view from an object. These properties use generics to capture the type of property. To use KVO and generate an automatic setter, these properties contain a key path. Here is a very simplified version of the property class:

class XUEntityProperty<Entity: NSManagedObject, Value> {
    let keyPath: String
    var customSetter: ((Entity, Value) -> Void)?

    func setValue(value: Value, onEntity entity: Entity) {
        /// If custom setter is set, use it.
        if let setter = self.customSetter {
            setter(entity, value)
            return
        }

        /// Otherwise set the object using the keypath.
        guard let objValue = value as? AnyObject else {
            XUThrowAbstractException() // Use custom setter
        }

        entity.setValue(objValue, forKeyPath: self.keyPath)
    }
}

It works very well with almost anything. The problem is when it comes to options. For example:.

let property = XUEntityProperty<MyEntity, NSDate?>(keyPath: "optionalDate")

The problem here is that in the method, setValuecasting to AnyObjectfails, because a value Optional<NSDate>that cannot be attributed to AnyObject- objValue as? NSDatewill return nil, even if it objValueis .Some(_).

, , Optional.

, , , , , Optional Optional.

- , , Any Optional, , , AnyObject?

, :

let any: Any = Optional<String>("123")
any.dynamicType // -> Optional<String>.Type

var object: AnyObject? = nil

/// ... -> put value from `any` to `object`.
+4
2

, is , Mirror(..) Any:

protocol IsOptional {}
extension Optional : IsOptional {}

/* Detect if any is of type optional */
let any: Any = Optional<String>("123")
var object : AnyObject? = nil
switch any {
case is IsOptional:
    print("is an optional")
    if let (_, a) = Mirror(reflecting: any).children.first {
        object = a as? AnyObject
    }
default:
    print("is not an optional")
} /* Prints "is an optional" */

/* Detect if any2 is of type optional */
let any2: Any = String("123")
switch any2 {
case is IsOptional:
    print("is an optional")
    // ...
default:
    print("is not an optional")
} /* Prints "is not an optional" */

Charlie Monroes (+1!). , .

, _XUOptional Optional ( Charlies), , Any , nil:

let anyOpt: Any = Optional<String>("123")
let anyNotOpt: Any = String("123")
var object: AnyObject?

object = (anyOpt as? _XUOptional)?.objectValue ?? (anyOpt as? AnyObject)
/* anyOpt is Optional(..) and left clause of nil coalescing operator
   returns the unwrapped .objectValue: "123" as 'AnyObject'           */

object = (anyNotOpt as? _XUOptional)?.objectValue ?? (anyNotOpt as? AnyObject)
/* anyNotOpt is not optional and left-most optional chaining of left
   clause returns nil ('anyNotOpt as? _XUOptional' -> nil).
   In that case, right clause will successfully cast the non-optional
   'Any' type to 'AnyObject' (with value "123")                       */
+5

, . , . @dfri !

private protocol _XUOptional {

    var objectValue: AnyObject? { get }

}

extension Optional: _XUOptional {

    /// This property will return nil if the value is nil,
    /// or if the value isn't castable to AnyObject. String,
    /// Int, Bool and a few others are automatically converted
    /// to their ObjC counterparts.
    var objectValue: AnyObject? {
        switch self {
        case .None:
            return nil
        case .Some(_):
            return self! as? AnyObject
        }
    }

}

let any: Any = Optional<String>("123")
let obj = (any as! _XUOptional).objectValue!
obj.dynamicType /// _NSContiguousString.Type
+3

Source: https://habr.com/ru/post/1622244/


All Articles