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

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.

Related

List all window names in Swift

Iā€™m learning Swift. How do I fix the following code to list the window names?
import CoreGraphics
let windows = CGWindowListCopyWindowInfo(CGWindowListOption.optionAll, kCGNullWindowID)
for i in 0..<CFArrayGetCount(windows) {
if let window = CFArrayGetValueAtIndex(windows, i) {
print(CFDictionaryGetValue(window, kCGWindowName))
}
}
The error:
main.swift:6:32: error: cannot convert value of type 'UnsafeRawPointer' to expected argument type 'CFDictionary?'
print(CFDictionaryGetValue(window, kCGWindowName))
^~~~~~
as! CFDictionary
It becomes easier if you avoid using the Core Foundation types and methods, and bridge the values to native Swift types as early as possible.
Here, CGWindowListCopyWindowInfo() returns an optional CFArray of CFDictionaries, and that can be bridged to the corresponding Swift type [[String : Any]]. Then you can access its values with the usual Swift methods (array enumeration and dictionary subscripting):
if let windowInfo = CGWindowListCopyWindowInfo(.optionAll, kCGNullWindowID) as? [[ String : Any]] {
for windowDict in windowInfo {
if let windowName = windowDict[kCGWindowName as String] as? String {
print(windowName)
}
}
}
You can use unsafeBitCast(_:to:) to convert the opaque raw pointer to a CFDictionary. Note that you'll also need to convert the second parameter, to a raw pointer:
CFDictionaryGetValue(unsafeBitCast(window, to: CFDictionary.self), unsafeBitCast(kCGWindowName, to: UnsafeRawPointer.self))
unsafeBitCast(_:to:) tells the compiler to treat that variable as another type, however it's not very safe (thus the unsafe prefix), recommending to read the documentation for more details, especially the following note:
Warning
Calling this function breaks the guarantees of the Swift type system; use with extreme care.
In your particular case there should not be any problems using the function, since you're working with the appropriate types, as declared in the documentation of the Foundation functions you're calling.
Complete, workable code could look something like this:
import CoreGraphics
let windows = CGWindowListCopyWindowInfo(CGWindowListOption.optionAll, kCGNullWindowID)
for i in 0..<CFArrayGetCount(windows) {
let windowDict = unsafeBitCast(CFArrayGetValueAtIndex(windows, i), to: CFDictionary.self)
let rawWindowNameKey = unsafeBitCast(kCGWindowName, to: UnsafeRawPointer.self)
let rawWindowName = CFDictionaryGetValue(windowDict, rawWindowNameKey)
let windowName = unsafeBitCast(rawWindowName, to: CFString?.self) as String?
print(windowName ?? "")
}
Update
You can bring the CoreFoundation array sooner to the Swift world by casting right from the start:
let windows = CGWindowListCopyWindowInfo(CGWindowListOption.optionAll, kCGNullWindowID) as? [[AnyHashable: Any]]
windows?.forEach { window in
print(window[kCGWindowName])
}
The code is much readable, however it might pose performance problems, as the cast to [[AnyHashable: Any]]` can be expensive for large array consisting of large dictionaries.

Could not cast value of type 'Swift._SwiftDeferredNSArray' (0x11241ab30) to 'NSMutableArray' (0x1109ef598)

group.issues = temp.sortedArray(using: [sortDescriptor]) as! NSMutableArray
The system version is iOS 11.0, Xcode 9.0.
How can we solve this?
Depending on what you're actually doing, you're probably better off using a mutable Swift array, declared as a var. However, if you really do need reference semantics for some reason, you can make an NSMutableArray like this:
group.issues = NSMutableArray(array: temp.sortedArray(using: [sortDescriptor]))
The reason you cannot just cast it is because the Objective-C bridge bridges a Swift array to an immutable NSArray, which would throw an exception if you tried to call the mutating methods on it.
Again, though, the best thing to do is just use a Swift array, by declaring the issues property as a var:
struct Group {
...
var issues: [String] // or whatever type goes inside the array
...
}
Then just assign it like:
group.issues = temp.sortedArray(using: [sortDescriptor])
Swift 5 Easy and simple way
let arrPicOne = temparrpicFind as! [Any]
let arrPicTwo = temparrpic as! [Any]
let picResult = arrPicOne + arrPicTwo
arrGpic = NSMutableArray(array:picResult)

Variable used within its own initial value Swift 3

I try to convert my code to swift 3 an I have spent hours on the following error:
Type 'Any' has no subscript members
Here's was my original code:
let data: AnyObject = user.object(forKey: "profilePicture")![0]
I looked at the answers here but I'm still stuck. (I do programming as a hobby, I'm not a pro :/)
I've try that:
let object = object.object(forKey: "profilePicture") as? NSDictionary
let data: AnyObject = object![0] as AnyObject
But now I get this error:
Variable used within its own initial value
Second issue: Use always a different variable name as the method name, basically use more descriptive names than object anyway.
First issue: Tell the compiler the type of the value for profilePicture, apparently an array.
if let profilePictures = user["profilePicture"] as? [[String:Any]], !profilePictures.isEmpty {
let data = profilePictures[0]
}
However, the array might contain Data objects, if so use
if let profilePictures = user["profilePicture"] as? [Data], !profilePictures.isEmpty {
let data = profilePictures[0]
}
Or ā€“ what the key implies ā€“ the value for profilePicture is a single object, who knows (but you ...)
And finally, as always, don't use NSArray / NSDictionary in Swift.

ABAddressBook in Swift 2 fails

In Xcode7/Swift 2, the call
ABAddressBookCopyArrayOfAllPeople(addressBook)?.takeRetainedValue()
returns a non-null CFArrayRef, but when I cast it "as? NSArray" or "as? [ABRecordRef]" (which used to work) I now get nil. I understand that we should move to the Contacts framework, but the old way should still work for a while. What's up?
Correct syntax for Swift 2 requires you to string two casts in a row:
let rawCFArrayRef =
ABAddressBookCopyArrayOfAllPeople(addressBook)?.takeRetainedValue()
let swiftArray = rawCFArrayRef as? NSArray? as? [ABRecordRef]
Note that the ? on NSArray? is crucial.

Dictionary cannot be bridged from Objective-C -> Issues in 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])