Categories
Non classé

Design de l’application

Comme je ne dispose que de deux jours, je vais commencer par faire l’application la plus simple possible: elle va reprendre exactement les fonctions de la version réelle du jouet: déplacer la mine et effacer l’écran en le secouant. Pour l’instant, je ne prévois pas d’enregistrement des images, de galerie, etc. Pour la surface de dessin, je vais reprendre le rapport largeur/hauteur de 3/2.

Les molettes rotatives du jouet original ne sont pas pratiques sur un écran tactile; aussi vais-je utiliser des molettes linéaires. Cette entorse à la nostalgie a l’avantage de retirer deux défauts:

  • le jouet doit préciser pour chaque molette si elle transmet un mouvement horizontal ou vertical à la mine.
  • par ailleurs, il existe un doute sur la direction: une rotation de la molette horizontale dans le sens horaire déplace-t-elle la mine vers la droite ou vers la gauche ?

Enfin, je vais opter pour un boitier bleu afin de bien me différencier du modèle original. La forme sera aussi différente.

Categories
Non classé

Déploiement aérien (Over the Air) d’une application iOS

Que vous veuillez envoyer votre application à des testeurs (distribution AdHoc) ou l’installer sur les terminaux de l’entreprise (distribution Enterprise), la manière classique de procéder est de transmettre l’application sous forme d’un fichier .ipa, de glisser celui-ci dans iTunes, puis de brancher et synchroniser le terminal iOS.

Cependant, il existe aussi un déploiement aérien (“Over the Air”) qui consiste à fournir (par courriel ou SMS) une URL vers une page web. En se rendant sur cette page avec le terminal iOS, on touche un simple lien et iOS propose d’installer l’application, puis la télécharge. Cette manière de procéder est peu connue; le fait d’Apple qui l’a documentée dans son guide ”Distributing Enterprise Apps for iOS 4 Devices”, alors que cette procédure est tout à fait permise en mode Ad Hoc.

En pratique

Vous allez créez un répertoire sur votre site web, qui contiendra:

  • le fichier .ipa
  • un fichier index.html qui sera la page web contenant le lien pour télécharger
  • un fichier manifeste, qui est une .plist comportant les renseignements sur votre application.

Le fichier html

Voici un exemple simple, mais bien suffisant:

!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<body>
<a href="itms-services://?action=download-manifest&url=http://www.monsite.fr/monappli/
MonAppli.plist">Installer l'application</a>
</body>

Cette page comportera uniquement un lien vers les services d’itunes auxquels on fournit l’adresse du manifeste, MonAppli.plist dans cet exemple. Il est évident que le passage par les services d’iTunes garantit que vous n’abuserez pas du système !

Application et manifeste

La doc d’apple donne un manifeste d’exemple fort compliqué. En fait, Xcode 4 va le générer pour nous. Pour cela:

  • commencez par Archiver l’application.
  • une fois l’archive dans l’Organiseur, cliquez le bouton ”Share…”
  • choisissez ”iOS App Store Package (.ipa)” et le certificat qui convient pour un déploiement Ad Hoc ou Enterprise.
  • conserver le nom du fichier .ipa, et cochez la case ”Save for Enterprise Distribution”
  • des champs apparaissent, renseignez au moins l’URL de l’appli (http://monsite.fr/monappli/MonAppli.ipa) et le titre. Xcode a généré app.ipa et app.plist, vous n’avez plus qu’à les copier dans le répertoire de votre site web.

Si ça ne fonctionne pas

D’expérience, il est assez rare que cela fonctionne au premier essai !

  • Commencez par vérifier l’URL du manifeste dans le fichier HTML
  • Vérifiez ensuite l’URL de l’appli dans le manifeste. L’adresse doit comporter le nom de l’application, donc se terminer par /MonAppli.ipa
  • Jetez un œil à la Console du terminal dans l’Organizer de Xcode. La raison est parfois affichée.
  • Si cela ne fonctionne toujours pas, c’est probablement un problème de certificat. On observe généralement que le téléchargement se termine mais que l’application disparait aussitôt. Dans ce cas, rebranchez votre terminal iOS et tentez une installation filaire pour résoudre ce problème.

Limitations

Ce mode de distribution n’est possible qu’à partir d’iOS 4. Sachez qu’il existe des sites web qui se proposent de stocker le fichier .ipa pour vous; dans le cas ou le terminal tourne sur une version antérieure d’iOS, ils proposent alors d’envoyer un courriel comportant l’URL de l’application pour pouvoir effectuer une installation filaire. Voilà, essayer le déploiement aérien, c’est l’adopter !

Categories
Non classé

PortraiMatic iPad (2): Reprendre le code de la version Mac ?

C’est décidé, je vais convertir PortraiMatic du Mac à l’iPad. Mais par où commencer ?

Faut-il reprendre le code de la version Mac ?

Il s’agit d’une question économique, à savoir: sachant que les bibliothèques de développement Mac et iOS possèdent de nombreuses similitudes, peut-on partager les fichiers sources entre les deux plateformes ? Après tout, le langage de programmation est le même, et Cocoa Touch est une adaptation de Cocoa Mac. Et si la réponse est positive, est-ce que la lourdeur structurelle (mise en place sous Xcode) n’anéantis pas le bénéfice de cette réutilisation?

Le gros du code concerne l’IHM

L’Interface Homme-Machine représente une grosse proportion dans PortraiMatic. Or, il s’agit probablement d’une des plus grosses différences entre Mac OS et iOS. D’un côté, nous utilisons App Kit, de l’autre UIKit. Il ne s’agit pas d’une simple adaptation: les ingés d’Apple l’ont repensé pour l’utilisation avec un écran tactile et l’ont grandement modernisé.

PM_ReframeView

Par exemple, je sais d’avance que je vais devoir reprogrammer entièrement la vue qui sert à recadrer les portraits. Or, il s’agit de la classe la plus complexe de PortraiMatic Mac.

Pas de bindings

Les Bindings sont une des technologies les plus utiles, mais également les plus complexes qui existe sur Cocoa Mac. Cette technologie synchronise l’état des vues (ce qui apparaît à l’écran) et l’état interne (couche ‘métier’). PortraiMatic en fait grand usage, entre autres parce qu’elle réduit les dépendances entre classes, et donc la complexité de l’architecture. Pour des raisons de performances, je pense, cette technologie n’a pas (encore) trouvé son chemin dans Cocoa Touch. Il existe des alternatives, mais cela signifie à nouveau que de grand pans du code ne peuvent pas être réutilisés.

Pas de Core Image

Core Image est ma technologie préférée dans Mac OS X. Elle permet d’appliquer des effets sur les images voire de générer des images. C’est une technologie à la fois performante, facile à utiliser et extensible. Malheureusement, elle n’a pas encore été portée sous iOS, même si je ne doute pas que cela sera fait prochainement. En conséquence, j’ai décidé, dans un premier temps, de ne pas inclure de fonctions de corrections de couleurs dans la version iPad. Non, pas que ceci me poserait de problème particulier (la programmation graphique est ma spécialité), mais je veux publier l’application rapidement.

Gestion des fichiers

La gestion des fichiers dans PortraiMatic est à la fois un motif de satisfaction — parce qu’elle est maintenant bien au point — et une inquiétude à chaque nouvelle version pour s’assurer de la compatibilité avec les versions précédentes. Elle présente quelques détails malins. Tout d’abord, la “galerie” est un bundle, c’est à dire un dossier qui apparaît comme un fichier sous Finder. On peut l’ouvrir sous Finder, par un clic droit > Afficher le contenu du paquet, et constater que chaque portrait possède son dossier:

PM_BundleGalerie

Chaque dossier comporte l’image utilisée par le portrait et un fichier Portrait.plist qui décrit les paramètres du portrait (recadrage, correction des couleurs). Cette organisation s’est révélée pratique en cas de problème (on peut mettre un dossier à la corbeille), plus simple à déboguer (on voit immédiatement si les données ont été enregistrées). Elle a aussi permis de mettre en œuvre facilement la fusion des galeries de deux utilisateurs. Cependant, je vais passer à Core Data pour enregistrer les données.

Core Data permet de décrire le modèle (la partie “métier”) de l’application de façon graphique et de stocker les informations dans une base de données SQLite de façon transparente. J’en attends des simplifications de l’architecture et une baisse de volume du code source. Par ailleurs, elle devrait régler mes soucis de migration d’une version à l’autre, en m’obligeant à décrire précisément les évolutions de chaque version.

À l’heure du choix

Finalement, je constate que peu de code peut être repris; si nécessaire, je dispose toujours de la possibilité de copier-coller du code. Je n’ai franchement pas envie de m’engager dans des problèmes d’inclusions de fichier d’un projet Xcode à l’autre. Il me semble donc plus opportun de ne pas partager de fichiers entre les versions Mac et iPad, et de créer un projet Xcode tout neuf.

Categories
Non classé

Traduction d’une appli Cocoa

Lorsque l’on propose son application dans plusieurs langues, comme c’est mon cas pour PortraiMatic, la traduction (terme que je préfère au vilain anglicisme “localisation”) est plus complexe que ce qu’Apple laisse entendre. En gros, trois aspects doivent être gérés:

  • Ne pas utiliser de chaînes de caractères ”en dur”.
  • Il faut les écrire dans un fichier .strings et les charger avec une fonction ou méthode adéquate (NSLocalizedString() ou -NSBundle localizedStringForKey:value:table).
  • Adapter la saisie et la présentation des nombres, monnaies et dates (NSNumberFormatters et compagnie)
  • Traduire les fenêtres, vues et boites de dialogue, habituellement contenus dans des .xib.

Comme nous allons le voir, ce dernier aspect est délicat.

Solution 1: Traduire les XIB à la mano

Apple propose un concept simple: le bundle de l’application contient un dossier par langue (french.lproj, english.lproj, deutsch.lproj, etc.) qui contient les éléments d’interface utilisateur spécifiques à cette langue. Pour traduire un XIB anglais en français, il suffit donc de copier le .xib de english.lproj dans french.lproj et de le traduire. Cette manière fonctionne bien… la première fois.

Imaginons que vous vouliez ajouter une case à cocher dans votre logiciel:

  • vous ajoutez la case dans le xib anglais
  • vous créez ses connexions (outlets, actions et bindings)
  • et vous refaites le même boulot pour le xib français !

En dehors des risques d’erreur sur les connexions, qui ne peuvent être détectées qu’à l’exécution, c’est un travail rébarbatif, à tel point qu’il vaut mieux repartir des xib anglais et tout retraduire. C’est ce que je faisais jusqu’à récemment.

Un autre problème est que si vous voulez faire traduire le logiciel, vous ne pouvez pas demander au traducteur d’utiliser Interface Builder pour modifier le xib lui-même. Non seulement, c’est compliqué, mais il risque de casser quelque chose. Vous devez lui fournir un simple fichier texte, ce qui nous amène à la…

Solution 2: Extraire les chaînes

C’est la solution que j’utilise actuellement pour PortraiMatic. Je me suis inspiré de la technique exposée par Philippe Casgrain:

1) un outil en ligne de commande, ibtool, extrait les chaînes de caractères du xib anglais et les écrit dans un fichier .strings.

ibtool --export-strings-file English/MainMenu.strings ../English.lproj/MainMenu.xib

Voici un extrait du fichier .strings produit:

/* Class = "NSMenuItem"; title = "About PortraiMatic"; ObjectID = "58"; */ "58.title" = "About PortraiMatic"; /* Class = "NSMenu"; title = "File"; ObjectID = "81"; */ "81.title" = "File";

2) le traducteur traduit le fichier .strings

/* Class = "NSMenuItem"; title = "About PortraiMatic"; ObjectID = "58"; */ "58.title" = "À propos de PortraiMatic"; /* Class = "NSMenu"; title = "File"; ObjectID = "81"; */ "81.title" = "Fichier";

3) ibtool recompose le xib français en remplaçant les chaînes traduites:

ibtool --import-strings-file French/MainMenu.strings --write ../French.lproj/MainMenu.xib ../English.lproj/MainMenu.xib

Philippe Casgrain propose d’extraire les chaînes à chaque build (en ajoutant une ”Script Build Phase” à la ”Target” sous XCode). C’est une bonne idée car votre logiciel de gestion de version va vous alerter d’une modification des fichiers .strings, qu’il faudra renvoyer au traducteur.

Par contre, il suggère aussi de recomposer les XIB traduits à chaque build: je ne le fais pas, car le build s’en trouve allongé, alors que ça n’agit que quand le fichier .strings vient d’être traduit.

Cette technique a l’avantage d’être simple à mettre en œuvre, mais pose encore deux problèmes:

  • les objets de l’IHM ne sont pas redimensionnés; or on sait que l’Anglais est souvent plus concis que le Français ou l’Allemand. Il faut donc prévoir de la marge dans les dimensions lors de l’élaboration des XIB anglais. Ce n’est pas toujours esthétique: il reste deux ou trois boutons que je redimensionne à chaque livraison.
  • les XIB sont volumineux, et l’application prend de l’embonpoint à chaque langue ajoutée. C’est particulièrement pénalisant pour une appli iPhone.

Solution 3: Traduire au chargement du NIB

Cette solution a été exposée par Axel Péju, l’auteur de Squirrel (et accessoirement gagnant d’un Apple Design Awards pour la version Mac), lors de sa présentation au dernier Cocoa Heads parisien. L’idée est simple: il suffit de mettre toutes les chaînes de caractères qui apparaissent à l’écran dans des fichiers .strings. Quand on charge un NIB, on remplace alors le texte de chaque objet par le code.Pour redimensionner les objets, on appelle leur méthode -sizeToFit pour que leur taille s’adapte au texte affiché.

Cette technique résout tous les problèmes exposés précedemment: il n’y a plus qu’un seul XIB, et les chaînes peuvent être fournies directement aux traducteurs. Elle possède aussi un avantage: le traducteur peut essayer lui-même l’application traduite: il n’a qu’à glisser les fichiers .strings traduits dans le dossier .lproj qui correspond à sa langue.

Ceci dit, le travail est important, puisqu’un objet contrôleur doit posséder des outlets vers tous les objets qui affichent du texte, même si ce sont de simple libellés, et faire les appels correspondant.

Solution 4: Traduire (automatiquement) au chargement du NIB

Voici une solution à laquelle j’avais pensé; Guillaume Cerquant m’a dit qu’il l’utilisait. L’idée est que chaque élément d’IHM du XIB à traduire a son titre qui commence par un astérisque (par exemple). Lors du chargement du XIB, on parcourt la hiérarchie des vues en entier: si le titre de la vue commence par “*”, on va chercher le titre dans un fichier .strings et on remplace le titre. Il me semble que cette technique est un bon compromis: à la fois assez légère à mettre en œuvre, tout en conservant les qualités de la solution 3.