Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

swift reflection causes impossible nil value for any

I'm trying to use swift reflection to check for changes in objects so I can send only changed properties up to the server. Some of my properties are optional. To compare those values, I need to unwrap them but, of course, you can ONLY unwrap actual values, not nil values. So, I need to check if one of the values is nil before I compare them.

In my playground, I tried the following:

import UIKit

class myClass
{

    var fieldOne:String?
    var fieldTwo:Int?
    var fieldThree:Float?

}

var oneMyClass = myClass()
oneMyClass.fieldOne = "blah"
oneMyClass.fieldThree = 3.5

var oneOtherClass = myClass()
oneOtherClass.fieldOne = "stuff"
oneOtherClass.fieldTwo = 3

let aMirror = Mirror(reflecting: oneMyClass)
let bMirror = Mirror(reflecting: oneOtherClass)

for thing in aMirror.children
{
    for thing2 in bMirror.children
    {
        if thing.label! == thing2.label!
        {
            print("property: \(thing.label!)")

            print("before: \(thing.value)")
            print("after: \(thing2.value)")
            print("")

            //let myTest = thing.value == nil ? "nil" : "not nil"
        }
    }
}

And it generates the following output:

property: fieldOne
before: Optional("blah")
after: Optional("stuff")

property: fieldTwo
before: nil
after: Optional(3)

property: fieldThree
before: Optional(3.5)
after: nil

As you can see, the expected properties are displayed as "nil". However, if you uncomment the let statement, you get an error stating:

playground52.swift:37:38: error: value of type 'Any' (aka 'protocol<>') can never be nil, comparison isn't allowed

And yet, we know from the output that it IS nil. How can this be and what can I do about it?

like image 534
aspro Avatar asked Nov 23 '15 17:11

aspro


2 Answers

Based on this answer, I recommend using if case Optional<Any>.some(_).

For example:

aMirror.children.forEach {
    guard let propertyName = $0.label else { return }
    if case Optional<Any>.some(_) = $0.value {
        print("property: \(propertyName) is not nil")
    } else {
            print("property: \(propertyName) is nil")
    }
}
like image 177
n8tr Avatar answered Oct 19 '22 18:10

n8tr


Thats look like some sort of bug. Look at that

let x = childMirror.value == nil ? "Nil" : "Not Nil" //dont compile.

let y = { (value:Any?) in
   return value == nil ? "Nil" : "Not Nil"
}

let z = y(childMirror.value) //compile, but doesn't evaluate.

I guess the problem is because Any can store a Optional, but can't be wrapped around one. Try this:

func getValue(unknownValue:Any) -> Any {

    let value = Mirror(reflecting: unknownValue)
    if value.displayStyle != .Optional || value.children.count != 0 {
        return "Not Nil"
    } else {
        return "Nil"
    }
}
like image 42
Will Glück Avatar answered Oct 19 '22 20:10

Will Glück