Ashim Dahal
Ashim Dahal

Reputation: 1147

setValuesForKeysWithDictionary in Swift 4?

It seems like Swift 4 is implementing different method to (void)setValuesForKeysWithDictionary(), what is appropriate replacement to this function in Swift 4?

I have struct of

struct User {
    let name: String?
    let email: String?
}

The dictionaryOfUsers is coming from data structure which value are presented dynamically based on the number of users in the database:

let dictionaryOfUsers = [{key1: "name1", key2 : "[email protected]"}, {key1 : "name2", key2 : "[email protected]"} ]   
let users = [User]

Now I want to use this function to create my user array to create dictionary in Swift 4:

for dictionary in dictionaryOfUsers {
    let user = User()
    user.setValuesForKeysWithDictionary(dictionary) 

and append to users

    users.append(user)
}     

Upvotes: 1

Views: 6160

Answers (4)

Sachindra Pandey
Sachindra Pandey

Reputation: 427

I was working on something. In case it helps... P.S : Please optimise it using generics.

  import Foundation
  import CoreData

   enum ModelInitializationResult {
   case none
   case completedWithoutErrors
   case completedWithNullDataErrors
   case completedWithNoSuchKeyErrors
    }

 extension NSManagedObject {

 //Note: For this to work, the keys in the dictionary should be same as attibute name of this class
func initializeModelFromDictionary(dictionary: [String:Any] )  -> ModelInitializationResult{
  var completionResult = ModelInitializationResult.none

  let modelAttributes = self.entity.attributesByName
  let modelAttributeKeys = modelAttributes.keys

  for case let (keyToSet, valueToSet) in dictionary {
    //check for invalid key
      guard modelAttributeKeys.contains(keyToSet) else{
          completionResult = .completedWithNoSuchKeyErrors
          continue;
      }

      //check valueToSetType for Null
      let valueToSetType = type(of: valueToSet)
      guard valueToSetType != NSNull.self else{
          print("ERROR: Found NSNUll for value to set")
          completionResult = .completedWithNullDataErrors
          continue;
      }

      //Check desiredType ; set value for defined types
      let modelAttributeValue = modelAttributes[keyToSet]
      let modelAttributeType = modelAttributeValue?.attributeType

    switch (modelAttributeType!) {
      case .undefinedAttributeType :
          print("Error : undefinedAttributeType")
      case .integer16AttributeType, .integer32AttributeType, .integer64AttributeType :
          if let anInt = valueToSet as? Int{
              self.setValue(anInt, forKey: keyToSet)
          }
          else{
              //print(Pretty//printedFunction(),"Error  in setting value for key:\(keyToSet) Incompatible type")
          }
      case .stringAttributeType :
          if let anString = valueToSet as? String{
              self.setValue(anString, forKey: keyToSet)
          }
          else{
              //print(Pretty//printedFunction(),"Error  in setting value for key:\(keyToSet) Incompatible type")
          }
      case .booleanAttributeType :
          if let aBool = valueToSet as? Bool{
              self.setValue(aBool, forKey: keyToSet)
          }
          else{
              //print(Pretty//printedFunction(),"Error  in setting value for key:\(keyToSet) Incompatible type")
          }
      case .dateAttributeType :
          if let aDate = valueToSet as? Date{
              self.setValue(aDate, forKey: keyToSet)
          }
          else{
              //print(Pretty//printedFunction(),"Error  in setting value for key:\(keyToSet) Incompatible type")
          }
      case .binaryDataAttributeType :
          if let aData = valueToSet as? Data{
              self.setValue(aData, forKey: keyToSet)
          }
          else{
              //print(Pretty//printedFunction(),"Error  in setting value for key:\(keyToSet) Incompatible type")
          }

      case .transformableAttributeType :// If your attribute is of NSTransformableAttributeType,
          //print(Pretty//printedFunction(), "desiredType : transformableAttributeType")
          if let anObject = valueToSet as? NSObject {
              self.setValue(anObject, forKey: keyToSet)
          }
          else{
              //print(Pretty//printedFunction(),"Error  in setting value for key:\(keyToSet) Incompatible type")
          }

      case .doubleAttributeType :
          //print(Pretty//printedFunction(), "desiredType : doubleAttributeType")
          if let anObject = valueToSet as? Double {
              self.setValue(anObject, forKey: keyToSet)
          }
          else{
              //print(Pretty//printedFunction(),"Error  in setting value for key:\(keyToSet) Incompatible type")
          }
      case .floatAttributeType :
          //print(Pretty//printedFunction(), "desiredType : floatAttributeType")
          if let anObject = valueToSet as? Float {
              self.setValue(anObject, forKey: keyToSet)
          }
          else{
              //print(Pretty//printedFunction(),"Error  in setting value for key:\(keyToSet) Incompatible type")
          }
      case .decimalAttributeType :
          print("Error: Data type decimalAttributeType Not handled\(String(describing: modelAttributeType))")
      case .objectIDAttributeType:
          print("Error: Data type transformableAttributeType Not handled\(String(describing: modelAttributeType))")
      default :
          print("ERROR : \(String(describing: modelAttributeType))")
      }
  }
  return completionResult
}

func toDictionary() -> [String:Any]     {
    let keys = Array(self.entity.attributesByName.keys)
    let dict = self.dictionaryWithValues(forKeys: keys)
    return dict
}

func printAllValues (){
    for key in self.entity.attributesByName.keys{
        let value: Any? = self.value(forKey: key)
        print("\(key) = \(String(describing: value))")
    }
}

}

Upvotes: 0

zemunkh
zemunkh

Reputation: 712

There is no need to say that Swift 4 has no setValuesForKeysWithDictionary method. Also, the current setValuesForKeys method is often failed in Swift 4 at the latest Xcode version.

My user object is as follow.

import UIKit

class User: NSObject {
    var name: String?
    var email: String?
}

My own solution to this problem is to add manually keys as NSDictionary as follow.

let user = User()
user.email = (snapshot.value as? NSDictionary)?["email"] as? String ?? ""
user.name = (snapshot.value as? NSDictionary)?["name"] as? String ?? ""
print(user.email as Any)
print(user.name as Any)

It works without any failure. Just try it.

Upvotes: 1

Paulo Mattos
Paulo Mattos

Reputation: 19339

Try this method instead:

func setValuesForKeys(_ keyedValues: [String : Any])

Sets properties of the receiver with values from a given dictionary, using its keys to identify the properties. The default implementation invokes setValue(_:forKey:) for each key-value pair...

Your code has many minor issues — and some major issues as well (see matt answer for more info). Anyway, I think this should accomplish what you are after:

import Foundation

class User: NSObject {
    @objc var name: String!
    @objc var email: String!
}

let dictionaryOfUsers = [
    ["name": "name1", "email": "[email protected]"],
    ["name": "name2", "email": "[email protected]"]
]

var users = [User]()
for dictionary in dictionaryOfUsers {
    let user = User()
    user.setValuesForKeys(dictionary) 
    users.append(user)
} 

Upvotes: 4

matt
matt

Reputation: 535801

The problem is that you are trying to use Cocoa key-value coding — which is what setValuesForKeys(_:) comes from — on a struct. You can't do that. Cocoa is Objective-C; Objective-C has no idea what a Cocoa struct is. You need User to be an @objc class derived from NSObject, and all its properties need to be exposed as @objc as well.

Alternatively, use the new Swift 4 keypaths feature, which can access a struct property by its key. Example:

struct User {
    var name: String?
    var email: String?
}

let arrayOfDicts = [
    [\User.name: "name1", \User.email : "[email protected]"],
    [\User.name : "name2", \User.email : "[email protected]"]
]

for d in arrayOfDicts {
    var user = User()
    for key in d.keys {
        user[keyPath:key] = d[key]!
    }
    print(user)
}
/*
 User(name: Optional("name1"), email: Optional("[email protected]"))
 User(name: Optional("name2"), email: Optional("[email protected]"))
*/

Upvotes: 4

Related Questions