Un outil pour le versionnage

L'utilitaire agvtool est un petit outil en ligne de commande fourni avec Xcode. Il permet de gérer facilement les numéros de versions embarqués dans votre application. Voyons comment l'utiliser pour automatiser certaines tâches.

Une application iPhone contient deux numéros de version. Le premier est le numéro dit marketing. Il s'agit d'une chaine de caractères embarquée dans la clef CFBundleShortVersionString du fichier Info.plist qui peut être choisie librement par le développeur. Traditionnellement, il s'agit d'une chaîne de la forme X.Y où X représente le numéro de version majeur et Y le numéro de version mineur, mais vous pouvez choisir tout autre type de numérotation si vous le souhaitez. Apple n'impose aucun format. En revanche, ce numéro doit identifier de façon unique chaque version publiée de votre application : très logiquement, vous ne pouvez pas sortir deux versions 2.3 du même produit.

Le second numéro est le build number. Il s'agit d'un nombre entier stocké à la fois dans la clef CFBundleVersion du fichier info.plist – c'est là que les outils Apple iront le lire – et dans le fichier projet – c'est là que le compilateur ira le lire pour générer automatiquement une constante permettant d'y accéder depuis votre code. Ce nombre est utilisé par l'iPhone pour déterminer si la version d'une application sur l'AppStore est plus récente ou plus ancienne que la version actuellement installée. Pour que ce mécanisme de détection de mise à jour fonctionne, ce numéro doit être strictement croissant d'une mise à jour sur l'autre.

Préparer votre projet

Votre projet doit d'abord être configuré convenablement pour utiliser agvtool. Ouvrez la page Build Settings de votre projet dans Xcode et localisez la section Versioning. À la ligne Versioning System, sélectionnez l'option Apple Generic. À la ligne Current Project Version, saisissez une valeur numérique quelconque, par exemple 6. La valeur n'a pas d'importance, elle sera écrasée ensuite.

C'est tout !

Fixer le numéro marketing

Première opération, fixer le numéro marketing. C'est une opération normalement assez rare. On ne publie pas de mise à jour sur l'AppStore tous les matins ! Pour ce faire, ouvrez une fenêtre de terminal, placez-vous dans le répertoire contenant votre fichier projet, puis tapez la commande suivante :

agvtool -noscm new-marketing-version "X.Y"

Cette commande va analyser votre projet afin de trouver tous les fichiers *-Info.plist qu'il contient, puis va écrire le numéro marketing voulu dans chacun de ces fichiers. L'avantage d'utiliser cette commande par rapport à une modification manuelle est que vous êtes certain que le même numéro sera écrit partout.

Bien sûr, il vous faudra remplacer la chaîne "X.Y" dans l'exemple ci-dessus par le numéro de version voulu. L'option -noscm indique que votre projet n'est pas sous le contrôle d'un contrôleur de source tel que Git ou SVN. Si c'est le cas, il vous faudra plutôt utiliser l'option -usecvs ou -usesvn. Il est également possible de configurer votre environnement pour que agvtool utilise par défaut l'option qui vous convient le mieux. Reportez-vous à la page man de l'outil pour plus d'information.

Fixer le numéro de build

Opération suivante, fixer le build number initial. Ceci ne doit être fait en principe qu'une seule fois, au moment de la création du projet (ou au moment de sa configuration pour agvtool). La commande à utiliser est la suivante :

agvtool -noscm new-version -all 1

Cette commande va fixer le numéro de build à 1 dans tous les fichiers nécessaires, à savoir le fichier projet lui-même et tous les fichiers *-Info.plist qu'il contient. Là encore, cette façon de faire garantit la cohérence de l'opération par rapport à une édition manuelle des fichiers.

Incrémenter le numéro de build

Il s'agit de l'opération la plus fréquente. Idéalement, le numéro de build doit être incrémenté à chaque fois que vous compilez une version qui va être distribuée vers l'extérieur : les bêta-testeurs, vos clients dans le cas d'une distribution Ad Hoc, le grand public dans le cas d'un chargement sur l'AppStore, etc. Si le build number n'est pas incrémenté, vos utilisateurs risquent de rencontrer des problèmes d'installation, le système considérant que cette nouvelle version n'est pas plus récente que la version déjà installée.

La commande à utiliser pour incrémenter le build number est la suivante :

agvtool -noscm bump -all

Cette commande va lire le numéro de build présent dans le fichier projet, l'incrémenter, puis écrire cette nouvelle valeur partout où c'est nécessaire. Et voilà !

Certains développeurs ajoutent cette commande dans un script exécuté par Xcode avant chaque compilation. L'avantage est qu'ils sont ainsi certains que le build number est correctement incrémenté. L'inconvénient est qu'il est en pratique incrémenté beaucoup trop souvent : la plupart des compilations ne conduisent pas à une version qui sera distribuée. Après quelques mois de développement, on se retrouve avec un build number tournant autour de plusieurs dizaines de milliers… Un autre inconvénient est que agvtool modifie le fichier projet, or Xcode devient parfois un peu instable lorsque le projet ouvert est modifié par un autre process.

Personnellement, je préfère exécuter cette commande à la main avant chaque compilation destinée à la distribution d'une nouvelle bêta ou d'une nouvelle version finale. À vous de choisir ce qui vous convient le mieux.

Accéder aux numéros de version depuis le code

Il peut être nécessaire de lire le numéro marketing ou le numéro de build depuis le code de l'application, par exemple pour les afficher sur un écran d'information. Une application peut également utiliser le numéro de build pour détecter qu'elle a été mise à jour (voir plus bas).

La méthode habituelle pour lire le numéro marketing consiste à aller le récupérer dans le fichier Info.plist grâce au code suivant :

NSString * version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];

Lire le numéro de build est encore plus immédiat. Lorsque votre projet est configuré pour utiliser agvtool, Xcode déclare automatiquement une variable const double initialisée avec la valeur du build number. Il suffit donc de déclarer ce symbole externe pour y pouvoir accéder.

Le nom de cette variable est par défaut de la forme <NomDuProjet>VersionNumber. Par exemple, si votre application s'appelle MyApp, la déclaration pour y accéder sera :

extern const double MyAppVersionNumber;

Vous pouvez ensuite utiliser cette variable dans votre code partout où vous avez besoin de tester ou d'afficher le numéro de build. L'exemple suivant montre comment utiliser le build number et la classe NSUserDefaults pour détecter au démarrage de l'application que l'utilisateur a effectué une mise à jour.

extern const double MyAppVersionNumber;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  double previousBuildNumber;

  previousBuildNumber = [[NSUserDefaults standardUserDefaults] doubleForKey:@"BuildNumber"];
  if (MyAppVersionNumber > previousBuildNumber)
  {
    [[NSUserDefaults standardUserDefaults] setDouble:MyAppVersionNumber forKey:@"BuildNumber"];

    // Le build number a changé : entreprendre les
    // actions nécessaires.

    ...
  }

  // Suite du code de lancement de l'application.

  ...
}

Le principe consiste à maintenir une copie du numéro de build dans une entrée du fichier de préférences. Au lancement, on relit la dernière version de cette information et on la compare au numéro de build actuel. Si la valeur a changé, c'est que l'utilisateur a effectué une mise à jour de l'application. On peut alors entreprendre les actions utiles. Par exemple, il peut s'agir de mettre à jour le schéma de base de données, ou encore de réinitialiser les bulles d'aides de l'application.

Notez qu'au tout premier lancement de l'application, il n'y a encore aucune entrée dans le fichier de préférences. La variable previousBuildNumber recevra la valeur zéro, la condition à la ligne suivante sera toujours vraie, ce qui fait que le code dans le bloc if sera exécuté. Si cela est gênant, il suffit de prévoir explicitement ce cas.