Introduction

Swift est un nouveau langage de programmation pour développer des applications iOS et OS X. Il est basé sur C et Objective-C, mais sans les contraintes de compatibilité de C. Swift adopte des modèles de programmation sûrs et ajoute des fonctionnalités modernes pour rendre la programmation plus simple, plus flexible et plus amusante. L'interface est construite sur les frameworks Cocoa et Cocoa Touch largement appréciés, montrant une nouvelle direction dans le développement logiciel.

apple-swift

Swift est en développement depuis de nombreuses années. Apple a construit son architecture sous-jacente sur des compilateurs, des débogueurs et des frameworks existants. Il utilise ARC (Automatic Reference Counting) pour simplifier la gestion de la mémoire. Notre pile de frameworks a toujours été basée sur Cocoa. Objective-C a évolué pour prendre en charge les blocs, les littéraux de collection et les modules, permettant d'utiliser des frameworks de langage modernes sans implication profonde. Grâce à ce travail fondamental, il est devenu possible d'introduire un nouveau langage de programmation dans le développement logiciel d'Apple.

Les développeurs Objective-C trouveront Swift familier. Swift adopte les paramètres nommés et le modèle d'objet dynamique d'Objective-C. Il assure l'interopérabilité avec les frameworks Cocoa et le mix-and-match. Sur ces fondations, Swift introduit de nombreuses nouvelles fonctionnalités et combine des fonctionnalités procédurales et orientées objet.

Swift est également accessible aux nouveaux programmeurs. C'est un langage de programmation système de qualité industrielle, mais aussi convivial qu'un langage de script. Il prend en charge les playgrounds, ce qui permet aux programmeurs d'expérimenter un extrait de code Swift et de voir immédiatement les résultats, sans les tracas de compiler et d'exécuter une application.

Swift intègre des idées issues des langages de programmation modernes et la sagesse de la culture d'ingénierie d'Apple. Le compilateur est optimisé pour la performance, tandis que le langage est optimisé pour le développement, sans compromettre ni l'un ni l'autre. Vous pouvez commencer par « Hello, world » et passer à un système entier. Tout cela fait de Swift une source d'innovation pour les développeurs de logiciels Apple.

Swift est un excellent moyen d'écrire des applications iOS et OS X, et il continuera d'introduire de nouvelles fonctionnalités. Nous avons hâte de voir ce que vous allez créer avec lui.

Premiers pas avec Swift

L'apprentissage d'un nouveau langage devrait commencer par l'affichage de « Hello, world ». En Swift, cela ne prend qu'une seule ligne :

println("Hello, world")

Si vous avez déjà écrit du code C ou Objective-C, cette syntaxe vous semble familière. En Swift, il s'agit d'un programme complet. Vous n'avez pas besoin d'importer une bibliothèque distincte pour les entrées/sorties et la gestion des chaînes de caractères. Le code à portée globale constitue le point d'entrée du programme, donc vous n'avez pas besoin d'écrire une fonction main(). Vous n'avez pas non plus besoin d'écrire des points-virgules après chaque instruction. Ce guide fournit suffisamment d'informations pour vous apprendre à accomplir une tâche de programmation. Ne vous inquiétez pas des choses que vous ne comprenez pas encore ; tout ce qui n'est pas expliqué clairement ici sera couvert en détail plus loin dans ce livre.

Remarque :En guise de bonne pratique, vous pouvez ouvrir ce chapitre dans un playground Xcode. Les playgrounds vous permettent de modifier le code et de voir immédiatement les résultats.

Valeurs simples

Utilisez let pour définir une constante et var pour définir une variable. La valeur d'une constante n'a pas besoin d'être connue au moment de la compilation, mais vous devez l'assigner au moins une fois. Cela signifie que vous pouvez utiliser une constante pour nommer une valeur que vous déterminez une fois mais utilisez à plusieurs endroits.

var myVariable = 42
myVariable = 50
let myConstant = 42

Remarque :Les définitions de constantes ici sont similaires aux variables des langages de programmation fonctionnelle : une fois assignées, elles ne peuvent pas être modifiées. Utilisez-les souvent ; c'est bon pour vous.

Une constante ou une variable doit avoir le même type que la valeur que vous assignez. Par conséquent, vous n'avez pas à définir explicitement le type. Fournissez simplement une valeur pour créer une constante ou une variable, et laissez le compilateur inférer son type. Dans l'exemple ci-dessus, le compilateur infère que myVariable est de type entier car sa valeur initiale est un entier.

Remarque :Les types sont liés aux noms de variables, ce qui fait de ce langage un langage à typage statique. Cela aide à l'optimisation statique. Cela diffère de Python, JavaScript et d'autres. Si la valeur initiale ne fournit pas suffisamment d'informations (ou s'il n'y a pas de valeur initiale), vous pouvez écrire le type après le nom de la variable, séparé par deux-points.

let imlicitInteger = 70
let imlicitDouble = 70.0
let explicitDouble: Double = 70

Les valeurs ne sont jamais converties implicitement vers d'autres types. Si vous devez convertir une valeur vers un type différent, construisez explicitement une instance du type souhaité.

let label = "The width is "
let width = 94
let widthLabel = label + String(width)

Il existe une manière encore plus simple d'inclure des valeurs dans des chaînes : écrivez la valeur entre parenthèses et placez une barre oblique inverse ("\") avant les parenthèses. Par exemple :

let apples = 3
let oranges = 5     
let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruit."

Pour créer un tableau ou un dictionnaire, utilisez des crochets "[]", et accédez à leurs éléments en écrivant l'index ou la clé entre crochets.

var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"

var occupations = [
   "Malcolm": "Captain",
   "Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"

Pour créer un tableau ou un dictionnaire vide, utilisez la syntaxe d'initialisation :

let emptyArray = String[]()
let emptyDictionary = Dictionary<String, Float>()

Si les informations de type ne peuvent pas être inférées, vous pouvez écrire un tableau vide sous la forme "[]" et un dictionnaire vide sous la forme "[:]" — par exemple, lorsque vous définissez une variable d'un type connu et la passez comme argument à une fonction.

shoppingList = []   //去购物并买些东西

Flux de contrôle

Utilisez if et switch pour les conditionnels. Utilisez for-in, for, while et do-while pour les boucles. Les parenthèses ne sont pas obligatoires, mais les accolades autour du corps sont obligatoires.

let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
    if score > 50 {
        teamScores += 3
    } else {
        teamScores += 1
    }
}
teamScore

Dans une instruction if, la condition doit être une expression booléenne — cela signifie que if score { ... } est une erreur et ne peut pas être implicitement comparé à 0. Vous pouvez utiliser if et let ensemble pour manipuler des valeurs qui pourraient être absentes. Ces valeurs sont optionnelles. Une valeur optionnelle peut contenir une valeur ou contenir nil pour indiquer que la valeur n'existe pas encore. Écrivez un point d'interrogation "?" après le type pour marquer la valeur comme optionnelle.

var optionalString: String? = "Hello"
optionalString == nil

var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
    greeting = "Hello, \(name)"
}

Remarque :Si la valeur optionnelle est nil, la condition est fausse et le code entre accolades est ignoré. Sinon, la valeur optionnelle est déballée et affectée à une constante, ce qui rend la valeur déballée disponible dans le bloc de code.

switch prend en charge de nombreux types de données et une grande variété de comparaisons — il n'est pas limité aux entiers et aux tests d'égalité.

let vegetable = "red pepper"
switch vegetable {
case "celery":
    let vegetableComment = "Add some raisins and make ants on a log."
case "cucumber", "watercress":
    let vegetableComment = "That would make a good tea sandwich."
case let x where x.hasSuffix("pepper"):
    let vegetableComment = "Is it a spicy \(x)?"
default:    
    let vegetableComment = "Everything tastes good in soup."
}

Remarque :

Après l'exécution d'un cas correspondant, le programme quitte le switch au lieu de passer au cas suivant. Vous n'avez donc plus besoin de break pour quitter le switch.

Vous pouvez utiliser for-in pour parcourir chaque élément d'un dictionnaire, en fournissant une paire de noms à utiliser pour chaque paire clé-valeur.

let interestingNumbers = [
    "Prime": [2, 3, 5, 7, 11, 13],
    "Fibonacci": [1, 1, 2, 3, 5, 8],
    "Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
    for number in numbers {
        if number > largest {
            largest = number
        }
    }
}

Utilisez while pour répéter un bloc de code jusqu'à ce que la condition change. La condition de boucle peut être placée à la fin pour garantir que la boucle s'exécute au moins une fois.

var n = 2
while n < 100 {
    n = n * 2
}
n

var m = 2
do {
    m = m * 2
} while m < 100
m

Vous pouvez conserver un index dans une boucle, en utilisant ".." pour indiquer une plage d'indices, ou en déclarant explicitement une valeur initiale, une condition et un incrément. Ces deux boucles font la même chose :

var firstForLoop = 0
for i in 0..3 {
    firstForLoop += i
}
firstForLoop

var secondForLoop = 0
for var i = 0; i < 3; ++i {
    secondForLoop += 1
}
secondForLoop

Une plage construite avec .. ignore la valeur la plus élevée, tandis qu'une plage construite avec ... inclut les deux valeurs.

Fonctions et fermetures

Utilisez func pour déclarer une fonction. Appelez une fonction par son nom suivi d'une liste d'arguments entre parenthèses. Utilisez -> pour séparer les noms des paramètres et le type de retour.

func greet(name: String, day: String) -> String {
    return "Hello \(name), today is \(day)."
}
greet("Bob", "Tuesday")

Utilisez un tuple pour renvoyer plusieurs valeurs.

func getGasPrices() -> (Double, Double, Double) {
    return (3.59, 3.69, 3.79)
}
getGasPrices()

Une fonction peut accepter un nombre variable d’arguments, rassemblés dans un tableau.

func sumOf(numbers: Int...) -> Int {
    var sum = 0
    for number in numbers {
        sum += number
    }
    return sum
}
sumOf()
sumOf(42, 597, 12)

Les fonctions peuvent être imbriquées. Une fonction imbriquée peut accéder aux variables de la fonction dans laquelle elle est définie. Vous pouvez utiliser des fonctions imbriquées pour organiser du code qui serait autrement trop long ou trop complexe.

func returnFifteen() -> Int {
    var y = 10
    func add() {
        y += 5
    }
    add()
    return y
}   
returnFifteen()

Les fonctions sont des types de première classe. Cela signifie qu’une fonction peut renvoyer une autre fonction.

func makeIncrementer() -> (Int -> Int) {
    func addOne(number: Int) -> Int {
        return 1 + number
    }
    return addOne
}
var increment = makeIncrementer()
increment(7)

Une fonction peut prendre d’autres fonctions comme arguments.

func hasAnyMatches(list: Int[], condition: Int -> Bool) -> Bool {
    for item in list {
        if condition(item) {
            return true
        }
    }
    return false
}

func lessThanTen(number: Int) -> Bool {
    return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(numbers, lessThanTen)

Les fonctions sont en fait un cas particulier des fermetures. Vous pouvez écrire une fermeture sans nom, simplement en la plaçant entre accolades. Utilisez in pour séparer les arguments et la valeur de retour du corps.

numbers.map({
    (number: Int) -> Int in
    let result = 3 * number
    return result
    })

Il existe plusieurs options lors de l’écriture de fermetures. Lorsque le type d’une fermeture est déjà connu, par exemple pour un rappel, vous pouvez omettre ses paramètres et son type de retour, ou les deux. Une fermeture à expression unique peut directement renvoyer sa valeur.

numbers.map({number in 3 * number})

Vous pouvez faire référence à un argument par son numéro plutôt que par son nom, ce qui est utile pour les fermetures très courtes. Une fermeture passée comme dernier argument à une fonction peut être placée après les parenthèses de l’appel de la fonction.

sort([1, 5, 3, 12, 2]) { $0 > $1 }

Objets et classes

Utilisez class pour créer une classe. Une déclaration de propriété n’est qu’une déclaration de constante ou de variable dans le contexte de la classe. Les méthodes et les fonctions s’écrivent de la même manière.

class Shape {
    var numberOfSides = 0
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

Créez une instance d’une classe en plaçant des parenthèses après le nom de la classe. Utilisez la syntaxe point pour accéder aux propriétés et méthodes de l’instance.

var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

Cette version de la classe Shape omet quelque chose d’important : un initialiseur pour configurer la classe lors de la création d’une instance. Utilisez init pour en créer un.

class NamedShape {
    var numberOfSides: Int = 0
    var name: String

    init(name: String) {
        self.name = name
    }   

    func simpleDescription() -> String {
        return "A Shape with \(numberOfSides) sides."
    }
}

Note:self est utilisé pour distinguer la propriété name de l’argument name. Les initialiseurs sont déclarés comme des fonctions, sauf qu’ils créent une instance de la classe. Chaque propriété a besoin d’une valeur assignée, soit dans sa déclaration, soit dans l’initialiseur.

Utilisez deinit pour créer un déinitialiseur afin d'effectuer un nettoyage lorsque l'objet est détruit.

Les sous-classes incluent le nom de leur superclasse, séparé par un deux-points. Il n'est pas nécessaire de déclarer une superclasse lorsqu'on hérite de la classe racine standard, vous pouvez donc l'omettre.

Les méthodes d'une sous-classe peuvent redéfinir l'implémentation de la superclasse en les marquant avec override. Sans override, le compilateur le traite comme une erreur. Le compilateur vérifie également que les méthodes marquées override redéfinissent réellement une méthode de la superclasse.

class Square: NamedShape {
    var sideLength: Double

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 4
    }

    func area() -> Double {
        return sideLength * sideLength
    }

    override func simpleDescription() -> String {
        return "A square with sides of length \(sideLength)."
    }
}

let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()

Remarque :Les propriétés peuvent avoir des getters et des setters.

class EquilateralTriangle: NamedShape {
    var sideLength: Double = 0.0

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name)
        numberOfSides = 3
    }

    var perimeter: Double {
    get {
        return 3.0 * sideLength
    }
    set {
        sideLength = newValue / 3.0
    }
    }

    override func simpleDescription() -> String {
        return "An equilateral triangle with sides of length \(sideLength)."
    }
}

var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
triangle.perimeter
triangle.perimeter = 9.9
triangle.sideLength

Dans le setter de perimeter, la nouvelle valeur a le nom implicite newValue. Vous pouvez fournir un nom distinct entre parenthèses après set si vous préférez.

Remarque :L'initialiseur de EquilateralTriangle comporte trois étapes différentes :

  • Définir la valeur des propriétés
  • Appeler l'initialiseur de la superclasse
  • Modifiez la valeur d'une propriété définie par la superclasse, et ajoutez ici aussi du travail supplémentaire pour utiliser des méthodes, des getters et des setters.

Si vous n'avez pas besoin d'une propriété calculée, mais que vous souhaitez tout de même fournir un travail à effectuer après avoir défini une valeur, utilisez willSet et didSet. Par exemple, la classe suivante garantit que la longueur du côté de son triangle est égale à la longueur du côté du rectangle.

class TriangleAndSquare {
    var triangle: EquilaterTriangle {
    willSet {
        square.sideLength = newValue.sideLength
    }
    }

    var square: Square {
    willSet {
        triangle.sideLength = newValue.sideLength
    }
    }

    init(size: Double, name: String) {
        square = Square(sideLength: size, name: name)
        triangle = EquilaterTriangle(sideLength: size, name: name)
    }
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
triangleAndSquare.square.sideLength
triangleAndSquare.triangle.sideLength
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
triangleAndSquare.triangle.sideLength

Il existe une différence importante entre les méthodes de classe et les fonctions. Les noms des paramètres de fonction ne sont utilisés qu'à l'intérieur de la fonction, mais les noms des paramètres de méthode peuvent également être utilisés lors de l'appel de la méthode (sauf le premier paramètre). Par défaut, une méthode a un paramètre du même nom, et lorsqu'elle est appelée, c'est le paramètre lui-même. Vous pouvez spécifier un second nom à utiliser à l'intérieur de la méthode.

class Counter {
    var count: Int = 0
    func incrementBy(amount: Int, numberOfTimes times: Int) {
        count += amount * times
    }
}
var counter = Counter()
counter.incrementBy(2, numberOfTimes: 7)

Lorsque vous travaillez avec des valeurs optionnelles, vous pouvez écrire "?" avant un opérateur, comme pour les méthodes et les propriétés. Si la valeur avant "?" est déjà nil, tout ce qui suit "?" est automatiquement ignoré, et l'expression entière vaut nil. Sinon, la valeur optionnelle est déballée, et tout ce qui suit "?" s'applique à la valeur déballée. Dans les deux cas, la valeur de l'expression entière est une valeur optionnelle.

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength

Énumérations et structures

Utilisez enum pour créer une énumération. Comme les classes et les autres types nommés, les énumérations peuvent avoir des méthodes.

enum Rank: Int {
    case Ace = 1
    case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
    case Jack, Queen, King
    func simpleDescrition() -> String {
        switch self {
        case .Ace:
            return "ace"
        case .Jack:
            return "jack"
        case .Queen:
            return "queen"
        case .King:
            return "king"
        default:
            return String(self.toRaw())
        }
    }
}
let ace = Rank.Ace  
let aceRawValue = ace.toRaw()

Dans l'exemple ci-dessus, le type de valeur brute est Int, donc vous n'avez besoin de spécifier que la première valeur brute. Les valeurs brutes suivantes sont attribuées dans l'ordre. Vous pouvez aussi utiliser des chaînes de caractères ou des nombres à virgule flottante comme valeurs brutes d'une énumération.

Utilisez les fonctions toRaw et fromRaw pour convertir entre valeurs brutes et valeurs d'énumération.

if let convertedRank = Rank.fromRaw(3) {
    let threeDescription = convertedRank.simpleDescription()
}

Les valeurs des membres d'une énumération sont des valeurs réelles, et non des valeurs brutes écrites d'une autre manière. En fait, dans certains cas, il n'y a pas de valeur brute, c'est-à-dire lorsque vous n'en fournissez pas.

enum Suit {
    case Spades, Hearts, Diamonds, Clubs
    func simpleDescription() -> String {
        switch self {
        case .Spades:
            return "spades"
        case .Hearts:
            return "hearts"
        case .Diamonds:
            return "dismonds"
        case .Clubs:
            return "clubs"
        }
    }
}
let hearts = Suit.Hearts    
let heartsDescription = hearts.simpleDescription()

Notez les deux façons de référencer le membre Hearts ci-dessus : lors de l'affectation à la constante hearts, le membre d'énumération Suit.Hearts est référencé par son nom complet car la constante n'a pas de type explicite. Dans le switch, l'énumération est référencée via .Hearts car la valeur de self est déjà connue. Vous pouvez utiliser la méthode pratique chaque fois que possible.

Utilisez struct pour créer une structure. Les structures prennent en charge un grand nombre des mêmes comportements que les classes, y compris les méthodes et les constructeurs. Une différence importante est que les structures sont toujours copiées lorsqu'elles sont transmises dans le code (transmission par valeur), tandis que les classes sont transmises par référence.

struct Card {
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String {
        return "The \(rank.simpleDescription()) of \
        (suit.simpleDescription())"
    }
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
<p><strong>注意:</strong>

Un membre d'instance d'une énumération peut avoir des valeurs d'instance. Les instances d'un même membre d'énumération peuvent avoir des valeurs différentes. Vous assignez les valeurs lors de la création de l'instance. La différence entre les valeurs assignées et les valeurs brutes : les valeurs brutes d'une énumération sont les mêmes pour toutes ses instances, et vous fournissez les valeurs brutes lors de la définition de l'énumération.

Par exemple, supposons qu'une situation nécessite d'obtenir les heures de lever et de coucher du soleil à partir d'un serveur. Le serveur peut répondre avec les mêmes informations ou un message d'erreur.

enum ServerResponse {
    case Result(String, String)
    case Error(String)
}

let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")

switch success {
case let .Result(sunrise, sunset):
    let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
    let serverResponse = "Failure... \(error)"
}

Remarque :Les heures de lever et de coucher du soleil sont en fait sélectionnées à partir d'une correspondance partielle de ServerResponse.

Références

Tutoriel de prise en main du langage de programmation Apple Swift :http://gashero.iteye.com/blog/2075324

Le langage de programmation Swift :https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/