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?
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]
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 }
let mutableArray = NSMutableArray()
mutableArray.add("Abc")
mutableArray.add("def")
mutableArray.add("ghi")
if let array = mutableArray as? [String] {
print(array) // ["Abc", "def", "ghi"]
}
Avec Swift 1.2, ce qui suit fonctionnera:
let mutableArray = NSMutableArray(array: ["a", "b", "c"])
let swiftArray = NSArray(array: mutableArray) as? [String]
dans Xcode 6.3, j'ai utilisé ce qui suit:
var mutableArray = NSMutableArray(array:"1", "2", "3")
let swiftArray = mutableArray as AnyObject as! [String]
pour Swift 3
vous pouvez considérer le code suivant
let array: [String] = nsMutableArrayObject.copy() as! [String]
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()
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])
}