How to Check If a Property Has Been Set Using Swift Reflection

How can I check if a property has been set using Swift reflection?

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
}

Check if variable is computed or stored

Here is what seems to be a working solution, based on suggestion by dasblinkenlight.

Rather than using the Objective-C method outlined above, create a Mirror of the class which has a children made up of all settable properties, therefore excluding computables.

Used like this:

let mirror = Mirror(reflecting: MyObject)

for case let (label?, value) in mirror.children {
print (label, value)
}

Here label is the name of the variable and value is obviously the value.

EDIT: In case anyone wants to convert objects into dictionary, I am posting the full code here as well. Do however remember that if values are custom objects as well, those will need to be converted too.

func dictionary() -> [String:Any] {
let mirror = Mirror(reflecting: self)

var dictionaryRepresentation = [String:Any]()

for case let (label, value) in mirror.children {

guard let key = label else { continue }

dictionaryRepresentation[key] = value
}

return dictionaryRepresentation
}

How to check if all optional members of a class have been set?

You cannot do that.

The problem is even getting the list of all properties because Swift has no reflection.

However, if you can make your objects inherit from NSObject, then you can use Obj-C runtime to do that (with some limitations though).

Using reflection to set object properties without using setValue forKey

I found a way around this when I was looking to solve a similar problem - that KVO can't set the value of a pure Swift protocol field. The protocol has to be marked @objc, which caused too much pain in my code base.
The workaround is to look up the Ivar using the objective C runtime, get the field offset, and set the value using a pointer.
This code works in a playground in Swift 2.2:

import Foundation

class MyClass
{
var myInt: Int?
}

let instance = MyClass()

// Look up the ivar, and it's offset
let ivar: Ivar = class_getInstanceVariable(instance.dynamicType, "myInt")
let fieldOffset = ivar_getOffset(ivar)

// Pointer arithmetic to get a pointer to the field
let pointerToInstance = unsafeAddressOf(instance)
let pointerToField = UnsafeMutablePointer<Int?>(pointerToInstance + fieldOffset)

// Set the value using the pointer
pointerToField.memory = 42

assert(instance.myInt == 42)

Notes:

  • This is probably pretty fragile, you really shouldn't use this.
  • But maybe it could live in a thoroughly tested and updated reflection library until Swift gets a proper reflection API.
  • It's not that far away from what Mirror does internally, see the code in Reflection.mm, around here: https://github.com/apple/swift/blob/swift-2.2-branch/stdlib/public/runtime/Reflection.mm#L719
  • The same technique applies to the other types that KVO rejects, but you need to be careful to use the right UnsafeMutablePointer type. Particularly with protocol vars, which are 40 or 16 bytes, unlike a simple class optional which is 8 bytes (64 bit). See Mike Ash on the topic of Swift memory layout: https://mikeash.com/pyblog/friday-qa-2014-08-01-exploring-swift-memory-layout-part-ii.html

Edit: There is now a framework called Runtime at https://github.com/wickwirew/Runtime which provides a pure Swift model of the Swift 4+ memory layout, allowing it to safely calculate the equivalent of ivar_getOffset without invoking the Obj C runtime. This allows setting properties like this:

let info = try typeInfo(of: User.self)
let property = try info.property(named: "username")
try property.set(value: "newUsername", on: &user)

This is probably a good way forward until the equivalent capability becomes part of Swift itself.

swift check an object's property existence

If you're asking if Swift supports reflection, TL;DR: you need to subclass from NSObject. Else you get limited info.

In this question, Does Swift support reflection? you get a more detailed discussion about the possibilities you have.

Once you have this part cleared, an example of how to obtain a list of properties can be found in this SO Answer

Although a quick & dirty way could be just to try and access the property (using KVC) and catch the exception if it fails. Swift does NOT support Try/Catch/Finally constructs, but this nice hack allows you to write code like:

SwiftTryCatch.try({
// try something
}, catch: { (error) in
println("\(error.description)")
}, finally: {
// close resources
})

Using getter/setter for property makes it not appear in reflection (mirror)

For the first problem I ended up using the following extension, which does see properties with getters/setters unlike Mirror:

extension NSObject {

func propertiesNames() -> [String] {

var count : UInt32 = 0
let classToInspect = type(of: self)

guard let properties : UnsafeMutablePointer <objc_property_t> = class_copyPropertyList(classToInspect, &count) else { return [] }

var propertyNames : [String] = []

let intCount = Int(count)

for i in 0..<intCount {

let property : objc_property_t = properties[i]
let propertyName = NSString(utf8String: property_getName(property))!

propertyNames.append(propertyName as String)
}

free(properties)

return propertyNames
}
}

As for the second issue I ended up copying each property over from the theme to the button as they are always the same. The goal was to avoid having to maintain a Theme class to bridge values every time something new is implemented in ZFButton.

How to check if an object has a stored property?


import Foundation
class MyClass: NSObject {

var myVar1 : AnyObject!

// ...
}

let myClass: MyClass = MyClass()
let hasClassMemberMyVar1 = myClass.respondsToSelector(Selector("setMyVar1:")) // true
let hasClassMemberMyVar2 = myClass.respondsToSelector(Selector("setMyVar2:")) // false

it works for me ...

UPDATE, based on OP notes

import Foundation
class C:NSObject {}
class MyClass: NSObject {
var myVar1 : C? // Objective-C representable
var i: Int = 0 // Objective-C representable
var j: Int? = 10
}

let myClass: MyClass = MyClass()
let hasClassMemberMyVar1 = myClass.respondsToSelector(Selector("setMyVar1:")) // true
let hasClassMemberMyVar2 = myClass.respondsToSelector(Selector("setMyVar2:")) // false
let hasClassMemberI = myClass.respondsToSelector(Selector("setI:")) // true
let hasClassMemberJ = myClass.respondsToSelector(Selector("setJ:")) // false, because Optional<Int> is not representable in Objective-C !!!
print(myClass.i.dynamicType, myClass.j.dynamicType) // Int Optional<Int>

with class type properties only

import Foundation
class C:NSObject {}
class C1 {}
class MyClass: NSObject {
var c : C?
var cO1: C = C()
var cO2: C!
var c1: C1 = C1()
var c2: C1?
var c3: C1!
}

let myClass: MyClass = MyClass()
let hasClassMemberC = myClass.respondsToSelector(Selector("setC:")) // true
let hasClassMemberCO1 = myClass.respondsToSelector(Selector("setCO1:")) // true
let hasClassMemberCO2 = myClass.respondsToSelector(Selector("setCO2:")) // true
let hasClassMemberC1 = myClass.respondsToSelector(Selector("setC1:")) // false, class C1 is not Objective-C representable ...
let hasClassMemberC2 = myClass.respondsToSelector(Selector("setC2:")) // false, Optional<C1> is not Objective-C representable ...
let hasClassMemberC3 = myClass.respondsToSelector(Selector("setC3:")) // false, ImplicitlyUnwrappedOptional<C1> is not Objective-C representable ...

How to get a property name and its value using Swift 2.0, and reflection?

When i understand correct this should solve ur problem:

func aMethod() -> Void {
let feed = RSS2Feed()
feed.channel = RSS2FeedChannel()
feed.channel?.title = "The Channel Title"
// feed.channel?.description = "the description of your channel"

guard let channel = feed.channel else {
return
}

let mirror = Mirror(reflecting: channel)
for child in mirror.children {
guard let key = child.label else {
continue
}
let value = child.value

guard let result = self.unwrap(value) else {
continue
}

print("\(key): \(result)")
}
}

private func unwrap(subject: Any) -> Any? {
var value: Any?
let mirrored = Mirror(reflecting:subject)
if mirrored.displayStyle != .Optional {
value = subject
} else if let firstChild = mirrored.children.first {
value = firstChild.value
}
return value
}

just some little changes for swift 3:

private func unwrap(_ subject: Any) -> Any? {
var value: Any?
let mirrored = Mirror(reflecting:subject)
if mirrored.displayStyle != .optional {
value = subject
} else if let firstChild = mirrored.children.first {
value = firstChild.value
}
return value
}


Related Topics



Leave a reply



Submit