How can I overlay an NSMutableArray on a Swift array of a specific type?

I am migrating my iOS project to Swift. I do this class by class. When I call Objective-C methods from Swift, many types of Objective-C are converted to their Swift copy.

In my case, Objective-C NSMutableArray converted to Swift Array<AnyObject> . Now here is my problem. In my Swift class, I get such an array from an Objective-C object. Now that I am in the Swift world, I would like to pass this array to a specific type instead of AnyObject , because I know exactly what objects exist in this array.

The compiler won't let me do this! Let me simplify my problem by saying that I want to pass 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 have to agree with the compiler since String is really not identical to AnyObject. But I do not understand why this is a problem. I can dump AnyObject into a 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 a way to do what I'm trying to do here?

+62
ios objective-c swift
Sep 14 '14 at 20:10
source share
8 answers

You can do this double-click job, first on NSArray , then on [String] :

 var strings = myObjcObject.getStrings() as NSArray as [String] 

Tested on the playground using:

 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 if let with conditionally pressing 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 added 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] 
+125
Sep 14 '14 at 20:30
source share

compactMap is your friend in Swift 4.1 and up, as well as in Swift 3.3-3.4. This means that you do not have 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 will need to use flatMap for this purpose. Usage is the same as compactMap :

 let swiftArray: [String] = mutableArray.flatMap { $0 as? String } 
+33
Jan 21 '16 at 14:35
source share

With Swift 1.2, the following will work:

 let mutableArray = NSMutableArray(array: ["a", "b", "c"]) let swiftArray = NSArray(array: mutableArray) as? [String] 
+5
Mar 13 '15 at 23:29
source share
 let mutableArray = NSMutableArray() mutableArray.add("Abc") mutableArray.add("def") mutableArray.add("ghi") if let array = mutableArray as? [String] { print(array) // ["Abc", "def", "ghi"] } 
+5
02 Sep '15 at 6:42
source share

in Xcode 6.3 I used the following:

 var mutableArray = NSMutableArray(array:"1", "2", "3") let swiftArray = mutableArray as AnyObject as! [String] 
+2
Jul 10 '15 at 12:12
source share

for quick 3

you can consider the following code

 let array: [String] = nsMutableArrayObject.copy() as! [String] 
+1
Feb 20 '17 at 13:33
source share

In my case, the compiler wanted me to write it in such a way as to suppress all warnings and compilation problems, so that not only this exclamation mark, even if the imagesField field is already declared with one, but also brackets and "how!" to make sure no one complains.

 (imagesField!.images as! [UIImage]) ๐Ÿคฎ 

It made me feel rather awkward ... Swift might be better, his new language is so ... I made an extension:

  public static func cast(_ object: Any) -> Self { return object as! Self } 

This is assigned to the array:

 extension Array: CSLang { } 

And now I can write the same statement as this with the same effect:

 [UIImage].cast(imagesField.images) 

Like it or not, this is my way, fewer questions and exclamation points are better. I also did a 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]) } 
0
Jan 21 '19 at 18:21
source share

I created an extension for NSArray for this.

 extension NSArray { func swiftArray<T>() -> [T] { let result: [T] = self.compactMap { $0 as? T } return result } } 

Using:

 class Test { var title: String init(title: String) { self.title = title } } let mutableArray = NSMutableArray() mutableArray.add(Test(title: "1")) mutableArray.add(Test(title: "2")) mutableArray.add(Test(title: "3")) mutableArray.add(Test(title: "4")) let tests: [Test] = mutableArray.swiftArray() 
0
Apr 15 '19 at 8:29
source share



All Articles