Not able to cast __SwiftDeferredNSArray to NSMutableArray - ios

I have an array defined like so..
var filteredData = [[String: Any]]()
But when I try to convert it like so,
var filteredArray = filteredData as! NSMutableArray //CRASH HERE
dic = filteredArray[indexPath.row] as! NSMutableDictionary
cell.Sched_celltitle.text = "\(dic["name"]!)"
I get a crash like mentioned above saying Could not cast value of type 'Swift.__SwiftDeferredNSArray' (0x103..) to 'NSMutableArray' (0x200..).

Unlike the immutable types NSMutable... collection types are not related to native Swift collection types.
You cannot even cast the type from NSArray to NSMutableArray in Objective-C.
Just don't do that. NSMutable... collection types are pointless in Swift. The native types declared as variable are mutable for free.
var filteredData = [[String: Any]]()
...
let item = filteredArray[indexPath.row]
cell.Sched_celltitle.text = item["name"] as? String
A much better way is to create a custom struct replacing the quite unspecified [String:Any] dictionary

Related

Changing value in Dictionary in a Array in UserDefaults

I have a problem changing the value of a Dictionary in a Array
var array = (defaults.array(forKey: "Transactions")! as! [Dictionary<String, Any>])
(array.reversed()[index]["Title"] as! String) = titleTextField.text! // Cannot assign to immutable expression of type 'String'
Cannot assign to immutable expression of type 'String'
This is the error I get back
Is there a solution to this problem?
As Joakim points out, array.reversed() returns an immutable array.
Try this:
guard
var array = (defaults.array(forKey: "Transactions")! as? [Dictionary<String, Any>]),
let newText = titletextfield.text,
array[array.count-index]["Title"] = newText
(And then re-save your array to UserDefaults)
One more step will work
if var array = UserDefaults.standard.array(forKey: "Transactions") as? [Dictionary<String, Any>], let valueText = titleTextField.text {
array.reverse()
array[index]["Title"] = valueText
}

Crash while assigning element to another array

I have 2 arrays defined like so...
var smt_List_Offline: NSMutableArray = []
var smt_List_OfflineTemp: NSMutableArray = []
Now elsewhere I'm doing this...
self.smt_List_OfflineTemp = self.smt_List_Offline[0] as! NSMutableArray //CRASH HERE
self.smt_List_Offline.removeObject(at: 0)
But by doing this, I get a crash like indicated above with the message Could not cast value of type '__NSDictionaryM' (0x1b2345678) to 'NSMutableArray'
This self.smt_List_Offline[0] is a dictionary not an array so cast it to NSMutableDictionary/[String:Any]
let smt_List_OfflineFirst = self.smt_List_Offline[0] as! NSMutableDictionary
or
let smt_List_OfflineFirst = self.smt_List_Offline[0] as! [String:Any]
The error is very clear smt_List_Offline cannot be cast to type NSMutableArray. When you use as! you are telling the compiler this should crash if the casting is unsuccessful, so you should use as? instead.
if let smt_List_OfflineFirst = self.smt_List_Offline[0] as? NSMutableArray {
smt_List_OfflineTemp = smt_List_OfflineFirst
} else { print("Can't cast smt_List_OfflineFirst to NSMutableArray") }

Swift: use filter function on array of dictionaries? Error: Cannot invoke 'filter' with an argument list of type

The goal of this code below is to filter out dictionaries with a certain ID, where ID is a string.
let dictArray = networkData["dicts"] as! [[String:AnyObject]]
localData["dicts"] = dictArray.filter{ ($0["id"] as! String) != sample.getId() }
This code, however, generates an error:
Cannot invoke 'filter' with an argument list of type '(([String :
AnyObject]) throws -> Bool)'
Based on other SO answers like this one and this one, it seems the error is the dictionaries don't conform to Equatable.
So is the only option for using filter to create a custom class to hold the array of dictionaries and make that class conform to Equatable?
If so, perhaps it seems cleaner to simply iterate and create a new array.
Filtering [[String:AnyObject]] (a.k.a. Array>) results in another [[String:AnyObject]]. You're trying to assign this to a var of type AnyObject, which is not allowed in Swift 3, since arrays are structs, not objects.
Make a type-safe struct or object to hold this data, rather than a dict.
For example:
let dictArray = networkData["dicts"] as! [[String:AnyObject]]
let filteredDicts = dictArray.filter{ ($0["id"] as! String) != sample.getId() }
localData["dicts"] = filteredDicts
The issue isn't on hash objects not conform to Equatable because you are using String to do the comparing.
I have the code runs well in Playground:
// make sure data is type of [String: [[String: AnyObject]]]
// make sure filteredData is type of [String: [[String: AnyObject]]]
let key = "hashes"
if let hashArray = data[key] {
let id = sample.getId() // make sure it's String type
filteredData[key] = hashArray.filter { ($0["id"] as? String) != id }
}

cannot assign value of type 'Dictionary<_, _>' to type 'Dictionary'

I'm using Firebase to grab a set of menu items. I'm fairly new to swift and programming in general, and therefore don't always know how to structure my data properly. Since each menu item has a name, price, and description, I'm storing it as a dictionary, and trying to store all of the items together in an array of "Menu Item" dictionaries.
var menuItem : Dictionary = ["itemID" : String(), "itemName" : String(), "itemDescription" : String(), "itemPrice" : String()]
var menu = [[String: String]]()
func getMenuItems(){
let itemsRef = ref.childByAppendingPath("users/\(ref.authData.uid)/menu")
itemsRef.observeEventType(.Value, withBlock: { snapshot in
for id in snapshot.children.allObjects as! [FDataSnapshot] {
var newItem = self.menuItem
newItem["itemID"] = id.key as String
newItem["itemName"] = id.value["itemName"] as? String
newItem["itemDescription"] = id.value["itemDescription"] as? String
newItem["itemPrice"] = id.value["itemPrice"] as? String
self.menu.append(newItem)
}
})//End get menu info
}
When I try and run this code, I get this error:
(!) cannot assign value of type 'Dictionary<_, _>' to type 'Dictionary'
Am I declaring my dictionary wrong? Am I just adding unnecessary complexity by trying to store dictionaries in an array?

How can I cast an NSMutableArray to a Swift array of a specific type?

I am migrating my iOS project to Swift. I am doing this class by class. When I call Objective C methods from Swift, a lot of Objective C types are converted to their Swift counterparts.
In my case an Objective C NSMutableArray gets converted to Swift's Array<AnyObject>. Now here comes my problem. Within my Swift class, I get such an array back from an Objective C object. Now that I am in the Swift world, I would like to cast this array to a specific type instead of AnyObject, because I know for sure what kind of objects exist in this array.
The compiler won't let me do that! Let me simplify my problem by saying I want to cast to an array containing strings. This is what I tried:
var strings = myObjcObject.getStrings() as [String]
I get the following error from the compiler:
'String' is not identical to 'AnyObject'
I would have to agree with the compiler, since String is indeed not identical to AnyObject. But I don't see why that is a problem. I can downcast AnyObject to String if I want, right?
I also tried:
var strings = myObjcObject.getStrings() as? [String]
This seems to be a step in the right direction, but getStrings() returns an NSMutableArray so I get the following error:
'NSArray' is not a subtype of 'NSMutableArray'
Is there any way to do what I am trying to do here?
You can make this work with a double downcast, first to NSArray, then to [String]:
var strings = myObjcObject.getStrings() as NSArray as [String]
Tested in a Playground with:
import Foundation
var objCMutableArray = NSMutableArray(array: ["a", "b", "c"])
var swiftArray = objCMutableArray as NSArray as [String]
Update:
In later versions of Swift (at least 1.2), the compiler will complain about as [String]. Instead you should use an if let with a conditional downcast as?:
import Foundation
var objCMutableArray = NSMutableArray(array: ["a", "b", "c"])
if let swiftArray = objCMutableArray as NSArray as? [String] {
// Use swiftArray here
}
If you are absolutely sure that your NSMutableArray can be cast to [String], then you can use as! instead (but you probably shouldn't use this in most cases):
import Foundation
var objCMutableArray = NSMutableArray(array: ["a", "b", "c"])
var swiftArray = objCMutableArray as NSArray as! [String]
compactMap is your friend in Swift 4.1 and above, as well as in Swift 3.3-3.4 for that matter. This means that you don't have any double or forced casting.
let mutableArray = NSMutableArray(array: ["a", "b", "c"])
let swiftArray: [String] = mutableArray.compactMap { $0 as? String }
In previous versions of Swift, 2.0-3.2 and 4.0, you'll want to use flatMap for this purpose. The usage is the same as compactMap:
let swiftArray: [String] = mutableArray.flatMap { $0 as? String }
With Swift 1.2 the following will work:
let mutableArray = NSMutableArray(array: ["a", "b", "c"])
let swiftArray = NSArray(array: mutableArray) as? [String]
let mutableArray = NSMutableArray()
mutableArray.add("Abc")
mutableArray.add("def")
mutableArray.add("ghi")
if let array = mutableArray as? [String] {
print(array) // ["Abc", "def", "ghi"]
}
in Xcode 6.3 i used the following:
var mutableArray = NSMutableArray(array:"1", "2", "3")
let swiftArray = mutableArray as AnyObject as! [String]
for swift 3
you may consider the following code
let array: [String] = nsMutableArrayObject.copy() as! [String]
In my case compiler wanted me to write it like this to suppress all warnings and compilation problems, so not just that exclamation marks even if field imagesField is already declared with one, but also parentheses and "as!" to make it sure that nobody complains.
(imagesField!.images as! [UIImage]) 🤮
It made me quite uncomfortable... Swift could be nicer, its new language so... I made extension:
public static func cast(_ object: Any) -> Self {
return object as! Self
}
Assigned it to Array:
extension Array: CSLang {
}
And now I can write the same statement like this with the same effect:
[UIImage].cast(imagesField.images)
Like it or not, this is my way, less question and exclamation marks, better. I made also unit test:
func testCast() {
let array: NSMutableArray? = NSMutableArray()
array?.add("test string 1")
array?.add("test string 2")
let stringArray: [String] = [String].cast(array)
XCTAssertEqual("test string 2", stringArray[1])
}

Resources