I am using EVReflection in my app. One JSON response should be parsed as type Dictionary<String,Array<MyObject>>. I have successfully parsed this by overriding the setValue method like this:
override func setValue(_ value: Any!, forUndefinedKey key: String) {
switch key {
case "response":
if let dict = value as? NSDictionary {
response = Dictionary<String,Array<MyObject>>();
for (key, value) in dict {
var listValues = Array<MyObject>();
if let array = value as? NSArray {
for vd in array {
listValues.append(MyObject(dictionary: vd as! NSDictionary));
}
}
response![key as? String ?? ""] = listValues;
}
}
break;
}
}
However, I am seeing the following error in the console:
ERROR: Could not create an instance for type Swift.Dictionary<Swift.String, Swift.Array<MyObject>>
Is there a different way I should be doing this? How do I get the error to go away?
I was able to figure this out by using a propertyConverter as follows:
override public func propertyConverters() -> [(key: String, decodeConverter: ((Any?) -> ()), encodeConverter: (() -> Any?))] {
return[
(
key: "response"
, decodeConverter: {
if let dict = $0 as? NSDictionary {
self.response = Dictionary<String,Array<MyObject>>();
for (key, value) in dict {
var listValues = Array<MyObject>();
if let array = value as? NSArray {
for vd in array {
listValues.append(MyObject(dictionary: vd as! NSDictionary));
}
}
self.response![key as? String ?? ""] = listValues;
}
}
}
, encodeConverter: { return nil }
)
]
}
With EVReflection you should be using NSDictionary not a Dictionary (which is a struct).
If you do this then you shouldn't need to override any property converter methods.
In my object Dish xxx.Dish, I want to access the Choice class price and name to display but I failed. dish data load from web API and I tested data loaded success full and put the data to the object dish and it return the object list to viewcontroller to load tableview.
Output of printed console
Optional([xxx.Dish, xxx.Dish])
and in the dish class before append optionList?.append(_obj)
xxx.DishOption
Anyone helps me how can I do that .. I am new to swift and is it right way to implement? Please suggest me?
class Dish {
let dishId : String
var optionList : [DishOption]?
init?(fromAPIResponse resposne : Dictionary<String,AnyObject>) {
guard let dishId = resposne["dishId"] as? String else {
return nil
}
self.dishId = dishId
if let objs = resposne["options"] as? [[String: AnyObject]]{
for obj in objs {
if let _obj = DishOption(fromAPIResponse: obj){
optionList?.append(_obj)
}
}
}
}
class DishOption {
let optionId : String
var choiceList : [Choice]?
init?(fromAPIResponse resposne : Dictionary<String,AnyObject>) {
guard let optionId = resposne["optionId"] as? String else {
return nil
}
self.optionId = optionId
if let objs = resposne["choices"] as? [[String: AnyObject]]{
for obj in objs {
if let _obj = Choice(fromAPIResponse: obj){
choiceList?.append(_obj)
}
}
}
}
}
class Choice{
let choiceId : String
let name : String
let price : String
init?(fromAPIResponse resposne : Dictionary<String,AnyObject>) {
guard let choiceId = resposne["choiceId"] as? String ,
let name = resposne["name"] as? String,
let price = resposne["price"] as? String else {
return nil
}
self.choiceId = choiceId
self.name = name
self.price = price
}
}
UPDATE:
var dishMenuList = [Dish]()
guard let objs = json["menu_list"] as? [[String : AnyObject]] else {
return
}
for obj in objs {
if let _obj = Dish(fromAPIResponse: obj){
print(_obj.optionList) //always print nil
if let options = _obj.optionList {
for data in options {
print(data.displayAsButton)
}
}
dishMenuList.append(_obj)
}
}
From what I can see, you are never initializing both the optionList and choiceList arrays. It would be better to initialize them as empty arrays:
class Dish {
let dishId : String
var optionList = [DishOption]()
...
optionList.append(_obj)
This is the reason that you cannot see any options. Since the optionList is still nil, the line optionList?.append(_obj) does not execute.
I'm getting this error which I can't figure out how to fix:
Contextual type 'AnyObject' cannot be used with dictionary literal
I've searched on the internet but failed to find an answer. Here's my code:
struct Sweet {
let key:String!
let content:String!
let addedByUser:String!
let itemReft:FIRDatabaseReference?
init (content:String, addedByUser:String, key:String = "") {
self.key = key
self.content = content
self.addedByUser = addedByUser
self.itemReft = nil
}
init (snapshot:FIRDataSnapshot) {
key = snapshot.key
itemReft = snapshot.ref
if let dict = snapshot.value as? NSDictionary, let postContent = dict["content"] as? String {
content = postContent
} else {
content = ""
}
if let dict = snapshot.value as? NSDictionary, let postUser = dict["addedByUser"] as? String {
addedByUser = postUser
} else {
addedByUser = ""
}
}
func toAnyObject() -> AnyObject {
return ["content":content, "addedByUser":addedByUser]
}
The error happens at this line:
return ["content":content, "addedByUser":addedByUser]
I've been following this tutorial iOS Swift Tutorial: Get started with Firebase and an App like Twitter
Thanks for your time!
You have to cast the literal to the desired type
func toAnyObject() -> Any {
return ["content":content, "addedByUser":addedByUser] as Any
}
But - no offense – casting up a specific type to something more unspecific is silly. Why not
func toDictionary() -> [String:String] {
return ["content":content, "addedByUser":addedByUser]
}
I have response JSON of multitype objects from API.
It has type property inside. Now I'm trying to apply some kind of automated mapping basing on type property, but I can't make it work in any means.
private let modelClassMap = [
"first_type": First.self
]
func createModelWithDictionary(json: [String: AnyObject]) -> [AnyObject] {
var items: [AnyObject]
if let items = json["items"] as? [[String: AnyObject]] {
for item in items {
if let typeString = item["type"] as? String {
var Type = self.modelClassMap[typeString]
items.append(Mapper<Type>().map(item))
}
}
}
return items
}
error I am getting is that Type is not a type
What you're trying to do is not really possible, because template's associated types are not runtime. Compiler needs to know a type at compile time.
We can do it a bit differently, using enums:
enum ModelClassMap: String {
case FirstType = "first_type"
func map(item: [String: AnyObject]) -> AnyObject? {
switch self {
case FirstType:
return Mapper<First>().map(item)
}
}
}
And in your for-loop you can try convert string to enum:
func createModelWithDictionary(json: [String: AnyObject]) -> [AnyObject] {
var mappedItems: [AnyObject] = []
if let items = json["items"] as? [[String: AnyObject]] {
items.forEach() {
if let typeString = $0["type"] as? String,
let mappedType = ModelClassMap(rawValue: typeString),
let mappedObject = mappedType.map($0) {
// mappedObject represents an instance of required object, represented by "type"
mappedItems.append(mappedObject)
}
}
}
return mappedItems
}
I have following code for dictionary
var dic : [String: AnyObject] = ["FirstName": "Anvar", "LastName": "Azizov", "Website": NSNull(),"About": NSNull()]
I already remove key which have null value using below code
var keys = dic.keys.array.filter({dic[$0] is NSNull})
for key in keys {
dic.removeValueForKey(key)
}
It works for static dictionary,But I want do it dynamically,I want to done it using function but whenever I pass dictionary as a argument it works as a let means constant so can not remove null key
I make below code for that
func nullKeyRemoval(dic : [String: AnyObject]) -> [String: AnyObject]{
var keysToRemove = dic.keys.array.filter({dic[$0] is NSNull})
for key in keysToRemove {
dic.removeValueForKey(key)
}
return dic
}
please tell me solution for this
Rather than using a global function (or a method), why not making it a method of Dictionary, using an extension?
extension Dictionary {
func nullKeyRemoval() -> Dictionary {
var dict = self
let keysToRemove = Array(dict.keys).filter { dict[$0] is NSNull }
for key in keysToRemove {
dict.removeValue(forKey: key)
}
return dict
}
}
It works with any generic types (so not limited to String, AnyObject), and you can invoke it directly from the dictionary itself:
var dic : [String: AnyObject] = ["FirstName": "Anvar", "LastName": "Azizov", "Website": NSNull(),"About": NSNull()]
let dicWithoutNulls = dic.nullKeyRemoval()
Swift 5 adds compactMapValues(_:), which would let you do
let filteredDict = dict.compactMapValues { $0 is NSNull ? nil : $0 }
For Swift 3.0 / 3.1 this could be helpful. Also removes NSNull objects recursive:
extension Dictionary {
func nullKeyRemoval() -> [AnyHashable: Any] {
var dict: [AnyHashable: Any] = self
let keysToRemove = dict.keys.filter { dict[$0] is NSNull }
let keysToCheck = dict.keys.filter({ dict[$0] is Dictionary })
for key in keysToRemove {
dict.removeValue(forKey: key)
}
for key in keysToCheck {
if let valueDict = dict[key] as? [AnyHashable: Any] {
dict.updateValue(valueDict.nullKeyRemoval(), forKey: key)
}
}
return dict
}
}
Swift 3+: Remove null from dictionary
func removeNSNull(from dict: [String: Any]) -> [String: Any] {
var mutableDict = dict
let keysWithEmptString = dict.filter { $0.1 is NSNull }.map { $0.0 }
for key in keysWithEmptString {
mutableDict[key] = ""
}
return mutableDict
}
Use:
let outputDict = removeNSNull(from: ["name": "Foo", "address": NSNull(), "id": "12"])
Output: ["name": "Foo", "address": "", "id": "12"]
Nested NSNull supported
To remove any NSNull appearance in any nested level (including arrays and dictionaries), try this:
extension Dictionary where Key == String {
func removeNullsFromDictionary() -> Self {
var destination = Self()
for key in self.keys {
guard !(self[key] is NSNull) else { destination[key] = nil; continue }
guard !(self[key] is Self) else { destination[key] = (self[key] as! Self).removeNullsFromDictionary() as? Value; continue }
guard self[key] is [Value] else { destination[key] = self[key]; continue }
let orgArray = self[key] as! [Value]
var destArray: [Value] = []
for item in orgArray {
guard let this = item as? Self else { destArray.append(item); continue }
destArray.append(this.removeNullsFromDictionary() as! Value)
}
destination[key] = destArray as? Value
}
return destination
}
}
Swift 4
A little more efficient than the other solutions. Uses only O(n) complexity.
extension Dictionary where Key == String, Value == Any? {
var trimmingNullValues: [String: Any] {
var copy = self
forEach { (key, value) in
if value == nil {
copy.removeValue(forKey: key)
}
}
return copy as [Key: ImplicitlyUnwrappedOptional<Value>]
}
}
Usage: ["ok": nil, "now": "k", "foo": nil].trimmingNullValues // =
["now": "k"]
If your dictionary is mutable you could do this in place and prevent the inefficient copying:
extension Dictionary where Key == String, Value == Any? {
mutating func trimNullValues() {
forEach { (key, value) in
if value == nil {
removeValue(forKey: key)
}
}
}
}
Usage:
var dict: [String: Any?] = ["ok": nil, "now": "k", "foo": nil]
dict.trimNullValues() // dict now: = ["now": "k"]
The cleanest way to do it, just 1 line
extension Dictionary {
func filterNil() -> Dictionary {
return self.filter { !($0.value is NSNull) }
}
}
Rather than using a global function (or a method), why not making it a method of Dictionary, using an extension?
extension NSDictionary
{
func RemoveNullValueFromDic()-> NSDictionary
{
let mutableDictionary:NSMutableDictionary = NSMutableDictionary(dictionary: self)
for key in mutableDictionary.allKeys
{
if("\(mutableDictionary.objectForKey("\(key)")!)" == "<null>")
{
mutableDictionary.setValue("", forKey: key as! String)
}
else if(mutableDictionary.objectForKey("\(key)")!.isKindOfClass(NSNull))
{
mutableDictionary.setValue("", forKey: key as! String)
}
else if(mutableDictionary.objectForKey("\(key)")!.isKindOfClass(NSDictionary))
{
mutableDictionary.setValue(mutableDictionary.objectForKey("\(key)")!.RemoveNullValueFromDic(), forKey: key as! String)
}
}
return mutableDictionary
}
}
Swift 4 example using reduce
let dictionary = [
"Value": "Value",
"Nil": nil
]
dictionary.reduce([String: String]()) { (dict, item) in
guard let value = item.value else {
return dict
}
var dict = dict
dict[item.key] = value
return dict
}