I have a class called User()
class User {
var name: String?
var email: String?
var id: String?
var identification_number: String?
var phone_number: NSMutableArray?
var user_group: String?
var date: NSDate?
}
I want to get all of the variables in the class and their respective values. I am trying to use Mirror in this case.
func updateProfile(user: User) {
let mirror = Mirror(reflecting: user)
for child in mirror.children {
print("\(child.label!), \(child.value)")
}
}
My question is, how can I convert child.value to any other datatype, say String ?
I only got to find out that child.value belongs to the Protocol 'Any'
child.value has the Any type. Casting from Any to an optional poses some problems, fortunately Sandy Chapman gave a very nice solution in this post.
With his function, the code would look like this:
func castToOptional<T>(x: Any) -> T? {
return Mirror(reflecting: x).descendant("Some") as? T
}
func updateProfile(user: User) {
let mirror = Mirror(reflecting: user)
for child in mirror.children {
print("\(child.label!), \(child.value)")
if let stringVal = castToOptional(child.value) as String? {
print("Unwrapped a string: \(stringVal)")
} else if let stringVal = child.value as? String {
print("Found a non-optional string: \(stringVal)")
}
}
}
So if you're looking for strings, you need to look for both optional and non-optional ones. This applies to all types you need to check.
Create a protocol for extending Optional<Any> type to return it's non-optional-value:
private protocol AnyOptional {
var objectValue: Any? { get }
}
extension Optional: AnyOptional {
var objectValue: Any? {
switch self {
case .None:
return nil
case .Some(_):
return self! as Any
}
}
}
Thereafter you can use AnyOptional protocol as a type, and cast Any? objects to AnyOptional, thereafter allowing us to make use of the .objectValue property of AnyOptional
class User {
var name: String?
var email: String?
var id: String = "Default ID" // Lets try also with one non-optional
var identification_number: String?
var phone_number: NSMutableArray?
var user_group: String?
var date: NSDate?
}
var myUser = User()
myUser.name = "John"
myUser.phone_number = ["+44", "701 23 45 67"]
func updateProfile(user: User) {
let mirror = Mirror(reflecting: user)
for child in mirror.children {
let value : Any = (child.value as? AnyOptional)?.objectValue ?? child.value
switch(value) {
case let obj as String: print("String item: User.\(child.label!) = " + obj)
case let obj as NSMutableArray: print("NSMutableArray item: User.\(child.label!) = \(obj)")
case let obj as NSDate: print("NSDate item: User.\(child.label!) = \(obj)")
case _ : print("Non-initialized optional item: User.\(child.label!) = \(value)")
}
}
}
Which yields the following output
updateProfile(myUser)
/*
String item: User.name = John
Non-initialized optional item: User.email = nil
String item: User.id = Default ID
Non-initialized optional item: User.identification_number = nil
NSMutableArray item: User.phone_number = (
"+44",
"701 23 45 67"
)
Non-initialized optional item: User.user_group = nil
Non-initialized optional item: User.date = nil */
The benefit of using this solution is that it will "unwrap" optional non-nil values of child.value (without the "Optional(...)" padding) as well as values of child.value that are not optional, without the need of separate "unwrapping" for the two cases. In the switch case above, you can handle whatever non-nil property of the User object that you need to work with, not just as String but any of the types in your User class. The obj property in the switch case will be of the non-optional type of each of the non-nil properties of your class. The default case corresponds to optionals with value nil (not assigned).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With