Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 895
Tom van Zummeren Avatar asked Sep 14 '14 20:09

Tom van Zummeren


People also ask

What is difference between NSArray and NSMutableArray?

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.

Can we use NSArray in Swift?

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).

What is NSMutableArray in Swift?

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 .

Are strings arrays in Swift?

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.


2 Answers

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] 
like image 78
Mike S Avatar answered Oct 19 '22 21:10

Mike S


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 } 
like image 23
Simon Rice Avatar answered Oct 19 '22 21:10

Simon Rice