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?
The primary difference between NSArray and NSMutableArray is that a mutable array can be changed/modified after it has been allocated and initialized, whereas an immutable array, NSArray , cannot.
In Swift, the NSArray class conforms to the ArrayLiteralConvertible protocol, which allows it to be initialized with array literals. For more information about object literals in Swift, see Literal Expression in The Swift Programming Language (Swift 4.1).
Overview. The NSMutableArray class declares the programmatic interface to objects that manage a modifiable array of objects. This class adds insertion and deletion operations to the basic array-handling behavior inherited from NSArray .
Swift allows creating a String Array with a specific size and a default value for each string in the array. To create a String Array with a specific size and default value, use the following syntax. array_size is a number that defines the size of this array.
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 }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With