Guide du développeur iOS : d'Objective-C à Learning Swift

Publié: 2022-03-11

En 2008, Apple a annoncé et publié le SDK iPhone 2.0. Cet événement a déclenché une autre révolution dans le développement de logiciels et une nouvelle race de développeurs est née. Ils sont désormais reconnus comme développeurs iOS.

Beaucoup de ces développeurs n'avaient jamais utilisé Objective-C auparavant, et ce fut le premier défi qu'Apple leur lança. Malgré une syntaxe peu familière et une gestion manuelle de la mémoire, il a connu un immense succès, aidant à remplir l'App Store avec des dizaines de milliers d'applications. Apple a continuellement amélioré Objective-C à chaque version, en ajoutant des blocs et des littéraux, une gestion simplifiée de la mémoire avec un comptage automatique des références et de nombreuses autres fonctionnalités indicatives d'un langage de programmation moderne.

Et après 6 ans d'amélioration et de travail sur Objective-C, Apple a décidé de lancer un autre défi aux développeurs. Encore une fois, les développeurs iOS devront apprendre un nouveau langage de programmation : Swift . Swift supprime la gestion non sécurisée des pointeurs et introduit de nouvelles fonctionnalités puissantes, tout en maintenant l'interaction avec Objective-C et C.

Swift 1.0 est déjà une plate-forme de développement stable et solide, qui ne manquera pas d'évoluer de manière intéressante au cours des prochaines années. C'est le moment idéal pour commencer à explorer ce nouveau langage car c'est évidemment l'avenir du développement iOS.

Le but de ce didacticiel est de donner aux développeurs Objective-C un aperçu rapide des nouvelles fonctionnalités du langage Swift, vous aidant à passer à l'étape suivante et à commencer à adopter Swift dans votre travail quotidien. Je ne passerai pas trop de temps à expliquer Objective-C, et je supposerai que vous êtes familier avec le développement iOS.

Swift a changé la donne pour les développeurs Objective-C iOS.

Essayer Swift contre Objective-C

Pour commencer à explorer Swift, il vous suffit de télécharger XCode depuis l'App Store et de créer un terrain de jeu à expérimenter. Tous les exemples mentionnés dans cet article sont faits de cette façon.

La page d'accueil Swift d'Apple est la meilleure référence pour apprendre la programmation Swift. Vous le trouverez inestimable, et jusqu'à ce que vous soyez pleinement au courant du développement de Swift, je pense que vous reviendrez souvent ici.

Variables et constantes

La déclaration d'une variable dans Swift se fait à l'aide du mot-clé var .

 var x = 1 var s = "Hello"

Vous remarquerez que deux variables x et s sont de types différents. x est un entier, tandis que s est une chaîne. Swift est un langage de type sécurisé, et il déduira les types de variables de la valeur assignée. Si vous souhaitez rendre votre code plus lisible, vous pouvez éventuellement annoter le type de la variable :

 var y: Int y = 2

Les constantes sont similaires, mais vous les déclarez en utilisant let au lieu de var . La valeur d'une constante n'a pas besoin d'être connue au moment de la compilation, mais vous devez lui affecter une valeur exactement une fois.

 let c1 = 1 // Constant known at compile time var v = arc4random() let c2 = v // Constant known only at run time

Comme leur nom l'indique, ils sont immuables, donc le code suivant provoquera une erreur de compilation.

 let c = 1 c = 3 // error

D'autres types peuvent également être déclarés constants. Par exemple, le code suivant déclare un tableau comme une constante, et si vous tentez de modifier l'un des éléments, le compilateur Swift signalera une erreur :

 var arr2 = [4, 5, 6] arr2[0] = 8 print (arr2) // [8, 5, 6] let arr = [1, 2, 3] a[0] = 5 // error

Options

Les constantes doivent être initialisées lors de leur déclaration et les variables doivent être initialisées avant utilisation. Alors, où est l'équivalent nil d'Objective-C ? Swift introduit des valeurs facultatives . Les valeurs facultatives peuvent avoir une valeur ou être nil . Si vous jetez un œil au code suivant, vous remarquerez que x s'est vu attribuer une valeur Optional de 2014 . Cela signifie que le compilateur Swift était conscient que x pouvait également être nil .

 var s = "2014" var x = s.toInt() print(x) // Optional(2014)

Si vous modifiez ce code et affectez la valeur "abc" à s , qui ne peut pas être convertie en Integer, vous remarquerez que x est maintenant un nil .

 var s = "abc" var x = s.toInt() print(x) // nil

Le type de retour de la fonction toInt() est Int? , qui est un Int facultatif . Essayons d'appeler une fonction standard sur x :

 var x = "2014".toInt() print(x.successor()) // error

Le compilateur signale une erreur, car x est optionnel et pourrait potentiellement être nil . Nous devons d'abord tester x et nous assurer que la fonction successor est invoquée sur un nombre réel et non sur une valeur nil :

 var x = "2014".toInt() if x != nil { print(x!.successor()) // 2015 }

Notez que nous devons déballer x en ajoutant un point d'exclamation (!) . Lorsque nous sommes sûrs que x contient une valeur, nous pouvons y accéder. Sinon, nous aurons une erreur d'exécution. Nous pouvons également faire ce que Swift appelle une liaison optionnelle , en convertissant l'option en une variable non optionnelle

 let x = "123".toInt() if let y = x { print(y) }

Le code dans l'instruction if ne s'exécutera que si x a une valeur et l'affectera à y . Notez que nous n'avons pas besoin de déballer y , son type n'est pas facultatif puisque nous savons que x n'est pas nil .

Consultez le tutoriel Swift d'Apple pour lire plus de détails sur les options et les fonctionnalités intéressantes comme le chaînage optionnel

Interpolation de chaîne

En Objective-C, le formatage des chaînes se fait généralement avec la méthode stringWithFormat: :

 NSString *user = @"Gabriel"; int days = 3; NSString *s = [NSString stringWithFormat:@"posted by %@ (%d days ago)", user, days];

Swift a une fonctionnalité appelée interpolation de chaîne pour faire la même chose, mais elle est plus compacte et plus facile à lire :

 let user = "Gabriel" let days = 3 let s = "posted by \(user) \(days) ago"

Vous pouvez également utiliser des expressions :

 let width = 2 let height = 3 let s = "Area for square with sides \(width) and \(height) is \(width*height)"

Pour en savoir plus sur l'interpolation de chaîne de Swift et d'autres nouvelles fonctionnalités, rendez-vous ici.

Les fonctions

La définition de fonction dans Swift est différente de C. Un exemple de définition de fonction est ci-dessous :

 func someFunction(s:String, i: Int) -> Bool { ... // code }

Les fonctions Swift sont des types de première classe . Cela signifie que vous pouvez affecter des fonctions à des variables, les passer comme paramètres à d'autres fonctions ou leur faire renvoyer des types :

 func stringLength(s:String) -> Int { return countElements(s) } func stringValue(s:String) -> Int { if let x = s.toInt() { return x } return 0 } func doSomething(f:String -> Int, s:String) -> Int { return f(s).successor() } let f1 = stringLength let f2 = stringValue doSomething(f1, "123") // 4 doSomething(f2, "123") // 124

Encore une fois, Swift déduit les types de f1 et f2 ( String -> Int ), bien que nous aurions pu les définir explicitement :

 let f1:String -> Int = stringLength

Les fonctions peuvent également renvoyer d'autres fonctions :

 func compareGreaterThan(a: Int, b: Int) -> Bool { return a > b } func compareLessThan(a: Int, b: Int) -> Bool { return a < b } func comparator(greaterThan:Bool) -> (Int, Int) -> Bool { if greaterThan { return compareGreaterThan } else { return compareLessThan } } let f = comparator(true) println(f(5, 9))

Un guide des fonctions de Swift peut être trouvé ici.

Énumérations

Les énumérations dans Swift sont beaucoup plus puissantes que dans Objective-C. En tant que structures Swift, elles peuvent avoir des méthodes et sont transmises par valeur :

 enum MobileDevice : String { case iPhone = "iPhone", Andro, WP8 = "Windows Phone8", BB = "BlackBerry" func name() -> String { return self.toRaw() } } let m = MobileDevice.Android print(m.name()) // "Android"

Contrairement à Objective-C, les énumérations Swift peuvent affecter des chaînes, des caractères ou des flottants comme valeurs pour chaque membre, en plus des entiers. La méthode pratique toRaw() renvoie la valeur affectée à chaque membre.

Les énumérations peuvent également être paramétrées :

 enum Location { case Address(street:String, city:String) case LatLon(lat:Float, lon:Float) func description() -> String { switch self { case let .Address(street, city): return street + ", " + city case let .LatLon(lat, lon): return "(\(lat), \(lon))" } } } let loc1 = Location.Address(street: "2070 Fell St", city: "San Francisco") let loc2 = Location.LatLon(lat: 23.117, lon: 45.899) print(loc1.description()) // "2070 Fell St, San Francisco" print(loc2.description()) // "(23.117, 45.988)"

Plus d'informations sur les énumérations sont disponibles ici.

Tuples

Les tuples regroupent plusieurs valeurs en une seule valeur composée. Les valeurs d'un tuple peuvent être de n'importe quel type et ne doivent pas nécessairement être du même type les unes que les autres.

 let person = ("Gabriel", "Kirkpatrick") print(person.0) // Gabriel

Vous pouvez également nommer les éléments de tuple individuels :

 let person = (first: "Gabriel", last: "Kirkpatrick") print(person.first)

Les tuples sont extrêmement pratiques comme types de retour pour les fonctions qui doivent renvoyer plus d'une valeur :

 func intDivision(a: Int, b: Int) -> (quotient: Int, remainder: Int) { return (a/b, a%b) } print(intDivision(11, 3)) // (3, 2) let result = intDivision(15, 4) print(result.remainder) // 3

Contrairement à Objective-C, Swift prend en charge la correspondance de modèles dans une instruction switch :

 let complex = (2.0, 1.1) // real and imaginary parts switch complex { case (0, 0): println("Number is zero") case (_, 0): println("Number is real") default: println("Number is imaginary") }

Dans le second cas, nous ne nous soucions pas de la partie réelle du nombre, nous utilisons donc un _ pour correspondre à n'importe quoi. Vous pouvez également vérifier les conditions supplémentaires dans chaque cas. Pour cela, nous devons lier les valeurs du modèle à des constantes :

 let complex = (2.0, 1.1) switch complex { case (0, 0): println("Number is zero") case (let a, 0) where a > 0: println("Number is real and positive") case (let a, 0) where a < 0: println("Number is real and negative") case (0, let b) where b != 0: println("Number has only imaginary part") case let (a, b): println("Number is imaginary with distance \(a*a + b*b)") }

Notez comment nous devons lier uniquement les valeurs que nous allons utiliser dans la comparaison ou dans l'instruction case.

Pour en savoir plus sur les tuples, rendez-vous ici.

Classes et Structures

Contrairement à Objective-C, Swift ne vous oblige pas à créer des fichiers d'interface et d'implémentation séparés pour les classes et structures personnalisées. Au fur et à mesure que vous apprendrez Swift, vous apprendrez à définir une classe ou une structure dans un seul fichier, et l'interface externe de cette classe ou structure est automatiquement mise à disposition pour d'autres codes à utiliser.

Définition des classes

Les définitions de classe sont très simples :

 class Bottle { var volume: Int = 1000 func description() -> String { return "This bottle has \(volume) ml" } } let b = Bottle() print(b.description())

Comme vous pouvez le voir, la déclaration et l'implémentation sont dans le même fichier . Swift n'utilise plus les fichiers d'en-tête et d'implémentation. Ajoutons une étiquette à notre exemple :

 class Bottle { var volume: Int = 1000 var label:String func description() -> String { return "This bottle of \(label) has \(volume) ml" } }

Le compilateur se plaindra car label est une variable non facultative et ne contiendra pas de valeur lorsqu'une bouteille est instanciée. Nous devons ajouter un initialiseur :

 class Bottle { var volume: Int = 1000 var label:String init(label:String) { self.label = label } func description() -> String { return "This bottle of \(label) has \(volume) ml" } }

Ou, nous pourrions utiliser le type Optional pour une propriété, qui ne doit pas être initialisée. Dans l'exemple suivant, nous avons fait du volume un Optional Integer :

 class Bottle { var volume: Int? var label:String init(label:String) { self.label = label } func description() -> String { if self.volume != nil { return "This bottle of \(label) has \(volume!) ml" } else { return "A bootle of \(label)" } } }

Ouvrages

Le langage Swift a également des structs , mais ils sont beaucoup plus flexibles qu'en Objective-C. Le tutoriel de code suivant définit une struct :

 struct Seat { var row: Int var letter:String init (row: Int, letter:String) { self.row = row self.letter = letter } func description() -> String { return "\(row)-\(letter)" } }

Comme les classes dans Swift, les structures peuvent avoir des méthodes, des propriétés, des initialiseurs et se conformer aux protocoles. La principale différence entre les classes et les structures est que les classes sont passées par référence, tandis que les structures sont passées par value .

Cet exemple illustre le passage de classes par référence :

 let b = Bottle() print(b.description()) // "b" bottle has 1000 ml var b2 = b b.volume = 750 print(b2.description()) // "b" and "b2" bottles have 750 ml

Si nous essayons un cas similaire avec struct , vous remarquerez que les variables sont passées par valeur :

 var s1 = Seat(row: 14, letter:"A") var s2 = s1 s1.letter = "B" print(s1.description()) // 14-B print(s2.description()) // 14-A

Quand devons-nous utiliser struct et quand devons-nous utiliser class ? Comme dans Objective-C et C, utilisez des structures lorsque vous devez regrouper quelques valeurs et attendez-vous à ce qu'elles soient copiées plutôt que référencées. Par exemple, des nombres complexes, des points 2D ou 3D ou des couleurs RVB.

Une instance d'une classe est traditionnellement connue sous le nom d'objet. Cependant, les classes et les structures Swift ont des fonctionnalités beaucoup plus proches que dans d'autres langages, et de nombreuses fonctionnalités peuvent s'appliquer aux instances d'une classe ou d'un type de structure. Pour cette raison, le terme plus général utilisé dans la référence Swift est instance , qui s'applique à l'un de ces deux.

Apprenez les bases des classes et des structures Swift ici.

Propriétés

Comme nous l'avons vu précédemment, les propriétés dans Swift sont déclarées avec le mot-clé var dans une définition de classe ou de structure. Nous pouvons également déclarer des constantes avec une instruction let .

 struct FixedPointNumber { var digits: Int let decimals: Int } var n = FixedPointNumber(digits: 12345, decimals: 2) n.digits = 4567 // ok n.decimals = 3 // error, decimals is a constant

Gardez également à l'esprit que les propriétés de classe sont fortement référencées, sauf si vous les préfixez avec le mot clé weak . Cependant, il existe certaines subtilités avec des propriétés non facultatives faibles, alors lisez le chapitre sur le comptage automatique des références dans le guide Swift d'Apple.

Propriétés calculées

Les propriétés calculées ne stockent pas réellement de valeur. Au lieu de cela, ils fournissent un getter et un setter facultatif pour récupérer et définir indirectement d'autres propriétés et valeurs.

Le code suivant fournit un exemple de sign de valeur calculée :

 enum Sign { case Positive case Negative } struct SomeNumber { var number:Int var sign:Sign { get { if number < 0 { return Sign.Negative } else { return Sign.Positive } } set (newSign) { if (newSign == Sign.Negative) { self.number = -abs(self.number) } else { self.number = abs(self.number) } } } }

Nous pouvons également définir des propriétés en lecture seule en implémentant simplement un getter :

 struct SomeNumber { var number:Int var isEven:Bool { get { return number % 2 == 0 } } }

En Objective-C, les propriétés sont généralement soutenues par une variable d'instance, déclarée explicitement ou créée automatiquement par le compilateur. Dans Swift, en revanche, une propriété n'a pas de variable d'instance correspondante . Autrement dit, le magasin de sauvegarde d'une propriété n'est pas accessible directement. Supposons que nous ayons ceci en Objective-C

 // .h @interface OnlyInitialString : NSObject @property(strong) NSString *string; @end // .m @implementation OnlyInitialString - (void)setString:(NSString *newString) { if (newString.length > 0) { _string = [newString substringToIndex:1]; } else { _string = @""; } } @end

Étant donné que, dans Swift, les propriétés calculées n'ont pas de magasin de sauvegarde, nous devons faire quelque chose comme ceci :

 class OnlyInitialString { var initial:String = "" var string:String { set (newString) { if countElements(newString) > 0 { self.initial = newString.substringToIndex(advance(newString.startIndex, 1)) } else { self.initial = "" } } get { return self.initial } } }

Les propriétés sont expliquées plus en détail ici

À suivre

Il y a beaucoup de nouvelles choses importantes à découvrir dans Swift, comme les génériques, l'interaction avec les bibliothèques Objective-C, les fermetures, le chaînage facultatif et la surcharge des opérateurs. Un seul tutoriel ne peut pas décrire en détail un nouveau langage, mais je ne doute pas que beaucoup d'autres seront écrits sur l'apprentissage de la programmation Swift. Cependant, je pense que cette lecture rapide aidera de nombreux développeurs Objective-C, qui n'ont pas réussi à trouver le temps et à apprendre les détails du langage Swift, à se mettre sur la bonne voie et à laisser l'oiseau Swift les emmener vers de nouveaux sommets.