Accueil Nos publications Blog Les Nouveautés de Swift 2

Les Nouveautés de Swift 2

Logo Swift

Swift a été annoncé par Apple il y a seulement un an et pourtant il est déjà présent partout. Si, à l’époque, l’article que nous avions publié à ce sujet s’adressait surtout aux curieux, il a eu un succès important et aujourd’hui la plupart des développeurs semble confiante sur le fait de démarrer un projet iOS ou OS X en Swift. Plusieurs conférences sur le langage ont même eu lieu, comme la dotSwift en mars dernier à Paris. Il est déjà utilisé dans des applications en production, comme celle de Coursera, et nous avons commencé à l’utiliser aussi !

Après un an chargé en itérations rapides, le langage a fait l’objet d’évolutions considérables annoncées à l’occasion de la WWDC qui a eu lieu la semaine dernière à San Francisco. Apple semble prendre ce sujet au sérieux et confirme explicitement que Swift est bel et bien l’avenir de sa plate-forme.

Voyons de plus près les nouveautés les plus notables de cette version 2.

Swift News

Open Source !

Avec seulement un an d’ancienneté, Swift est déjà dans le top 15 du classement TIOBE et remonte comme le langage le plus aimé dans sondage Stack Overflow. Et pourtant il promettait jusqu’à présent de ne servir qu’à développer des applications iPhone, iPad, OS X et AppleWatch, avec Apple comme seul et unique contributeur.

Alors imaginez ce qui pourra arriver après qu’Apple ait ouvert le langage. D’ici la fin de l’année, les sources du compilateur et des bibliothèques standards seront diffusées pour iOS, OS X et Linux, et Apple acceptera les contributions de la communauté.

Swift Open Source

Attention, ça ne veut pas dire que vous pourrez développer des applications iPhone sur Linux, de nombreuses API du Framework restant encore sous scellés.

Gestion des erreurs

Enfin ! diront certains.

Le langage Swift ne proposait aucun moyen pour gérer les erreurs, et la convention était d’ailleurs d’utiliser un tuple ou un enum avec valeur associée dans les cas où “je vais peut-être retourner quelque chose, ou bien une erreur.”

Pour aider le compilateur à identifier les fonctions qui peuvent renvoyer des erreurs, on les marque avec le mot-clé throws.

func thisCanFail() throws {
    …
}

Lors de l’appel à cette fonction, le compilateur va renvoyer une erreur et demander l’usage de try, mais il ne s’utilise pas comme le trycatch auquel on peut être habitué. Ici, il est plus utilisé comme mot-clé pour communiquer explicitement le fait que l’appel peut rater.

func higherFunction throws {
    try thisCanFail()
    doSomethingAfter()
}

Dans le bout de code suivant, je ne fais que remonter l’erreur plus haut. J’ai marqué ma fonction higherFunction comme ayant la possibilité de lever une erreur, mais comment puis-je gérer mon erreur moi-même ?

func higherFunction() throws -> Bool {
    do {
        try thisCanFail()
    }
    catch {
        print("Oops !")
        return false
    }
    doSomethingAfter()
    return true
}

Et je peux utiliser du Pattern Matching dans la clause catch pour spécifier le type d’erreur que je veux gérer. À propos des types d’erreurs, n’importe quel type (struct, enum, class) qui se conforme au protocole ErrorType peut être envoyé. Une enum avec valeur associée marche très bien pour ça bien entendu, et NSError se conforme déjà à ce protocole.

Et si je dois exécuter du code quel que soit la raison pour laquelle je sors du contexte ? Avertir un délégué, par exemple.

Pour ceci je peux utiliser defer :

func higherFunction() throws -> Bool {
    defer { doSomethingAfterNoMatterWhat() }
    do {
        try thisCanFail()
    }
    catch {
        print("Oops !")
        return false
    }
    try thisCanAlsoFail()
    doSomethingAfter()
    return true
}

Ce qui se trouve dans le defer sera exécuté en sortie de contexte quoi qu’il arrive.

Apple garantit que l’usage de ce nouveau système ne plombera pas les performances, qui sont annoncées similaires à l’utilisation de conditionnels en if. A tester !

Availability Checking

On ne sait pas exactement comment mais Apple parvient à faire avancer son parc installé vers des nouvelles versions de l’OS à une vitesse fulgurante, si bien que de nombreux développeurs utilisent sans scrupule des API disponibles seulement aux dernières moutures.

Et faire le grand écart entre la version n et la version n-2 peut s’avérer très compliqué. En effet, il faut traquer soi-même ces usages d’API récentes, avant de les gérer avec des conditions bricolées et/ou l’utilisation de respondsToSelector. Et comme on en loupe toujours, il y a des plantages à l’exécution dans la nature et on a le cafard.

Bref ce n’est pas l’idéal, du coup de nombreux développeurs finissent par déserter très vite les vieilles versions de l’OS et les utilisateurs sont malheureux.

J’ai une bonne nouvelle : nous allons tous pouvoir souffler. D’une part, parce que le nouveau compilateur va surveiller les versions cibles de notre application et nous avertir automatiquement à l’utilisation d’une API qui ne serait pas gérée par toutes ces versions.

Availability Checking

D’autre part, parce qu’une fois le problème ciblé, on a des moyens de le résoudre beaucoup plus facilement qu’avant.

Pour mettre un appel dans une condition, il existe à présent une fonction #available(), qui prend une liste de versions d’OS et que l’on peut utiliser dans un if ou un guard (j’y reviendrai) pour s’assurer que notre code ne s’exécute que sur l’environnement qui le gère.

if #available (IOS 8, *) {
    …
}

Vous pouvez aussi calmer le compilateur en utilisant @available() pour marquer tout une fonction ou une classe comme n’étant compatible qu’à partir d’une certaine version de l’OS. Ainsi le problème va remonter un cran au-dessus et vous pourrez le gérer de manière plus factorisée, par exemple.

On dit “ouf !”

Pattern Matching

Le Pattern Matching est une de ces fonctionnalités du langage qui manquent quand on ne les a plus. Je l’ai utilisé à quelques reprises mais je suis sûr de sous-exploiter encore son potentiel.

Il était d’abord principalement utilisé dans le switchcase, dont il décuplait les possibilités en permettant de faire des choses comme ce système d’aiguillage en fonction d’un indexPath pour une UITableView

switch (indexPath.section, indexPath.row) {
    case (0, let row):
    …
    case (1, let row):
    …
    default:
}

Il peut maintenant être utilisé directement dans une clause if avec la nouvelle clause if case, comme ceci par exemple :

if case let (x, y) = getPoint() where x > 0 {
    …
}

Ce qui permet d’avoir accès à la puissance du Pattern Matching sans avoir à faire un switchcase alambiqué. Pas mal aussi, l’utilisation dans un while, ou encore mieux un forin :

for number in numbers where number % 2 == 0 {
    …
}

Guard

Vous pouvez aussi l’utiliser dans une nouvelle clause appelée guard, qui permet de traiter très facilement les sorties précoces.

guard let name = json["name"] as? String else {
    return .None("missing name")
}

Le code à l’intérieur de la clause else doit absolument sortir du scope, par un return, throw, break,… L’intérêt de guard est de garantir au compilateur qu’il peut partir du principe que ce test est passé pour la suite de la méthode ou du scope. Dans cet exemple, vous pouvez utiliser la variable name sans avertissement. Et puis ça fait une belle convention pour traiter les cas d’exception, non ?

Si le Pattern Matching vous intéresse, je vous invite à lire la page de la doc sur le sujet.

Protocol Extensions

Vous connaissez déjà la possibilité de Swift d’ajouter des méthodes à des types à l’aide d’extensions (on les appelait catégories, en Objective-C).

Mais Swift utilise énormément les protocoles, notamment SequenceTypeCollectionType sur lequel s’appuient les fonctions génériques comme filter, find ou map. Ces fonctions sont donc de haut ordre et difficiles à chaîner, comme en Java 8 avec les Streams par exemple. Apple avait dû créer des méthodes pour certaines classes, arbitrairement (Array mais pas Set par exemple).

Il est à présent possible de rajouter des méthodes à des protocoles, pour les rendre accessibles à tous les types qui respectent ce protocole. Oui, vos protocoles peuvent maintenant intégrer du comportement !

Les possibilités de conception offertes par ce nouveau principe sont assez vertigineuses. Il y avait même une session dédiée aux Protocols à la WWDC, intitulée Protocol-Oriented Programming (45:51).

Et les fonctions génériques de SequenceType et CollectionType sont maintenant disponibles dans des extensions de protocoles dans Swift 2 pour faciliter le chaînage.

Xcode

Il y a encore plein de petites pépites comme les OptionSets et l’unification de la façon d’appeler les méthodes et les fonctions. Mais pour finir, je vous propose de voir quelques nouveautés de Xcode, l’IDE qui a fait du chemin depuis la version 5 !

Bien entendu l’assistant de migration a été amélioré, il faut bien ça pour un langage qui bouge aussi vite. Mais le compilateur va se permettre de vous aider à écrire du meilleur code avec des avertissements tels que :

  • avertissement si vous assignez une valeur à une var et qu’elle n’est plus jamais changée par la suite, autant utiliser let
  • avertissement quand vous utilisez une fonction de haut ordre et que vous ne récupérez pas son résultat

Et aussi des messages d’erreur plus clairs, avec des propositions de correction plus pertinentes.

Enfin, les Playgrounds, qui permettent de jouer avec du code en live, sont encore plus puissants. On avait déjà rajouté les commentaires Markdown et les ressources associées, maintenant un playground peut avoir plusieurs pages avec des ressources partagées. J’y vois un sacré potentiel pour rédiger des tutoriels ou des cours interactifs.

Rich Playgrounds

Pour que tout ça soit disponible au tout-venant, il faudra attendre que Xcode 7 sorte en version finale, probablement autour de l’automne, comme tous les ans. En attendant, les détenteurs d’un compte Apple Developer payant peuvent télécharger la version beta et faire joujou avec toutes ces nouveautés !

Dans tous les cas, c’est sûrement le meilleur moment pour se mettre à apprendre Swift, qui semble avoir un bel avenir devant lui !

Pour en savoir plus :