Note – Article en cours de révision – version du 2015.09.30
Présenté à la conférence de la WWDC du printemps 2014, Swift est un langage de programmation développé par la société Apple. Il est destiné à la programmation d’applications sur les systèmes d’exploitation iOS (iPhone, iPad) et OS X (MAC).
Swift peut coexister avec l’Objective-C, langage actuel de programmation pour les systèmes d’exploitation d’Apple. Selon la firme de Cupertino , ce nouveau langage est rapide, moderne, sécurisé et interactif. Il contribuera probablement à atténuer la courbe d’apprentissage du développement d’application pour iOS entre autre grâce au module « playGround »
Mise à part la simplicité de la syntaxe du langage, une des nouveautés contribuant à l’apprentissage du langage est le module ‘PlayGround’.
PlayGround est un environnement où il est possible d’écrire des instructions Swift et d’obtenir des résultats en temps réel.
Une des façons d’obtenir ce ‘terrain de jeu’ est de créer un nouveau projet Xcode et de choisir l’option ‘Get started with a playground‘:
Action – Démarrons Xcode et sélectionnions ‘Get started with a playground’:
Nommons le projet:
Voici le terrain de jeux:
Référence Apple
Note: Ceci n’est pas un document d’introduction à la programmation. Nous prenons pour acquis que vous maîtrisez des concepts tel que: variables, boucles, tests, fonctions, objet, méthodes, propriétés, …
[table]
Type, Exemple
Int – UInt – UInt32, 0 ; 1 ; 55 ; 32767 ; -2
Float – CGFloat – Double, 2.345; 3.141592 ; -105678.999
Bool, true ; false
String , ‘Paul’ ; « CSTJ.qc.ca »
ClassName, UIButton ; UIColor ; UIImage ; Personnage ; …
[/table]
Syntaxe:
var nom:Type = valeur
Exemples :
var unEntier:Int = 99 var unReel:Float = 3.1415 var unDouble:Double = 3.1415920123456789087654321 var unBooleen:Bool = true var uneChaine:String = "CSTJ.TIM" var uneVue:UIView = UIView(frame : CGRectMake(0,0,50,50))
var unEntier = 99 var unReel = 3.1415 var unDouble = 3.1415920123456789087654321 var unBooleen = true var uneChaine = "CSTJ.TIM" var uneVue = UIView(frame : CGRectMake(0,0,50,50))
var uneVariableMutable = 2.2 // anglais : mutable uneVariableMutable = 1.4241 let uneVariableImmutable = 9.9 // anglais : immutable uneVariableImmutable = 123 // erreur de compilation
Note: Apple recommande l’utilisation de contantes à la place de variables lorsque possible.
var uneChaineAvecValeurNonOptionnelle :String println(uneChaineAvecValeurNonOptionnelle) // produira une erreur à l’exécution uneChaineAvecValeurNonOptionnelle = nil // produira une erreur à l’exécution
var uneChaineAvecValeurOptionnelle:String? If let uneChaineAvecValeurOptionnelleValidee = uneChaineAvecValeurOptionnelle { println(uneChaineAvecValeurOptionnelleValidee) } else { println("La variable ‘uneChaineAvecValeurOptionnelle’ n’est pas initialisée.") }
let tim = "Étudiants et étudiantes de CSTJ.TIM" let cours = "Introduction à Xcode" var msgBienvenue = tim + ", bienvenue au cours : " + cours msgBienvenue += "!"
Syntaxe:
« … \(votre code) … »
let anneeCours = 2014 msgBienvenue = "\(tim), bienvenue au cours : \(cours) de l'année \(anneeCours)" let nbSemaines = 15 let heuresSemaine = 4 let msg = "À \(heuresSemaine) heures de cours par semaine pendant \(nbSemaines) semaines, le cours a une durée de \(nbSemaines * heuresSemaine) heures."
let perso1 = "Fred " + "LeToff" let perso2 = "Fred LeToff" if perso1 == perso2 { println("\(perso1) et \(perso2) sont une seule et même personne") } else { println("Il n'y définitivement aucun lien de parenté entre \(perso1) et \(perso2)") }
let perso1 = "Fred " + "Letoff" let perso2 = "Fred LeToff" if perso1.lowercaseString == perso2.lowercaseString { println("\(perso1) et \(perso2) sont une seule et même personne") } else { println("Il n'y définitivement aucun lien de parenté entre \(perso1) et \(perso2)") }
let jeSuisUneChaineVide = "" let moiAussi = String() if jeSuisUneChaineVide.isEmpty && moiAussi.isEmpty { println("Il n'y a que du vide ici...") }
let prix:Float = 12.67 let quant = "2" var total = prix * Float(quant.toInt()!) let i = String(99) let peutEtreunNombre = "".toInt() if let unNombre = peutEtreunNombre { println("unNombre vaut \(unNombre)") } else { println("Meilleur chance la prochaine fois...") } // Note: pas encore de méthode String.toDouble() avec la beta3 de Xcode 6 // Conversion en utilisant un pond vers NSString // Note pas à tester 'nil', si chaine invalide alors retourne 0.0 let UnDouble = ("1.618" as NSString).floatValue let famille = "MAISONNEUVE.TIM" if (famille as NSString).containsString("TIM") { println("Membre de la famille...") } // Un autre exemple de méthodes disponibles avec la Class NSString let motCache = "swilfkjnsljfswjftswiftswjkfswftfdf" as NSString motCache.substringWithRange(NSMakeRange(7, 7)) println("Le mot caché est '\(motCache.substringWithRange(NSMakeRange(17, 5)))'")
// Définition
let jeSuisUnTableauDeChaine = ["Tintin", "Milou", "Tournesol"]
let jeSuisUnTabloDeChaine:Array<String> = jeSuisUnTableauDeChaine
var personnages:[String] = jeSuisUnTableauDeChaine var tableauVideDesChaines:[String] = []
let perso = personnages[0] let dernierPerso = personnages[personnages.count - 1] if !personnages.isEmpty { println("Il y a \(personnages.count) personnage(s) dans le tableau") }
// Note: retourne nil si pas trouvé. Utiliser la syntaxe 'if let' if let trouve = find(personnages, "Milou") { println("Nous avons trouvé Milou à la position \(trouve) dans le tableau des personnages") }
personnages+="Haddock" // Ne fonctionne plus à partir de la beta 5, il faut utiliser la syntaxe de la ligne suivante personnages.append("Haddock")
let retrait = personnages.removeAtIndex(1) println(retrait)
personnages.removeAll(keepCapacity: true)
personnages.insert("Malice", atIndex: 1) // Remplacer un (1) ou des items du tableau // Dans l'exemple suivant: remplacer items 0 à 1 par ... personnages[0...1] = ["Tuntun", "MunLun", "UnDun", "SunSun"] // ou 0..<2 personnages
personnages.sort {a, b in a < b} // voir 'closure' personnages
Définition: une collection de paires clé/valeur
// Syntaxe:
// … = [ clé1:valeur1, clé2:valeur2, …]
var personnage01 = [ "Nom": "Fred", "Force": "99", "Rang":"Novice"] // Accès simple personnage01["Nom"] // Accès avec test de la clé var nom = "" let cle = "Nom" if let temp = personnage01[cle] { nom = temp } else { println("Erreur:Clé '\(cle)' invalide") } nom // ******************************** var dict:[String:String] dict = ["Nom":"Fred"] dict["Age"] = "44" dict
let personnage02:Dictionary<String, AnyObject> = [ "Nom": "Tom", "Force": 34, "Rang":"Expert"]
let dictioVide = Dictionary<String, String>()
let nomPerso1 = personnage01["Nom"] var tabloPerso = [personnage01, personnage02] // let forcePerso2 = tabloPerso[1]["Force"] as Int // Note: Il est recommandé d'utiliser la syntaxe 'if set'. Un champ non défini retournera 'nil' let force = "Force" if let forcePerso1 = personnage01[force]?.toInt() { println("La \(force) de Perso1 = \(forcePerso1)") } else { println("Erreur: Impossible d'obtenir la \(force) du personne...") } // Utilisation d'un dictionnaire avec des éléments de type 'AnyObject' let personnage02:Dictionary<String, AnyObject> = [ "Nom": "Tom", "Force": 34, "Rang":"Expert"] let force = personnage02["Force"] as NSNumber let nom = personnage02["Nom"] as NSString println("Mon nom est: \(nom) et j'ai une force de: \(force)")
let personnage001/*:Dictionary<String, AnyObject>*/ = [ "Nom": "Le petit", "Force": 3, "Rang":"novice"] let personnage002:Dictionary<String, AnyObject> = [ "Nom": "Tom", "Force": "34", "Rang":"Expert"] var personnages02:Array<Dictionary<String, AnyObject>> = Array() personnages02.append(personnage001) personnages02.append(personnage002) personnages02[0]["Rang"] personnages02[1]["Force"] let total = 22 + personnages02[1]["Force"] // erreur! // Solution if let fp = personnages02[1]["Force"] as? Int { let i = fp + 22 i } else { println("Erreur sur le type du champ 'force'") }
let personnage0001 = [ "Nom": "Le petit", "Force": 3, "Rang":"novice", "Amis": ["Moi","Toi","Lui"]] let personnage0002 = [ "Nom": "Le grand", "Force": 78, "Rang":"Expert", "Amis": ["Eux","Nous","Ils"]] let personnage0003 = [ "Nom": "Le Boss", "Force": 99, "Rang":"Expert", "Amis": ["Monstre rouge"]] // AnyObject = classe de type NS var personnages3:Array<AnyObject> = Array() personnages3.append(personnage0001) personnages3.append(personnage0002) personnages3.append(personnage0003) println(personnages3) personnages.count let nom3 = personnages3[1]["Nom"] as! String let nbAmisPerso3 = (personnages3[2]["Amis"] as! NSArray).count // Bloucler sur les éléments du tableau (voir 'for in') // for (key,value) for perso in personnages3 { let nomPerso = perso["Nom"] as! String println("Amis de \(nomPerso): ") for ami in perso["Amis"] as! NSArray { println(" \(ami)") } println("-------------") }
Note: Ne fonctionne qu’avec <String, String>.
personnage01["Vies"] = "4"
Note: Ne fonctionne qu’avec <String, String>
personnage01["Rang"] = nil personnage01.removeValueForKey("Rang")
let personnages4 = ["Tintin", "Milou", "Tournesol", "Haddock", "Allan", "Rastapopoulos", "Dupont", "Dupond", "Muller", "Lampion", "Tapioca" ] let unEnsemble = NSMutableSet() // Un tableau sans doublon var nbIterations = 0 while unEnsemble.count <= 3 { let unNombreAuHasard = arc4random_uniform(UInt32(count(personnages))) let unPersonnage = personnages[Int(unNombreAuHasard)] println(unPersonnage) unEnsemble.addObject(unPersonnage) nbIterations++ } // while unEnsemble.count <= 3 println("Itérations: \(nbIterations), contenu de l'ensemble: \(unEnsemble)")
let personnages5 = ["Tintin", "Milou", "Tournesol", "Haddock", "Allan", "Rastapopoulos", "Dupont", "Dupond", "Muller", "Lampion", "Tapioca" ] let unEnsemble2 = NSCountedSet() // Un tableau sans doublon var nbIterations2 = 0 while unEnsemble2.count <= 3 { let unNombreAuHasard = arc4random_uniform(UInt32(count(personnages))) let unPersonnage = personnages[Int(unNombreAuHasard)] println(unPersonnage) unEnsemble2.addObject(unPersonnage) nbIterations++ } for x in unEnsemble2 { println("Personnage: \(x), nb d'insertions: \(unEnsemble2.countForObject(x))") }
Résultat:
Lampion Tintin Lampion Dupond Milou Haddock Muller Dupond Muller Muller Tournesol Allan Haddock Haddock Lampion Dupont Lampion Tapioca Personnage: Dupond, nb d'insertions: 2 Personnage: Muller, nb d'insertions: 3 Personnage: Lampion, nb d'insertions: 4 Personnage: Tintin, nb d'insertions: 1 Personnage: Allan, nb d'insertions: 1 Personnage: Milou, nb d'insertions: 1 Personnage: Tournesol, nb d'insertions: 1 Personnage: Dupont, nb d'insertions: 1 Personnage: Tapioca, nb d'insertions: 1 Personnage: Haddock, nb d'insertions: 3
let personnages = ["Tintin", "Milou", "Tournesol", "Haddock", "Allan", "Rastapopoulos", "Dupont", "Dupond", "Muller", "Lampion", "Tapioca" ] let indexSet = NSMutableIndexSet() indexSet.addIndex(0) indexSet.addIndex(2) indexSet.addIndex(4) (personnages as NSArray).objectsAtIndexes(indexSet) // retourne; Tintin, Tournesol et Allan
Syntaxe
for in range (…, ..<)
for inclusif in 0...5 { println("La valeur de inclusif 'closed range' est \(inclusif)") } for exclusif in 0..<5 { println("La valeur de exclusif 'half-open range' est \(exclusif)") } for car in "Bonjour le monde" { println(car) } let aventuresTintin = ["Tintin", "Milou", "Tournesol"] for indice in 0..<aventuresTintin.count{ let msg = "Le nom du personnage \(indice + 1) est \(aventuresTintin[indice])" } for personnage in aventuresTintin { let unPersonnage = personnage println(unPersonnage) }
for (affectation; condition; incrémentation) {}
for var compteur = 0, x = 2 ; compteur < aventuresTintin.count ; compteur++ { x*=2 println("Le nom du personnage \(compteur + 1) est \(aventuresTintin[compteur]) et x vaut \(x)") }
Syntaxe
…
var multiplesDeDeux = 2 while multiplesDeDeux < 16 { multiplesDeDeux*=2 println(multiplesDeDeux) } let dernierePosition = 1 var nouvellePosition:Int do { nouvellePosition = Int(arc4random()) % 2 // un nombre entre 0 et 1 println(nouvellePosition) } while nouvellePosition == dernierePosition
Syntaxe
if else (if else)
Syntaxe
switch valeurATester {
case valeur1: code à exécuter pour valeur1 …
case valeur2, valeur 3: code à exécuter pour valeur2 et valeur3 …
default: code si aucunes des valeurs
}
let unPersonnage = "Jean Valjean" print(unPersonnage) switch unPersonnage { case "Tintin", "Milou": println(" fait partie des Aventures de Tintin") case "Jean " + "Valjean": println(" est misérable!") default: println(" ne fait partie de rien...") } println("\n\n") /// switch sur tous les élements d'un tableau de chaines let expressionsHaddock = [ "Bachi-bouzouk", "Mille millions de mille sabords", "Bougres de faux jetons à la sauce tartare", "Espèce de porc-épic mal embouché", "Patagon de zoulous", "Bougre d’extrait de cornichon", "Simili-martien à la graisse de cabestan", "Sombre oryctérope", "Traîne-potence", "Bougres d’extrait de crétins des Alpes", ] for expression in expressionsHaddock { print("'\(expression)', est une expression ") switch count(expression) { case 1...15: print("facile") case 16..<33: print("difficile") default: println("impossible") } // switch println(" à retenir.") } // for println("\n\n") // boucle sur les éléments contenant le mot 'Bougre' for expression in expressionsHaddock { // N/A à partir de beta5: if expression.bridgeToObjectiveC().containsString("Bougre") if (expression as NSString).containsString("Bougre") { print("'\(expression)', est une expression ") switch count(expression) { case 1...15: print("facile") case 16..<33: print("difficile") default: println("impossible") } // switch println(" à retenir.") } // if bougre } // for
Syntaxe
func nomDeLaFonction (nomParam1:sonType, nomParm2:sonType, …) -> typeValeurDeRetour {
instruction1
instruction…n
return expression
}
func sommeDeDeuxNombres(nb1: Double, nb2: Double ) -> Double { // Note: nb1 et nb2 sont des constantes. Par exemple, nb++ = erreur de compilation. // Il est possible de déclarer avec 'Var', Var nb1: Double. return nb1 + nb2 } // sommeDeDeuxNombres() sommeDeDeuxNombres(3, 0.1415 ) sommeDeDeuxNombres(1, 2) // Fonction sans paramètre et retour func minimaliste() ->() { println("Je suis minimaliste)") } // minimaliste() // ou bien func mini() { println("minimaliste)") } // mini()
Prenons le cas de la fonction suivante:
func déplacerPersonnage(unPerso:Personnage, vitesse:Float, x:Float, y:Float){
…
}
À la lecture de la ligne suivante par une tierce partie, les valeurs inscrites ne seront pas claires.
déplacerPersonnage(bob, 5, 50, 200)
Swift permet de donner des noms externes aux paramètres (un concept importé d’Ojective-C’)
func deplacerPersonnage(nomPersonnage unPerso:String, aLaVitesse vitesse:Float, vers_x x:Float, et_y y:Float) { let intro = "Pour votre plus grand plaisir, nous déplacons le personnage " println("\(intro) \(unPerso) à la position x: \(x), y: \(y) à la vitesse \(vitesse)") } // deplacerPersonnage() deplacerPersonnage(nomPersonnage: "Bob", aLaVitesse: 99.9, vers_x: 50, et_y: 200)
Si nous voulons avoir comme nom externe, le nom du paramètre il suffit alors de le précéder de ‘#’
Par exemple,
func deplacerPerso(#nomPersonnage:String, #aLaVitesse:Float, #vers_x:Float, #et_y:Float) { let intro = "Au risque de nous répéter, nous déplacons encore une fois, le personnage " println("\(intro) \(nomPersonnage) à la position x: \(vers_x), y: \(et_y) à la vitesse \(aLaVitesse)") } deplacerPerso(nomPersonnage: "Fred", aLaVitesse: 23, vers_x: 100, et_y: 100)
Note: Il faut placer les paramètres facultatifs à la fin de la déclaration. Un nom externe sera automatiquement attribué aux paramètres avec une valeur par défaut.
func deplacerSimplement(nomPersonnage:String, aLaVitesse:Float = 50, vers_x:Float = 0, et_y:Float = 0) { let intro = "Avec les valeurs par défaut, le personnage " println("\(intro) \(nomPersonnage) reviendra à la maison à la vitesse \(aLaVitesse)") } deplacerSimplement("Milou") // ************* func aPlusBC(nb1:Int = 10, nb2:Int = 20, nb3:Int = 0) -> Int { return nb1 + nb2 + nb3 } aPlusBC(nb2: 3) aPlusBC(nb3:2, nb2: 3, nb1:4)
func unGrandMerci(#listeDesParticipants:String...){ let debutMessage = "Nous tenons à remercier " let finMessage = "pour leur participation à cette démonstration." var lesParticipants:String = "" for participant:String in listeDesParticipants { lesParticipants += participant + "," } // for println("\(debutMessage) \(lesParticipants), \(finMessage)") } // unGrandMerci() unGrandMerci() unGrandMerci(listeDesParticipants: "Tintin", "Milou", "Tounesol")
func aditionnerLesNombresEntiersSuivants(desNombres:Int...) -> Int?{ if desNombres.count == 0 { return nil } // if var total = 0 for nombre in desNombres { total += nombre } // for return total } let peutEtreUnTotal = aditionnerLesNombresEntiersSuivants(23,66, 123) if let total = peutEtreUnTotal { println("La somme des nombres donne \(total)") } else { println("Attention, pour obtenir un total, il faut fournir des nombres...") } // if
func changerNom(unNom:String) { unNom = "Ne fait pas cela!" // Produit une erreur car par défaut la déclaration est de type 'let'. } // changerNom() func ajouterAppel(var unNom:String, appel:String) { unNom = appel + " " + unNom // Changera la copie du paramètre. } // ajouterAppel() var nomNomEst = "Jean le marin" ajouterAppel(nomNomEst, "Monsieur") println("Mon nom est \(nomNomEst)") func ajouterAppel2(inout unNom:String, appel:String) { unNom = appel + " " + unNom // Changera la variable originale. } // ajouterAppel2() var nomNom = "Gille Valiquette" ajouterAppel2(&nomNom, "Monsieur") println("Mon nom est \(nomNom)")
let ami = ("Fred", "33", "Bon") ami.0 let ami2 = (Nom:"Toto", Age:"99", Type:"Ancien") ami2.Nom // Note, une fonction peut retourner un 'tuple' func uneFonction() -> (String, Double){ ... return ("PI", 3.141592) } let (nom,valeur) = uneFonction()
Syntaxe
…
struct StructurePersonnage { var nom = "Non définie" var niveau = 0 var force = 0.0 var positionX = 0.0 var positionY = 0.0 var vies:Int = 0 } // StructurePersonnage var jeSuisDeTypePersonnage = StructurePersonnage() var moiAussiJeSuisDeTypePersonnage = StructurePersonnage(nom: "Bozo le clown", niveau: 3, force: 55, positionX: 0, positionY: 0, vies: 3) jeSuisDeTypePersonnage.vies -= 1 println("jeSuisDeTypePersonnage = \(jeSuisDeTypePersonnage)")
Syntaxe
…
class ClassePersonnage { var nom = "Non définie" var niveau = 0 var force = 0.0 var positionX = 0.0 var positionY = 0.0 var vies:Int = 0 func description() ->String { return "Je suis le personnage \(nom) et j'ai une force de \(force)." } // description() func deplacer(#vitesse:Int, posX:Float, posY:Float) { println("\(nom) se déplace vers (\(posX),\(posY)) à la vitesse \(vitesse).") } // deplacer() } // ClassePersonnage var personnage = ClassePersonnage() personnage.nom = "La terreur des barbares" println(personnage.description()) personnage.deplacer(vitesse: 99, posX: 100, posY: 200)
class ClassePersoAvecInit { var nom:String var niveau:Float var force:Float var positionX:Float var positionY:Float var vies:Int // = 0 // Va produire une erreur init(unNom:String){ nom = unNom; niveau = 0; force = 0; positionX = 0; positionY = 0; } // init(unNom:String) init(unNom:String, uneForce:Float) { nom = unNom; force = uneForce niveau = 0; positionX = 0; positionY = 0; } // init(unNom:String, uneForce:Float) func description() ->String { return "Je suis le personnage \(nom) et j'ai une force de \(force)." } // description() } // ClassePersoAvecInit let perso_01 = ClassePersoAvecInit(unNom: "Tintin") let perso_02 = ClassePersoAvecInit(unNom: "Milou", uneForce: 99)
extension ClassePersoAvecInit { func retourAuCamp() { println("\(nom) revient au camp.") } } let perso_03 = ClassePersoAvecInit(unNom: "Le voyageur") perso_03.retourAuCamp() // --------------------------------- // Avec une classe du langage swift: extension String { func servirARien()->String{ return "Quelle perte de temps pour '\(self)'!" } // servirARien() } let ouf = "" ouf.servirARien() "allo".servirARien()
class PersonnageComplexe:ClassePersoAvecInit { func methodeQuiTue() ->String { return "\(nom) est prit d'une rage et anéantit tous ses adversaires... glup!" } } let mechant = PersonnageComplexe(unNom: "Boris") println(mechant.methodeQuiTue())
enum TypeDeJoueur { case Novice case Inter case Expert } let joueur01:TypeDeJoueur joueur01 = .Novice if joueur01 != .Expert { println("Le joueur n'est pas un expert") } // if switch joueur01 { case .Novice: println("Le joueur est un novice"); break case .Inter: break case .Expert:break // Note, pas besoin de clause 'default:' car tous les choix sont épuisés. } // switch
// Exemple 1: 10 nombres aléatoires entre 0..<100 for x in 0...10 { arc4random_uniform(100) } // Un peu de trigonométrie dans le temps... for x in 0...255 { let t = Double(x) * sin(Double(x) * M_PI_4/4) } for i in 0...180 { let x = cos(Double(i) / M_PI_4) }