Looping an array of NSDictionaries in swift - ios

Ok, trying to catch the last train to learn Swift, I have seen similar questions but I am not getting them to solve my issue.
I have an NSDictionary called entries, and one of the values, corresponding to key "TYPES" is an NSArray of NSDictionaries. I am trying to loop over this latter NSDictionary and retrieve an integer value for the key "TID", I am doing:
for dict in entries["TYPES"] as NSDictionary {
let tid : Int = typeDict["TID"]
}
But I am receiving as error: (key: AnyObject, value: AnyObject) does not have a member named 'subscript'
I understand this is due to entries["TYPES"] being anyObject! and comes from Xcode 6 beta 6, where a large number of Foundation APIs have been audited for optional conformance and hence need unwrapping but I have tried my best to unwrap without success, the compiler is always complaining a different thing. Someone knows how to do this?

If this is a sample of your dictionary:
var entries: NSDictionary = [
"TYPES": [
[],
["TPD": 2],
["TID": 4]
] as NSArray
]
you have to:
retrieve the element identified by the TYPES key, and attempt to cast as NSArray
loop through all elements of the array
attempt a cast of each element as NSDictionary
check for the TID key existence, and read its value
if the value is not nil, the search is over
This is the code:
var tid: Int?
if let types = entries["TYPES"] as? NSArray {
for type in types {
if let dict = types.lastObject as? NSDictionary {
tid = dict["TID"] as? Int
if tid != nil {
break
}
}
}
}
Running the code in a playground with the sample data, the output I see is {Some 4}.
However I would keep #Zaph's advice into account and model your data in a different way, using structs and/or classes

Related

Unable to compare array value in swift ios

I'm a fresher in iOS
I'm unable to compare in an if statement.
It looks like:
for i in 0.. <array.count
{
if(array[i] == array[i+1])
{
let removedVal = array.remove(i+1)
}
}
The error shows on the if condition:
Binary operator '==' cannot be applied to two 'Any' operands
I googled it, but I am unable to understand what should I do in my case.
=======================================================================
Atlast able to find a solution.
And it worked for me
if ( ((tempArrayForTeamName[i]) as AnyObject).isEqual(tempArrayForTeamName[i+1] as AnyObject) )
need to compare array index position as Any object
And use .isEqual replace of ==
You have to Filter your Array
var newarray = [Int]()
let dictionary = ["A":0,"B":1,"C":1,"D":1,"E":1,"F":1,"G":1,"H":1,"J":0]
let newDictionary = dictionary.reduce([:]) { result, element -> [String: Int] in
guard element.value != 1 else {
return result
}
var newResult = result
newResult[element.key] = element.value
newarray.append(newResult[element.key]!)
return newResult
}
In Swift : Array is a Generic Structure, NSMutableArray is an Objective-C class[will work in Swift].
A NSMutableArray created is of type Any; an array that can contain heterogenous object(could be String, Int or Bool).
An Array is arbitrarily specilized to contain Any (using as [Any])
eg:
var array:Array = ["ABC", 123, true] as [Any]
var nsMutableArray : NSMutableArray = ["ABC", 123, true]
Generic Parameterization:
Even if there is an option to give generic parameterization(Datatype) to your NSMutableArray in Objective C[remember NSMutableArray in an Objective C class],this generic parameterization in unfortunately ignored/not allowed in Swift.
How to specify the datatype:
In Swift one cannot specify the datatype of a NSMutableArray.It would give a compilation error: Cannot specialize non-generic type NSMutableArray.
But one can always specify the datatype of Array(Swift structure) as say: Array<String>.
eg: var array:Array<String> = ["Tom", "Jerry", "Spike"]
Your code has another problem, consider your array has 3 items then i=2, and you are trying to access index 3 (i+1). And program will crash.
Crash point (array[i] == array[i+1])
Please declare specific types array for example
let myArray:[String] = ["a", "b", "c", "d"]

How to cast Dictionary in Swift to related type?

This is what I am trying to do with the dictionary:
if let deliveries = dictionary["deliveries"] as? NSDictionary {
var castedDeliveries = [Double: Double]()
for delivery in deliveries {
if let value = delivery.value as? Double {
castedDeliveries[Double(delivery.key as! NSNumber)] = value //Could not cast value of type 'NSTaggedPointerString' (0x1a1e3af20) to 'NSNumber' (0x1a1e458b0).
}
}
settings!.deliveries = castedDeliveries
}
And this is what I try to cast, as a part of JSON response from server:
deliveries = {
2 = 0;
5 = "2.59";
7 = "3.59";
};
It doesnt work, because there is an error at commented line:
Could not cast value of type 'NSTaggedPointerString' (0x1a1e3af20) to 'NSNumber' (0x1a1e458b0).
You are trying to cast dictionary directly but instead you need to cast each key - value pair. If you want generic solution to this problem take a look at SwiftyJSON library which address JSON parsing problem for you.
Casting doens't mean data transformation from a type to another.
Your dictionary seems to be composed by Integer keys and String values.
If you want to transform in something else you ca use the map function.
let converted = deliveries.map{[Double($0) : Double($1)]}
But pay attention.
Here we are saying, iterate over the dictionary (in the $0 there is the dictionary key in the $1 there is the value) and create a new dictionary that has as a key a Double initialized at the key value and as a new value a Double initialized as the old dictionary value. The last conversion can fail, so the returned data is an optional.
As I noted in the comments, this isn't casting. You want a data conversion. You need to do that explicitly, especially in this case since it might fail.
Looking at the error, I think you really have a dictionary of [String:String] here (in NSDictionary form). That suggests the JSON is badly encoded, but such is life. Assuming that dictionary looks something like this:
let dictionary: NSDictionary = ["deliveries": ["2":"0", "5": "2.59", "7": "3.59"]]
You would convert it to [Double:Double] like this:
if let jsonDeliveries = dictionary["deliveries"] as? [String:String] {
var deliveries: [Double: Double] = [:]
for (key, value) in jsonDeliveries {
if let keyDouble = Double(key),
valueDouble = Double(value) {
deliveries[keyDouble] = valueDouble
}
}
// Use deliveries
}
This silently ignores any values that can't be converted to Double. If you would rather generate errors, use a guard let rather than an if let.

Swift Subscript Error

I believe it has something to do with optionals, but I'm safely unwrapping sourceURL so I'm still not sure where the error is! I'm trying to access a JSON object's array's dictionary value.
However, I'm still getting the "could not find overload for 'subscript' that accepts the supplied arguments.
It seems simple, but I just can't seem to figure it out!
var dictTemp: NSDictionary! = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: &localError) as? NSDictionary
var finalURL: String
// error line below
if let sourceURL = dictTemp[0]["source"]["sourceUrl"] as? NSString {
finalURL = sourceURL as String
}
NSDictionary accessed from Swift is an interesting beast.
As long as Swift only knows something is an NSDictionary (not a more specific [Key: Value] Swift-style dictionary), you can only retrieve AnyObject?s out of it.
let dictTemp: NSDictionary = // from somewhere...
let step1 = dictTemp[0] // step1 is an AnyObject?
But then, since you've imported Foundation, you can keep going with a magical subscript operator that works on AnyObject, and checks whether the thing is a dictionary:
let step2 = step1?["source"] // step2 is any AnyObject??
Here's where it gets interesting, because
if step1 was a dictionary with a "source" key inside it, step2 will be the corresponding value.
if step1 was a dictionary without a "source" key, step2 will be nil — in particular, it's AnyObject??.Some(AnyObject?.None).
if step1 was nil (the original dictionary didn't have 0 as a key), or not a dictionary (it had a 0 key with some other kind of value), then step2 will be nil — in particular, AnyObject??.None.
(The distinction between the last 2 cases is mostly unimportant and you shouldn't worry about it, but if you're interested you can see it by using dump).
And of course, we can apply the same principle again:
let step3 = step2??["sourceUrl"] // step3 is AnyObject?? again
Now, binding them all in one if:
if let url = dictTemp[0]?["source"]??["sourceUrl"] as? String {
// do something with url...
}
Caveat
This type of syntax can be dangerous, since it works with arrays and dictionaries at the same time. What would you expect in these situations?
let dict: NSDictionary = [0: ["source": [3: "result"]]]
dict[0]?["source"]??[3] // returns nil (surprise!)
dict[0]?["source"]??[3 as NSNumber] // returns "result"
let dict2: NSDictionary = [0: ["source": [8, 7, 6, 5, 4]]]
dict2[0]?["source"]??[3] // returns 5
dict2[0]?["source"]??[3 as NSNumber] // returns nil (surprise!)

array count error swift

I have searched long, but couldn't find a solution for my bug. Swift somehow doesn't count my array (converted from json) correctly. This is the code I use to create the array:
let jsonData = NSData(contentsOfURL: url)
let jsonDic = NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers, error: &error) as NSDictionary
var count = jsonDic.count
When the count should be 3, the count is 2. So I just always added 1, but now if the count should be 4, the count is still 2.
Has anyone experienced something like that or is it just me doing something wrong?
EDIT: This is an example input:
{"items":[{"var1":"xxx","var2":"xxx","var3":"xxx","var4":"xxx","var5":0},{"var1":"xxx","var2":"xxx","var3":"xxx","var4":"xxx","var5":0}, {"var1":"xxx","var2":"xxx","var3":"xxx","var4":"xxx","var5":0}]}
The sample data you posted is a dictionary with one items key, and the corresponding value is an array (so the dictionary count should be 1).
By using this code:
let array = jsonDic["items"] as? NSArray
array?.count
I see that that array has 3 elements.
If what you are trying to count is the array, then I would use the above code, or this one using optional binding:
if let array = jsonDic["items"] as? NSArray {
array.count
}
NOTE: I'd warn you about using jsonDic["items"]!.count because it is not safe: if the items key is not in the dictionary, or if its value cannot be cast to an array, then a run time exception will be thrown.

Cannot downcast from 'AnyObject' whilst parsing JSON array

I'm in the process of trying to migrate an existing project to Swift, its more of a learning exercise but I think I've hit a problem which seems pretty basic and I think it may relate to my lack of understanding of AnyObject.
I've created a object that consists of a number of variables and is initialised as:
var customObject:MycustomObject = MYcustomObject()
I'm then using NSURLConnection to retrieve JSON data and this all appears to be working correctly as the jsonArray is being populated with data
jsonArray = NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers, error: &error) as NSArray
I'm then looping through the array and attempting to parse the data and this is where I'm experiencing problems, the for loop defaults details to AnyObject.
for details:AnyObject in jsonArray {
parseDetail(details as NSDictionary)
}
The problem relates to the integerValue, its generating an "Cannot downcast from 'AnyObject' to non-#objc...." error
func parseDetail(element: NSDictionary ) {
self.customObject.stringValue = element["id"] as String
self.customObject.integerValue = element["type"] as Integer
}
There also seems to be some differences in the way NSString & String are working, my understanding so far is that when using Swift I should be using the native types Sting, Float Integer etc etc. If I use NSString the stringValue displays correctly in the debug window but when using the type String I receive the following:
{
core = {
_baseAddress = Builtin.RawPointer = 0x0b227f49
_countAndFlags = 1073741828
_owner = Some {
Some = (instance_type = Builtin.RawPointer = 0x0b227f40 -> 0x006d58f0 (void *)0x006d58c8: __NSCFString)
}
}
}
Sorry this is a little long but is there any info regarding threading, this is my next challenge?
NSThread.detachNewThreadSelector(Selector: parseDetail(), toTarget: self, withObject: nil)
Here's a little example that does the job. First, here's a model class for our purposes:
class CustomObject {
var name: String = ""
var age : Int = 0
init(json: Dictionary<String, AnyObject>) {
name = json["name"] as NSString
age = (json["age" ] as NSNumber).integerValue
}
}
As you can see, we still need to deal with NSNumbers and NSStrings since NSJSONSerialization is not yet updated to the Swift types.
Next, a little json.txt file residing in our project:
[{"name":"Alice","age":30},{"name":"Bob","age":40}]
And last, the parser, right in application launch for a quick test:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
var filePath = NSBundle.mainBundle().pathForResource("json", ofType:"txt")
var data = NSData(contentsOfFile:filePath)
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: nil) as Array<Dictionary<String, AnyObject>>
var customObjects = json.map { dict in CustomObject(json: dict) }
println(customObjects[0].name)
println(customObjects[0].age)
return true
}
This will print:
Alice
30
A progress update, the following items are correct and are working:
.interegervalue
.doublevalue
.boolvalue
The reason why the above were appearing not to be working is that the JSON data had changed and some elements no longer existed. So element["type"] didn't exist, using Swift this results in a crash whereas in Objective-C no crash occurs.
All part of the learning process..

Resources