I've recently written a CoreData editor on iOS which automatically generates UI 
based on the model which is described using classes such as PrimitiveProperty, 
etc. Since it automatically sets the value on the entity, it needs to convert 
the value to AnyObject in order to pass it to setValue(_:forKey:), so it needs 
to be able to detect whether the value is Optional and in case it is, either 
transform the non-nil value to AnyObject (String -> NSString, Array -> NSArray, 
...). Which is currently really hard to achieve: 

var obj: IndexPath? = IndexPath()
let anyValue: Any = obj
anyValue.dynamicType /// Optional<Foundation.IndexPath>.Type

/// Using only anyValue, determine if it's Optional and retrieve its value if 
/// non-nil as AnyObject.
func isOptional(anyValue: Any) -> Bool {
    // Error: Cannot downcast from 'Any' (aka 'protocol<>') to a more 
    // optional type 'Optional<_>'
    return anyValue is Optional
    return anyValue as? Optional != nil
    ...
}

Unless there are major reasons why it's not exposed, I'd propose introducing a 
new function isOptional(anyValue: Any) -> Bool, which would simply call 
Builtin.isOptional just like _isOptional does in Builtin.swift. (which pretty 
much is just taking the current _isOptional, removing underscore and marking it 
public).

However, this still doesn't help with the issue of retrieving the value of the 
Optional. You now know the value in `anyValue` is Optional, but there is no 
good way to cast it to e.g. Optional<AnyObject>. Here we're getting into a 
vicious cycle that Any can be an Optional which is Any.

My second part of the proposal introduces another function:

func asOptional<T>(anyValue: Any) -> Optional<T>?

Which will:
- return nil if !isOptional(anyValue)
- return a non-nil value only if `anyValue` contains in fact an Optional of 
type T.

Usage:

if let anyObjOptional: AnyObject? = asOptional(anyValue: anyValue) {
    if let anyObj = anyObjOptional {
        // anyObj is now the actual content of the optional.
    }
}

As a sidenote, this is my current workaround:

private protocol _XUOptional {
    var objectValue: AnyObject? { get }
}

extension Optional: _XUOptional {
    var objectValue: AnyObject? {
        switch self {
        case .None:
            return nil
        case .Some(_):
            return self! as? AnyObject
        }
    }
}

if let optional = anyValue as? _XUOptional {
    let object = optional.objectValue
    /// ...
}



_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to