Different values (also Tuples) in a Dictionary - ios

I want to create a NSDictionary with multiple key-value pairs and different value-types. My main question is how can i add a simple (2 item)-Tuple and a NSString to a NSDictionary?
Just adding a String or even a Tuple to a NSDictionary is simple.
But to add both is complicated.
I tried this:
var myTuple = (longitude: -122.1234, latitude: 55.1234)
var aPersonDictionary: [String:AnyObject] = ["firstname":"Sally", "age":45, "location":myTuple]
But this does not work. It gives me the error: Value of type '(longitude: Double, latitude: Double') does not conform to expected dictionary value type 'AnyObject'.
I don't understand that because AnyObject is everything
Any help is highly appreciated

Actually AnyObject is not everything. It's representing an instance of any class type. Therefore, the thing you would like to have could be Any which can represent an instance of any type at all, including function types.
var myTuple = (longitude: -122.1234, latitude: 55.1234)
var aPersonDictionary: [String : Any] = ["firstname":"Sally", "age":45, "location":myTuple]

You should use Any instead of AnyObject because AnyObject is used for instances of a class type (which a tuple is not), it should be:
myTuple = (longitude: -122.1234, latitude: 55.1234)
var aPersonDictionary: [String:Any] = ["firstname":"Sally", "age":45, "location":myTuple]
Further explanation:
AnyObject can represent an instance of any class type.
Any can represent an instance of any type at all, apart from function types.

Related

Can we create a Dictionary with Generic?

In Stanford's class(see the picture 1), the professor initialized a Dictionary like this:
var a = Dictionary<String: Int>()
But it cannot work in my computer(see the picture 2), is there something wrong?
Dictionary is a generic struct, whose generic type parameters can be defined in the same way as any other generic struct, using a comma separated list in angle brackets:
let a = Dictionary<String, Int>()
There's also a special syntactic sugar that's specific to dictionaries, that let you express the same as above as:
let a = [String: Int]()
Mixing the two together as in Dictionary<String: Int> is invalid.

Swift: how to make array of mutable dictionaries? [duplicate]

I’m new to Swift and have been having some troubles figuring out some aspects of Arrays and Dictionaries.
I have an array of dictionaries, for which I have used Type Aliases - e.g.
typealias myDicts = Dictionary<String, Double>
var myArray : [myDicts] = [
["id":0,
"lat”:55.555555,
"lng”:-55.555555,
"distance":0],
["id":1,
"lat": 44.444444,
"lng”:-44.444444,
"distance":0]
]
I then want to iterate through the dictionaries in the array and change the “distance” key value. I did it like this:
for dict:myDicts in myArray {
dict["distance"] = 5
}
Or even specifically making sure 5 is a double with many different approaches including e.g.
for dict:myDicts in myArray {
let numberFive : Double = 5
dict["distance"] = numberFive
}
All my attempts cause an error:
#lvalue $T5' is not identical to '(String, Double)
It seems to be acting as if the Dictionaries inside were immutable “let” rather than “var”. So I randomly tried this:
for (var dict:myDicts) in myArray {
dict["distance"] = 5
}
This removes the error and the key is indeed assigned 5 within the for loop, but this doesn't seem to actually modify the array itself in the long run. What am I doing wrong?
The implicitly declared variable in a for-in loop in Swift is constant by default (let), that's why you can't modify it directly in the loop.
The for-in documentation has this:
for index in 1...5 {
println("\(index) times 5 is \(index * 5)")
}
In the example above, index is a constant whose value is automatically
set at the start of each iteration of the loop. As such, it does not
have to be declared before it is used. It is implicitly declared
simply by its inclusion in the loop declaration, without the need for
a let declaration keyword.
As you've discovered, you can make it a variable by explicitly declaring it with var. However, in this case, you're trying to modify a dictionary which is a struct and, therefore, a value type and it is copied on assignment. When you do dict["distance"] = 5 you're actually modifying a copy of the dictionary and not the original stored in the array.
You can still modify the dictionary in the array, you just have to do it directly by looping over the array by index:
for index in 0..<myArray.count {
myArray[index]["distance"] = 5
}
This way, you're sure to by modifying the original dictionary instead of a copy of it.
That being said, #matt's suggestion to use a custom class is usually the best route to take.
You're not doing anything wrong. That's how Swift works. You have two options:
Use NSMutableDictionary rather than a Swift dictionary.
Use a custom class instead of a dictionary. In a way this is a better solution anyway because it's what you should have been doing all along in a situation where all the dictionaries have the same structure.
The "custom class" I'm talking about would be a mere "value class", a bundle of properties. This was kind of a pain to make in Objective-C, but in Swift it's trivial, so I now do this a lot. The thing is that you can stick the class definition for your custom class anywhere; it doesn't need a file of its own, and of course in Swift you don't have the interface/implementation foo to grapple with, let alone memory management and other stuff. So this is just a few lines of code that you can stick right in with the code you've already got.
Here's an example from my own code:
class Model {
var task : NSURLSessionTask!
var im : UIImage!
var text : String!
var picurl : String!
}
We then have an array of Model and away we go.
So, in your example:
class MyDict : NSObject {
var id = 0.0
var lat = 0.0
var lng = 0.0
var distance = 0.0
}
var myArray = [MyDict]()
let d1 = MyDict()
d1.id = 0
d1.lat = 55.55
d1.lng = -55.55
d1.distance = 0
let d2 = MyDict()
d2.id = 0
d2.lat = 44.44
d2.lng = -44.44
d2.distance = 0
myArray = [d1,d2]
// now we come to the actual heart of the matter
for d in myArray {
d.distance = 5
}
println(myArray[0].distance) // it worked
println(myArray[1].distance) // it worked
Yes, the dictionary retrieved in the loop is immutable, hence you cannot change.
I'm afraid your last attempt just creates a mutable copy of it.
One possible workaround is to use NSMutableDictionary:
typealias myDicts = NSMutableDictionary
Have a class wrapper for the Swift dictionary or array.
class MyDictionary: NSObject {
var data : Dictionary<String,Any>!
init(_ data: Dictionary<String,Any>) {
self.data = data
}}
MyDictionary.data

Assign a [String:AnyObject] to [String:AnyObject] in Swift replaces comma with semi-colon

I have
var params = [String:AnyObject]()
I have a function which returns an [String:AnyObject]. So, I want to assign that to a key of params like this:
params["phoneDetails"] = getPhoneDetails()
The problem I am facing is, the return of getPhoneDetails() is different from the value in params["phoneDetails"].
Here is the output of getPhoneDetails()
[locale: en, ostype: 32bit, appversion: 4.0.0, architecture: x86, version: 8.1]
Here is the output of params["phoneDetails"]:
Optional({
appversion = "4.0.0";
architecture = "x86 ";
locale = en;
ostype = 32bit;
version = "8.1";
})
So, instead of a comma, I see a semi-colon, when using println(params["phoneDetails"]).
I want it to be same as the return type of getPhoneDetails. What am I doing wrong?
The reason for this is that Swift is implicitly converting your Swift.Dictionary into an NSDictionary:
let d: [String:AnyObject] = ["one":"1","two":"2"]
// Swift.Dictionary implements Printable.description
// (which println uses) as [key:value, key:value]
d.description
let nsd = d as NSDictionary
// NSDictionary on the other hand, implements
// it as {\n key = value;\n key = value;\n}
nsd.description
The reason for this conversion is your use of AnyObject. In theory, AnyObject can only store reference types (i.e. classes). Try the following in a playground without any import statements at the top i.e. remove import UIKit or whatever:
// this won’t even compile - Strings aren’t classes, they’re structs
// so you can’t assign them to AnyObject
let d: [String:AnyObject] = ["one":"1","two":"2"]
// this fixes that:
let e: [String:String] = ["one":"1","two":"2"]
// but this won’t compile:
let o: AnyObject = e // [String:String] doesn’t conform to protocol AnyObject
But import Foundation and suddenly magic happens. Two bits of magic in fact: string literals can now create NSString objects, which are classes and so do conform to AnyObject. And Swift dictionaries can be silently converted to NSDictionary objects, which also are classes so conform to AnyObject. That latter one is what’s happening to you, and so you get a different output, because your type is indeed of a different type.
If you don’t want them to be of different types, you have two choices – return an NSDictionary from your getPhoneDetails function (ick) or stop using AnyObject and instead declare your dictionary to be of type [String:[String:String]] (yay, but leading to stronger types and value rather than reference semantics, which may mean having to refactor other code).
(or I guess you could switch to [String:Any] but there lies madness)

Syntax explanation: square brackets in Swift

I'm studying Swift and got confusing with following syntax:
var treasures: [Treasure] = []
Treasure is custom class, declared as follow:
class Treasure: NSObject { }
In Objective-C square brackets mean method, but what do they mean in Swift?
Ok, this is the meaning of
var treasures: [Treasure] = []
var: you are declaring a variable
treasures: the name of your variable
[Treasure]: the type of your variable, in this case the type is Array of Treasure, the compiler will allow you to insert only object of type Treasure in your Array
[]: the actual object (Array) referenced by your variable, in this case an empty Array.
E.g. if you want the Array to hold 2 elements you can write
var treasures: [Treasure] = [Treasure(), Treasure()]
Hope this helps.
Update:
My example can also be written this way
var treasures = [Treasure(), Treasure()]
Infact thanks to the Type Inference the compiler can deduce the type of the variable treasures looking at the type of the assigned value.
[Treasure] is just a syntax sugar for Array<Treasure>.
The same way [String:Treasure] is just a syntax sugar for Dictionary<String,Treasure>.
[] is just an empty array of the type you defined. The same way [:] is an empty dictionary.
When it comes to Swift and square brackets, the rules are simple. They are used only in two situations:
1) working with Array and Dictionary types:
let vectors : [[Int]] = [[1,2,3],[4,5,6]]
let birthBook : [Int:[String]] = [1987:["John","William"], 1990: ["Mary"]]
2) for subscripting objects that support subscripting:
class RouteMapper {
private var routeMap : [String:String] = [:]
subscript(endpoint: String) -> String {
get {
if let route = routeMap[endpoint] {
return route
}
return "/"
}
set(newValue) {
routeMap[endpoint] = newValue
}
}
}
let routeMapper = RouteMapper()
routeMapper["users"] = "/v1/confirmed/users"
let url = routeMapper["admins"]
Since [ and ] are not allowed in custom operators, these are the only usages for now.

I can't downcast an NSDictionary, CFDictionaryRef, nor an NSMutableDictionary, to SecItemAdd()

I don't understand Keychain that well. As far as I know you want to send a CFDictionaryRef to SecItemAdd() to store the attributes in applications' keychain. I'm probably not understanding unwrapping or something. I feel like the answer is obvious but this is my first experience with something so low-level, so please be patient with me. :)
Here's my Swift:
var keychainDictionary : NSMutableDictionary? = NSMutableDictionary()
var username = "Arcrammer"
var password = "somePassword"
var site = "http://www.hausofalexander.tk/"
// Item class
keychainDictionary![(kSecClass as String)] = kSecClassInternetPassword
// Attributes
keychainDictionary![(kSecAttrAccount as String)] = username
keychainDictionary![(kSecAttrService as String)] = "Haus"
keychainDictionary![(kSecAttrAccessible as String)] = kSecAttrAccessibleWhenUnlockedThisDeviceOnly as CFTypeRef)
keychainDictionary![(kSecMatchCaseInsensitive as String)] = (kCFBooleanTrue asCFTypeRef)
keychainDictionary![(kSecReturnData as String)] = (kCFBooleanTrue as CFTypeRef)
// Completely mutable NSDictionary from the NSMutableDictionary
var result : OSStatus?
SecItemAdd(keychainDictionary!, result)
I've created an empty and optional NSMutableDictionary() to hold the Keychain dictionary attributes and values. Next, I add add some kSecAttr- values to the dictionary as keys. I'm also casting them as String because the compiler throws the error LoginViewController.swift:112:37: Type 'CFStringRef' does not conform to protocol 'NSCopying' if I try to them as CFTypeRef or I don't try to cast them at all. Of course, String does conform to the NSCopying protocol. I don't know if this has an effect on the attribute value, but it definitely dismisses the error.
After that I continue to pass information as the String data type. From what I've gathered (which isn't much, tbh) CFTypeRef is an enumeration and the data returned by something like kCFBooleanTrue will return the type value from that enumeration which would be something like CFBoolean, CFDictionary, or basically what AnyObject or id would be to Foundation.
After that I proceed to create an empty variable named result with the type OSStatus. This variable will hold the result of the AddSecItem() function following on line 118.
However, I'm given the following error:
swift:118:17: Cannot convert the expression's type '(#lvalue NSMutableDictionary, #lvalue OSStatus?)' to type 'CFDictionary!'
I have no idea what this means and I can't figure it out. What in the world am I doing wrong here?
I don't know the parameter supply for SecItemAdd but you need more something like
var idontknow = UnsafeMutablePointer<Unmanaged<AnyObject>?>()
result = SecItemAdd(keychainDictionary!, idontknow)
Try to find out from the docu what idontknow exactly is.

Resources