Sandip Gill
Sandip Gill

Reputation: 1100

How to test if an `NSObject` has a property with arbitrary name that can be set?

I want to implement a generic populating of a model from a dictionary. I test if the model has a property using a following test, but the condition always fails:

if (self.responds(to:(NSSelectorFromString(keyName)))){
    self.setValue(keyValue, forKey: key )
}

Here is an example code:

import UIKit

class myModel: NSObject {    
    var userName: String = ""
    var phoneNumber: String = ""

    init(dict: Dictionary<String, Any>) {
        super.init()

        for (key, value) in dict {
            let keyName = key     
            let keyValue: String = String(describing: value)

            print("key \(key) value \(value)")

            if (self.responds(to:(NSSelectorFromString(keyName)))){
                self.setValue(keyValue, forKey: key )
            }
        }          
    }        
}

Upvotes: 1

Views: 756

Answers (4)

Amir Khan
Amir Khan

Reputation: 1318

You are almost done. Add the following at top of your NSObject class - @objcMembers

Such as -

import UIKit

@objcMembers

class myModel: NSObject { 

Upvotes: 4

Milan Nos&#225;ľ
Milan Nos&#225;ľ

Reputation: 19737

This works as you expect (tested in playgrounds):

import UIKit

class myModel: NSObject {
    @objc var userName: String = ""
    @objc var phoneNumber: String = ""

    init(dict: Dictionary<String, Any>) {
        super.init()

        for (key, value) in dict {
            let keyCapitalized = key.prefix(1).uppercased() + key.dropFirst()

            let keyName = "set\(keyCapitalized):"
            let keyValue: String = String(describing: value)

            print("key \(key) (selector: '\(keyName))' value \(value)")

            if self.responds(to: Selector(keyName)) {
                self.setValue(keyValue, forKey: key)
            }
        }
    }
}

let m = myModel(dict: ["userName" : "milan", "data" : "data"])
print(">>> \(m.userName)") // prints ">>> milan"
print(">>> \(m.phoneNumber)") // prints ">>> " (number was not provided, and data key was ignored)

Just two points.

First of all, you need to expose those properties to ObjectiveC for responds() to work - therefore I added @objc annotations on both properties.

Second of all, the proper selector syntax to test if you can set the property named userName is NOT Selector("userName"), but Selector("setUserName:") (you are testing a setter).

Upvotes: 2

Susim Samanta
Susim Samanta

Reputation: 1593

Below way you can set model values from a dictionary.

class MyModel {
    var userName: String?
    var phoneNumber: String?

    init(dict: [String:Any]?) {
        self.userName = dict?["username"] as? String
        self.phoneNumber = dict?["phonenumber"] as? String
    }
}

Example Usage :

let myModel = MyModel(dict: ["username":"test","phonenumber":"1234567890"])

Upvotes: 0

Kane Cheshire
Kane Cheshire

Reputation: 1712

Dictionaries need to be given a type for their keys and values. What you have in your initialiser is too ambiguous.

Because the result of getting something out of a dictionary can be nil, you need to provide a default value which is what happens after ??

class MyModel: NSObject {

    var userName: String = ""
    var phoneNumber: String = ""

    init(dict: [String : String]) {
        self.userName = dict["userName"] ?? ""
        self.phoneNumber = dict["phoneNumber"] ?? ""
        super.init()
    }

}

Upvotes: 0

Related Questions