web-dev-qa-db-fra.com

Comment puis-je convertir un NSMutableArray dans un tableau Swift d'un type spécifique?

Je migre mon projet iOS vers Swift. Je fais cette classe par classe. Lorsque j'appelle des méthodes Objective C à partir de Swift, de nombreux types Objective C sont convertis en équivalents Swift.

Dans mon cas, une Objective C NSMutableArray est convertie en Array<AnyObject> de Swift. Maintenant, voici mon problème. Dans ma classe Swift, je récupère un tel tableau à partir d'un objet Objective C. Maintenant que je suis dans le monde Swift, je voudrais convertir ce tableau en un type spécifique au lieu de AnyObject, car je sais avec certitude quel type d'objets existe dans ce tableau.

Le compilateur ne me laissera pas faire ça! Permettez-moi de simplifier mon problème en disant que je souhaite transtyper un tableau contenant des chaînes. C'est ce que j'ai essayé:

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

Je reçois l'erreur suivante du compilateur:

'String' n'est pas identique à 'AnyObject'

Je suis d’accord avec le compilateur, puisque String n’est en effet pas identique à AnyObject. Mais je ne vois pas pourquoi c'est un problème. Je peux convertir AnyObject en chaîne si je le souhaite, non?

J'ai aussi essayé:

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

Cela semble être un pas dans la bonne direction, mais getStrings () retourne une NSMutableArray et j'obtiens l'erreur suivante:

'NSArray' n'est pas un sous-type de 'NSMutableArray'

Y a-t-il un moyen de faire ce que j'essaye de faire ici?

58
Tom van Zummeren

Vous pouvez faire cela avec un double downcast, d'abord à NSArray, puis à [String]:

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

Testé dans un terrain de jeu avec:

import Foundation

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

Mettre à jour:

Dans les versions ultérieures de Swift (au moins 1.2), le compilateur se plaindra de as [String]. À la place, vous devez utiliser un if let avec un downcast conditionnel as?:

import Foundation

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

Si vous êtes absolument sûr que votre NSMutableArray peut être converti en [String], vous pouvez utiliser as! à la place (mais vous ne devriez probablement pas l'utiliser dans la plupart des cas):

import Foundation

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

compactMap est votre ami dans Swift 4.1 et versions ultérieures, ainsi que dans Swift 3.3-3.4 pour cette question. Cela signifie que vous n'avez pas de casting double ou forcé.

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

Dans les versions précédentes de Swift, 2.0-3.2 et 4.0, vous souhaiterez utiliser flatMap à cette fin. L'utilisation est la même que compactMap:

let swiftArray: [String] = mutableArray.flatMap { $0 as? String }
28
Simon Rice
let mutableArray = NSMutableArray()

mutableArray.add("Abc")
mutableArray.add("def")
mutableArray.add("ghi")

if let array = mutableArray as? [String] {
    print(array)    // ["Abc", "def", "ghi"]
}
5
Leo Dabus

Avec Swift 1.2, ce qui suit fonctionnera:

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

dans Xcode 6.3, j'ai utilisé ce qui suit:

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

pour Swift 3

vous pouvez considérer le code suivant 

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

J'ai créé une extension sur NSArray pour cela.

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

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
P5ycH0

Dans mon cas, le compilateur voulait que je l’écrive comme ceci pour supprimer tous les avertissements et les problèmes de compilation, donc pas seulement les points d’exclamation, même si le champ imagesField est déjà déclaré avec un, mais aussi entre parenthèses et "comme!" faire en sorte que personne ne se plaint.

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

Cela me rendait assez mal à l'aise ... Swift pourrait être plus gentil, son nouveau langage donc ... j'ai fait l'extension:

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

Assigné à Array:

extension Array: CSLang {
}

Et maintenant, je peux écrire la même déclaration comme celle-ci avec le même effet:

[UIImage].cast(imagesField.images)

Qu'on le veuille ou non, c'est mon chemin, moins de questions et d'exclamations, c'est mieux. J'ai aussi fait des tests unitaires:

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
Renetik