Hoe kan ik een NSMutableArray casten naar een Swift-array van een specifiek type?

Ik migreer mijn iOS-project naar Swift. Ik doe dit klas voor klas. Wanneer ik Objective C-methoden van Swift aanroep, worden veel Objective C-typen geconverteerd naar hun Swift-tegenhangers.

In mijn geval wordt een Objective C NSMutableArraygeconverteerd naar Swift’s Array<AnyObject>. Nu komt hier mijn probleem. Binnen mijn Swift-klasse krijg ik zo’n array terug van een Objective C-object. Nu ik in de Swift-wereld ben, wil ik deze array graag casten naar een specifiek type in plaats van AnyObject, omdat ik zeker weet wat voor soort objecten er in deze array bestaan.

De compiler laat me dat niet doen! Laat me mijn probleem vereenvoudigen door te zeggen dat ik naar een array met strings wil casten. Dit is wat ik heb geprobeerd:

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

Ik krijg de volgende foutmelding van de compiler:

‘String’ is niet identiek aan ‘AnyObject’

Ik zou het eens moeten zijn met de compiler, aangezien String inderdaad niet identiek is aan AnyObject. Maar ik zie niet in waarom dat een probleem is. Ik kan AnyObject downcasten naar String als ik dat wil, toch?

Ik heb ook geprobeerd:

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

Dit lijkt een stap in de goede richting, maar getStrings() retourneert een NSMutableArray, dus ik krijg de volgende foutmelding:

‘NSArray’ is geen subtype van ‘NSMutableArray’

Is er een manier om te doen wat ik hier probeer te doen?


Antwoord 1, autoriteit 100%

Je kunt dit laten werken met een dubbele downcast, eerst naar NSArray, dan naar [String]:

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

Getest in een speeltuin met:

import Foundation
var objCMutableArray = NSMutableArray(array: ["a", "b", "c"])
var swiftArray = objCMutableArray as NSArray as [String]

Bijwerken:

In latere versies van Swift (minstens 1.2), zal de compiler klagen over as [String]. In plaats daarvan moet je een if letgebruiken met een voorwaardelijke neerslachtige as?:

import Foundation
var objCMutableArray = NSMutableArray(array: ["a", "b", "c"])
if let swiftArray = objCMutableArray as NSArray as? [String] {
    // Use swiftArray here
}

Als u absoluutzeker bent dat uw NSMutableArraykan worden gecast naar [String], dan kunt u gebruik in plaats daarvan as!(maar in de meeste gevallen zou u dit waarschijnlijk niet moeten gebruiken):

import Foundation
var objCMutableArray = NSMutableArray(array: ["a", "b", "c"])
var swiftArray = objCMutableArray as NSArray as! [String]

Antwoord 2, autoriteit 26%

compactMapis je vriend in Swift 4.1 en hoger, evenals in Swift 3.3-3.4 wat dat betreft. Dit betekent dat je geen dubbele of gedwongen casting hebt.

let mutableArray = NSMutableArray(array: ["a", "b", "c"])
let swiftArray: [String] = mutableArray.compactMap { $0 as? String }

In eerdere versies van Swift, 2.0-3.2 en 4.0, wil je hiervoor flatMapgebruiken. Het gebruik is hetzelfde als compactMap:

let swiftArray: [String] = mutableArray.flatMap { $0 as? String }

Antwoord 3, autoriteit 4%

Met Swift 1.2 werkt het volgende:

let mutableArray = NSMutableArray(array: ["a", "b", "c"])
let swiftArray = NSArray(array: mutableArray) as? [String]

Antwoord 4, autoriteit 4%

let mutableArray = NSMutableArray()
mutableArray.add("Abc")
mutableArray.add("def")
mutableArray.add("ghi")
if let array = mutableArray as? [String] {
    print(array)    // ["Abc", "def", "ghi"]
}

Antwoord 5

in Xcode 6.3 heb ik het volgende gebruikt:

var mutableArray = NSMutableArray(array:"1", "2", "3")
let swiftArray = mutableArray as AnyObject as! [String]

Antwoord 6

voor snelle 3

u kunt de volgende code overwegen

let array: [String] = nsMutableArrayObject.copy() as! [String]

Antwoord 7

In mijn geval wilde de compiler dat ik het zo zou schrijven om alle waarschuwingen en compilatieproblemen te onderdrukken, dus niet alleen die uitroeptekens, zelfs als veld imagesField al met één is gedeclareerd, maar ook haakjes en “as!” om ervoor te zorgen dat niemand klaagt.

(imagesField!.images as! [UIImage]) 🤮

Het maakte me nogal ongemakkelijk… Swift kon aardiger zijn, de nieuwe taal dus… Ik maakte extensie:

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

Toegewezen aan Array:

extension Array: CSLang {
}

En nu kan ik dezelfde verklaring als deze schrijven met hetzelfde effect:

[UIImage].cast(imagesField.images)

Of je het nu leuk vindt of niet, dit is mijn manier, minder vraag- en uitroeptekens, beter. Ik heb ook een eenheidstest gemaakt:

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])
}

Other episodes