Dictionary cannot be bridged from Objective-C -> Issues in Swift - swift

I have been porting some objective-c code over into swift and I am trying to get the result set as a dictionary and then pack each dictionary (equivalent to a row from the db) into an array. But I am getting this error message "Dictionary cannot be bridged from Objective-C". I have read this from apple but still I am no further along towards a solution. Any ideas? Thanks.
This is the line where the error is:
resultsArray.append(resultSet!.resultDictionary() as Dictionary<String,String>)

From the awesome robertmryan reposted here for convenience:
This will happen if your database has an null values (which return [NSNull null] objects) or numeric values (which return NSNumber objects). You can fix this by defining resultsArray as:
var resultsArray = Array<Dictionary<String,AnyObject>>()
Or, I personally prefer:
var resultsArray = [[String: AnyObject]]()
And then when adding objects, I'd
resultsArray.append(resultSet!.resultDictionary() as Dictionary<String, AnyObject>)
or
resultsArray.append(resultSet!.resultDictionary() as [String: AnyObject])

Related

How to add nil value to a NSArray in swift?

NSArray.init(array: [Any])
NSArray has an initial function whose parameter is [Any]. But not [Any?], so, how to add a nil value to this array?
NSArray/NSMutableArray don't allow you to store nil values in the array. This is why none of the Swift APIs allow optional values.
If you really need to something for nil, use NSNull() though I'm not 100% sure how that will work with Core Data.
And Swift actually does this for you. If you pass a Swift array of optionals, any nil values get converted to NSNull. Example:
var array = [Int?]()
array.append(4)
array.append(nil)
let nsa = array as NSArray
print(nsa) // (4, "<null>")
print(type(of: nsa[1])) // NSNull
Note that using let nsa = NSArray(array: array) instead of let nsa = array as NSArray worked without warning under Swift 4.0 but gives a warning as of Swift 4.1.

Iterating dictionary swift 3

I have below code in my project.
for (key, value) in photoDic {
if let url = URL.init(string: value as! String){
let photo : PhotoRecord = PhotoRecord.init(name:key as! String, url:url)
self.photoRecords.append(photo)
}
}
My question is how can I make key and value in for loop optional, or check if either of them are nil?
I am not able to check if they are nil, getting warning saying any cannot be nil because it is nonoptional.
I was thinking of using something like
for(key:String?, value:String?){}
But it is not working.
The key in a dictionary can't be an optional. (The key must conform to the Hashable protocol, and optionals don't.) So you CAN'T make the keys in your dictionary optional
If you want the values of your dictionary to be Optionals then you need to declare them as Optionals.
So, for example, change
let photoDic: [String: String] = ["key1": "http://www.someDomain.com/image.jpg"]
to
let photoDic: [String: String?] = ["key1": "http://www.someDomain.com/image.jpg"]
(Note that the type of photoDic is changed to [String: String?].)
As mentioned already all keys in a dictionary are non-optional by definition.
Further in NSDictionary all values are non-optional by definition, too.
Be happy about that because
There is no need to check for nil.
The code will never crash.
A Swift dictionary can theoretically contain optional values but practically you are discouraged from using it. For compatibility reasons to NSDictionary a nil value indicates key is missing.

How to access CFDictionary in Swift 3?

I need to read and write some data from CFDictionary instances (to read and update EXIF data in photos). For the life of me, I cannot figure out how to do this in Swift 3. The signature for the call I want is:
func CFDictionaryGetValue(CFDictionary!, UnsafeRawPointer!)
How the heck do I convert my key (a string) to an UnsafeRawPointer so I can pass it to this call?
If you don't have to deal with other Core Foundation functions expecting an CFDictionary, you can simplify it by converting to Swift native Dictionary:
if let dict = cfDict as? [String: AnyObject] {
print(dict["key"])
}
Be careful converting a CFDictionary to a Swift native dictionary. The bridging is actually quite expensive as I just found out in my own code (yay for profiling!), so if it's being called quite a lot (as it was for me) this can become a big issue.
Remember that CFDictionary is toll-free bridged with NSDictionary. So, the fastest thing you can do looks more like this:
let cfDictionary: CFDictionary = <code that returns CFDictionary>
if let someValue = (cfDictionary as NSDictionary)["some key"] as? TargetType {
// do stuff with someValue
}
What about something like:
var key = "myKey"
let value = withUnsafePointer(to: &key){ upKey in
return CFDictionaryGetValue(myCFDictionary, upKey)
}
You can write something like this:
let key = "some key" as NSString
if let rawResult = CFDictionaryGetValue(cfDictionary, Unmanaged.passUnretained(key).toOpaque()) {
let result = Unmanaged<AnyObject>.fromOpaque(rawResult).takeUnretainedValue()
print(result)
}
But I guess you would not like to write such thing at any time you retrieve some data from that CFDictionary. You better convert it to Swift Dictionary as suggested in Code Different's answer.

Ambiguous use of a subscript when accessing a 2-dimensional array bridged from Obj-C

This code which ran perfectly in Xcode 7.0 now complains with a error : Ambiguous use of a subscript in Xcode 7.3.1 on the second line.
let ar = sender.draggingPasteboard().propertyListForType("ABLinkedPeopleUIDsPboardType") as! NSArray?
let uniqueID = ar![0][0] as! String
I understand that the NSArray on its own is now considered bad practice, but what do I need to do to get this to compile and run?
NSArray is a single-dimensioned array, but you're trying to use it as a two-dimensional array. I can't see how this would ever compile.
You need to translate into Swift types immediately so you can continue programming in Swift, not go adrift in a sea of force-unwrapped Optionals.
How about:
if let ar = sender.draggingPasteboard().propertyListForType("ABLinkedPeopleUIDsPboardType") as? [[String]] {
// I'm assuming you're expecting to get back a two-dimensional array of Strings, or in the Obj-C realm, an NSArray of NSArrays of NSStrings
let uniqueID = ar[0][0]
}
else {
print("Property List for ABLinkedetc. is not a 2D String Array!")
}
Hayden's link is the correct general discussion but if you're new to bridging Obj C to Swift it may be difficult to apply in your particular case.

Dictionary saved to NSUserDefaults always returns nil

I'm trying to save a dictionary to NSUserDefaults using the setObject() function but when I use the objectForKey() function to retrieve the dictionary it returns nil. Why is this happening?
var data = NSUserDefaults.standardUserDefaults();
var scoreboard = [Int : String]()
let scores = "scoresKey"
scoreboard[3] = "spencer"
scoreboard[6] = "brooke"
scoreboard[11] = "jason"
data.setObject(scoreboard, forKey: scores)
data.objectForKey(scores) // Returns nil
The first problem was that it's not possible to use NSUserDefaults in a Playground.
See: https://stackoverflow.com/a/31210205/3498950
A second problem is found when the code above runs in a normal iOS project. It throws an NSInvalidArgumentException since the dictionary was a non-property list object because the keys needed to be of type String.
Although NSDictionary and CFDictionary objects allow their keys to be
objects of any type, if the keys are not string objects, the
collections are not property-list objects.
See: "What is a Property List?" in the Apple Docs