Some of my models have optional properties. I'm trying to write a method that can evaluate if they've been set.
Below is an attempt, but I can't figure out how to determine a nil value from an Any
object [edit: (the child
variable is of type Any
)]. It doesn't compile.
func allPropertiesHaveValues(obj: AnyObject) -> Bool {
let mirror = Mirror(reflecting: obj)
for child in mirror.children {
let value = child.value
if let optionalValue = value as? AnyObject? { //Does not compile
if optionalValue == nil {
return false
}
}
}
return true
}
Edit:
I forgot to clarify that the child
value in the above example is always of type Any
. The Any
type is difficult in that it cannot be compared to nil
and a cast to AnyObject
always fails. I've tried to illustrate it in the playground below.
var anyArray = [Any]();
var optionalStringWithValue: String? = "foo";
anyArray.append(optionalStringWithValue);
var nilOptional: String?
anyArray.append(nilOptional)
print(anyArray[0]); // "Optional("foo")\n"
print(anyArray[1]); // "nil\n"
if let optionalString = anyArray[0] as? AnyObject {
//will always fail
print("success")
}
//if anyArray[1] == nil { // will not compile
//}
Obsolete:
You can simply check if the optional value is nil or not :
func allPropertiesHaveValues(obj: AnyObject) -> Bool {
let mirror = Mirror(reflecting: obj)
for child in mirror.children {
//child.value being an optional
if child.value == nil {
return false
}
}
return true
}
Edit:
To check if an Any
object is optional and contains a value or not using reflection :
let optionalString : String? = "optional string"
let any : Any = optionalString
//First you will need to create a mirror of the any object
let mirror = Mirror(reflecting : any)
//Then you can check the display style to see if it's an optional
if mirror.displayStyle == .Optional {
//If it is, check the count of its children to see if there is a value or not
if mirror.children.count == 0 {
print("I don't have a value")
}
else {
print("I have a value")
}
}
Here is a playground example (based on yours):
var anyArray = [Any]()
var optionalStringWithValue: String? = "foo"
anyArray.append(optionalStringWithValue)
var nilOptional: String?
anyArray.append(nilOptional)
let string = "string not optional"
anyArray.append(string)
print(anyArray[0]) // "Optional("foo")\n"
print(anyArray[1]) // "nil\n"
print(anyArray[2]) // "string not optional\n"
let mirrorOptionalWithValue = Mirror(reflecting: anyArray[0])
if mirrorOptionalWithValue.displayStyle == .Optional
&& mirrorOptionalWithValue.children.count == 1 {
print("Is an optional and contains a value")
}
let mirrorOptionalWithoutValue = Mirror(reflecting: anyArray[1])
if mirrorOptionalWithoutValue.displayStyle == .Optional &&
mirrorOptionalWithoutValue.children.count == 0 {
print("Is an optional but is nil")
}
let mirrorNotAnOptional = Mirror(reflecting: anyArray[2])
if mirrorNotAnOptional.displayStyle != .Optional {
print("Is not an optional")
}
I used @ebluehands technique of reflecting the Any
value to modify the original function. It cycles through the properties with an initial mirror, then reflects each one individually using displayStyle
to determine if the property is optional.
func allPropertiesHaveValues(obj: AnyObject) -> Bool {
let mirror = Mirror(reflecting: obj)
for child in mirror.children {
let value: Any = child.value
let subMirror = Mirror(reflecting: value)
if subMirror.displayStyle == .Optional {
if subMirror.children.count == 0 {
return false
}
}
}
return true
}
Another option is create a extension.
extension NSManagedObject {
func checkIfAllRequiredMembersAreSet() -> Bool {
let attributes = self.entity.attributesByName
for (attribute, value) in attributes {
if value.attributeValueClassName != nil {
let v: AnyObject? = self.valueForKey(attribute)
if !value.optional && v != nil {
return false
}
}
}
return true
}
}
Based on this answer, I recommend using if case Optional<Any>.some(_)
.
I did something recently to make sure I have at least one optional set. Here's an example to make sure all are set. You can paste into playgrounds:
import Foundation
struct SomeError: Error {
let code: Int?
let message: String?
let errorDescription: String?
var allValuesSet: Bool {
for aChild in Mirror(reflecting: self).children {
if case Optional<Any>.some(_) = aChild.value {
continue
} else {
return false
}
}
return true
}
}
let errorTest = SomeError(code: nil, message: "failed", errorDescription: nil)
let errorTest2 = SomeError(code: -1, message: "failed", errorDescription: "missing information")
print("is valid: \(errorTest.allValuesSet)") //is valid: false
print("is valid: \(errorTest2.allValuesSet)") //is valid: true