I have this Struct:
struct Alphabet {
let a = "ciao"
let b = "hi"
let c = "hola"
}
let alphabet = Alphabet()
I want the value of each property become the string of the property itself.
Like this:
alphabet.a = "a"
alphabet.b = "b"
alphabet.c = "c"
But I want to be done regardless of the number of properties or their value:
I tried this:
Mirror(reflecting: Alphabet.self).children.forEach { (label, value) in
self.alphabet[keyPath: label] = label!
}
but I know this is not the way KeyPath works...
Probably there are type safety problems going on too.
Any idea?
As far as I know keyPaths aren't the way to go, you need to use CodingKeys
Here's a working sample, creating JSON and then decoding it might not be the perfect so you better change my solution to suit your needs.
struct Alphabet: Codable {
let a: String
let b: String
let c: String
enum CodingKeys: String, CodingKey, CaseIterable
{
case a
case b
case c
}
static func generateJSON() -> String {
var json = "{"
for i in CodingKeys.allCases
{
json += "\"\(i.stringValue)\": \"\(i.stringValue)\","
}
json.removeLast()
json += "}"
return json
}
}
let decoder = JSONDecoder()
let alphabet = try decoder.decode(Alphabet.self, from: Alphabet.generateJSON().data(using: .utf8)!)
print(alphabet.a) //Prints "a"
var object1 = "C_active.scn"
var object86 = "Soap.scn"
var object41 = "image.scn"
var object9 = "NaCl.scn"
Name of different .SCN files
public func addBox(sceneView: ARSCNView) {
let imagePlaneScene = SCNScene(named: "art.scnassets/" + object1)
let imagePlaneNode = imagePlaneScene?.rootNode.childNode(withName: "object1", recursively: true)
imagePlaneNode?.position = positioner
I have a code reader that gives me a number and from that Int I have to place a specific .SCN file. I don't want to add 100 if statements like I do below. Is the some way to concatenate a string with a Int and turn that into a var in swift? (The numbers after each object is the number I receive from my code reader)
if(coding == 1) {
sceneView.scene.rootNode.addChildNode(imagePlaneNode!)
} else if(coding == 2) {
sceneView.scene.rootNode.addChildNode(imagePlaneNode!)
} else {
sceneView.scene.rootNode.addChildNode(imagePlaneNode!)
}
Something like
var("object" + coding) -> coding41 (Var)
Why don't you just use a dictionary to store your file names?
var object: [Int: String] = [1: "C_active.scn", 9: "NaCl.scn" ...]
When you need a particular filename, just use the number key attached to that string.
print(object[9]) //Prints "NaCl.scn"
In my reflection library EVReflection I have the following problem when class definitions are nested (class within a class). Below is a worked out case which can be found as a unit test here and The location in the library itself where the code needs to change is Here
I need to get the Internal Swift string representation of a nested
class for a property which is an array of that nested class.
Below you can see a unit test where I am able to get the correct type for the property company that is an other object. It will output _TtCC22EVReflection_iOS_Tests13TestIssue114b10Company114 instead of Company114
When I try the same for the friends property my goal is that it outputs something like: Swift.Array<_TtCC22EVReflection_iOS_Tests13TestIssue114b7User114>
What do I have to do to get that?
As you can see in the test I have various assignments to the value valueType. None of these assignments work. I am only able to get an Array<User114> or an Swift._EmptyArrayStorage.
As you also can see in the test is that if I set a breakpoint and do a po in the output window I am able to get the correct output. So what code will accomplish the same in my code?
class TestIssue114b: XCTestCase {
class User114: EVObject {
var company: Company114 = Company114()
var friends: [User114] = []
}
class Company114: EVObject {
var name: String = ""
var address: String?
}
func testIssueNestedObjects() {
let x = User114()
print("type 1 = \(NSStringFromClass(type(of: x.company)))") // output = type 2 = _TtCC22EVReflection_iOS_Tests13TestIssue114b10Company114
print("type 2 = \(testIssueNestedObjects(x.friends))")
}
func testIssueNestedObjects(_ theValue: Any) -> String {
var valueType = ""
let mi = Mirror(reflecting: theValue)
valueType = NSStringFromClass(type(of: (theValue as! [NSObject]).getTypeInstance() as NSObject)) // NSObject
valueType = "\(type(of: theValue))" // Array<User114>
valueType = "\(mi.subjectType)" // Array<User114>
valueType = ObjectIdentifier(mi.subjectType).debugDescription //"ObjectIdentifier(0x0000000118b4a0d8)"
valueType = (theValue as AnyObject).debugDescription // <Swift._EmptyArrayStorage 0x10d860b50>
valueType = NSStringFromClass(type(of: theValue as AnyObject)) // Swift._EmptyArrayStorage
// set breakpont en enter this in output window: (lldb) po type(of: theValue)
// Ouput will be: Swift.Array<EVReflection_iOS_Tests.TestIssue114b.User114>
return valueType
}
}
Background info:
Actually the end goal is that I have to be able to create instances of the object that I can add to the array. Since the array property is only available as a result from a Mirror command the variable will be of type Any. I do have an extension for arrays in place that will return a new array element. however I am only able to get that when the Any is casted to Array<NSObject> and because of that my extension will return an NSObject. So I would like to get a string like Swift.Array<_TtCC22EVReflection_iOS_Tests13TestIssue114b7User114> I can then get the parts between <> and then create an instance for that using NSClassFromString.
String(reflecting: type(of: theValue))
update by Edwin Vermeer:
For the required conversion to the internal string representation i now have the following function (still in draft)
public class func convertToInternalSwiftRepresentation(type: String) -> String {
if type.components(separatedBy: "<").count > 1 {
// Remove the Array or Set prefix
let prefix = type.components(separatedBy: "<") [0] + "<"
var subtype = type.substring(from: prefix.endIndex)
subtype = subtype.substring(to: subtype.characters.index(before: subtype.endIndex))
return prefix + convertToInternalSwiftRepresentation(type: subtype) + ">"
}
if type.contains(".") {
var parts = type.components(separatedBy: ".")
if parts.count == 2 {
return parts[1]
}
let c = String(repeating:"C", count: parts.count - 1)
var rv = "_Tt\(c)\(parts[0].characters.count)\(parts[0])"
parts.remove(at: 0)
for part in parts {
rv = "\(rv)\(part.characters.count)\(part)"
}
return rv
}
return type
}
Is it possible to find out the variable name, what I called it, while the variable has a value?
SO what I mean is:
var varName: Int
...
if ((varName == "varName") && (varName == 6)) {
...
}
Is this possible?
take a look about Reflection in Swift example below
struct Car {
let type: CarType
let name: String
init(_ type: CarType, name: String) {
self.type = type
self.name = name
}
}
enum CarType {
case Sport
case Economics
}
let bmw = Car(CarType.Sport, name: "BMW")
let bmwMirror = Mirror(reflecting: bmw)
let children = bmwMirror.children
print("car properties: \(children.count)") //2
var generator = children.generate()
let type = generator.next()
print(type!.label) // type Optional
print(type!.value) //Sport
let name = generator.next()
print(name!.label) // name Optional
print(name!.value) //BMW
Note : I am using Swift 2.0 Xcode beta 7 in Swift 1.2 use let bmwMirror = reflect(bmw)
and you can access the properties bmwMirror.[indexOfProperty].1(.value or label)
Why you need to check variable name? Maybe better to use dictionary instead?
If you make variable names from string and then check it for right names, maybe better to check original strings?
So I am trying to get the Actual Variable Name as String in Swift, but have not found a way to do so... or maybe I am looking at this problem and solution in a bad angle.
So this is basically what I want to do:
var appId: String? = nil
//This is true, since appId is actually the name of the var appId
if( appId.getVarName = "appId"){
appId = "CommandoFurball"
}
Unfortunately I have not been able to find in apple docs anything that is close to this but this:
varobj.self or reflect(var).summary
however, this gives information of what is inside the variable itself or the type of the variable in this case being String and I want the Actual name of the Variable.
This is officially supported in Swift 3 using #keyPath()
https://github.com/apple/swift-evolution/blob/master/proposals/0062-objc-keypaths.md
Example usage would look like:
NSPredicate(format: "%K == %#", #keyPath(Person.firstName), "Wendy")
In Swift 4 we have something even better: \KeyPath notation
https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md
NSPredicate(format: "%K == %#", \Person.mother.firstName, "Wendy")
// or
let keyPath = \Person.mother.firstName
NSPredicate(format: "%K == %#", keyPath, "Andrew")
The shorthand is a welcome addition, and being able to reference keypaths from a variable is extremely powerful
As per the updated from this answer, it is supported in Swift 3 via #keyPath
NSPredicate(format: "%K == %#", #keyPath(Person.firstName), "Andrew")
This is my solution
class Test {
var name: String = "Ido"
var lastName: String = "Cohen"
}
let t = Test()
let mirror = Mirror(reflecting: t)
for child in mirror.children {
print(child.label ?? "")
}
print will be
name
lastName
This works:
struct s {
var x:Int = 1
var y:Int = 2
var z:Int = 3
}
var xyz = s()
let m = Mirror(reflecting: xyz)
print(m.description)
print(m.children.count)
for p in m.children {
print(p.label as Any)
}
I've come up with a swift solution, however unfortunately it doesn't work with Ints, Floats, and Doubles I believe.
func propertyNameFor(inout item : AnyObject) -> String{
let listMemAdd = unsafeAddressOf(item)
let propertyName = Mirror(reflecting: self).children.filter { (child: (label: String?, value: Any)) -> Bool in
if let value = child.value as? AnyObject {
return listMemAdd == unsafeAddressOf(value)
}
return false
}.flatMap {
return $0.label!
}.first ?? ""
return propertyName
}
var mutableObject : AnyObject = object
let propertyName = MyClass().propertyNameFor(&mutableObject)
It compares memory addresses for an object's properties and sees if any match.
The reason it doesn't work for Ints, Floats, and Doubles because they're not of type anyobject, although you can pass them as anyobject, when you do so they get converted to NSNumbers. therefore the memory address changes. they talk about it here.
For my app, it didn't hinder me at all because I only needed it for custom classes. So maybe someone will find this useful. If anyone can make this work with the other datatypes then that would be pretty cool.
Completing the accepted answer for extensions:
The property needs to be #objc.
var appId: String? {
....
}
You need to use #keyPath syntax, \ notation is not supported yet for extensions.
#keyPath(YourClass.appId)
The best solution is Here
From given link
import Foundation
extension NSObject {
//
// Retrieves an array of property names found on the current object
// using Objective-C runtime functions for introspection:
// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
//
func propertyNames() -> Array<String> {
var results: Array<String> = [];
// retrieve the properties via the class_copyPropertyList function
var count: UInt32 = 0;
var myClass: AnyClass = self.classForCoder;
var properties = class_copyPropertyList(myClass, &count);
// iterate each objc_property_t struct
for var i: UInt32 = 0; i < count; i++ {
var property = properties[Int(i)];
// retrieve the property name by calling property_getName function
var cname = property_getName(property);
// covert the c string into a Swift string
var name = String.fromCString(cname);
results.append(name!);
}
// release objc_property_t structs
free(properties);
return results;
}
}