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.
the app crashes on checking for count of NSMutablearray if it is nil,i am not getting how to handle it, i am new to ios
let userDefaults: UserDefaults? = UserDefaults.standard
let array = userDefaults?.object(forKey: "purchaselist") as? NSMutableArray
for i in 0..<array!.count {
}
You should check for nil also,
if let array = userDefaults?.object(forKey: "purchaselist") as? [Any], !array.isEmpty {
//Your code goes here
}
You can do this way also,
if let array = userDefaults?.object(forKey: "purchaselist") as? NSMutableArray {
if array.count != 0 {
//Your code goes here
} else {
//array count zero
}
} else {
//Your array is nil
}
FYI. Code is not tested, it is just information.
I have an array with custom objects by 2 types. Also I have TableView, which shows objects from array. I need to select tableViewCell and check, if the element already in array - remove it from array, otherwise add it to array. I know, there is method for the checking array.contains(element) but my array looks like [Any] and it doesn't have this method.
I'm trying to check it with use for-in, but it's not good solution.
How can I do this?
let a: Int = 5
let b: String = "3"
let array: [Any] = [a, b]
You are able to cast Any to Int or String type and just use array.contains
array.contains {
if let intValue = $0 as? Int {
return intValue == 3
} else if let stringValue = $0 as? String {
return stringValue == "3"
}
return false
}
OR use this extension (Swift 4):
extension Array where Element: Any {
func contains<T: Equatable>(_ element: T) -> Bool {
return contains {
guard let value = $0 as? T else { return false }
return value == element
}
}
}
array.contains("3") // true for your example
I am trying to append to an NSMutableDictionary with the following code:
let RSVPDirectory = NSMutableDictionary()
for i in 0..<self.RSVPs.count {
var tmp = self.RSVPs[i]
var firstLetter = String()
if(tmp["lastname"] is NSNull)
{
firstLetter = ((tmp["email"] as? NSString)?.substring(to: 1).uppercased())!
}
else
{
firstLetter = ((tmp["lastname"] as? NSString)?.substring(to: 1).uppercased())!
}
if RSVPDirectory[firstLetter] == nil {
RSVPDirectory[firstLetter] = [AnyHashable]()
}
RSVPDirectory[firstLetter] = tmp
}
My problem with this is that I am expecting multiple tmp inside RSVPDirectory[firstLetter] but it only adds the first one as if its overriding the previous tmp
How do I append to NSMutableDictionary in swift, I know in objective-c you can do this:
[[RSVPDirectory objectForKey:firstLetter] addObject:tmp];
What would be the equivalent to that in swift?
Try the below code in a playground you will see the output, hope this gives you an idea.
func upperCaseFirstLetter(_ str: String) -> String {
guard let first = str.first else { return "" }
return "\(first)".uppercased()
}
var RSVPs = [[String:String]]()
var RSVPDirectory = [String: [[String:String]]]()
//Test Data
var str = ["email":"test1#c.com"]
RSVPs.append(str)
str = ["lastname":"Atest2"]
RSVPs.append(str)
for i in 0..<RSVPs.count {
var tmp = RSVPs[i]
var firstLetter = ""
if(tmp["lastname"] == nil) {
firstLetter = upperCaseFirstLetter(tmp["email"]!)
} else {
firstLetter = upperCaseFirstLetter(tmp["lastname"]!)
}
if RSVPDirectory[firstLetter] == nil {
RSVPDirectory[firstLetter] = [[String:String]]()
}
RSVPDirectory[firstLetter]?.append(tmp)
}
print(RSVPDirectory)
This is the native Swift version of your Objective-C-ish code.
It uses the Dictionary(grouping API of Swift 4
let RSVPDirectory = Dictionary(grouping: RSVPs) { (dictionary) -> String in
if let lastName = dictionary["lastname"] as? String {
return String(lastName.prefix(1).uppercased())
} else if let email = dictionary["email"] as? String {
return String(email.prefix(1).uppercased())
} else {
return "🚫"
}
}
Yes you are actually replacing the RSVPDirectory[firstLetter], overriding it every time with new tmp.
What you are looking for is this:
//RSVPDirectory[firstLetter] = tmp //Replace this line with below code
let tempArray = RSVPDirectory[firstLetter] as? [AnyHashable]
tempArray?.append(tmp)
RSVPDirectory[firstLetter] = tmpArray
Here I have used a tempArray because we want to mutate the array. Accessing it directly and trying to append new value will in-turn try to mutate an immutable value. So first I have got the array in the tempArray and then after mutating the array I swapped it back in the dictionary with updated values.
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
}