Serveur custom de distribution d'App iOS
Etant donné que :
- TestFlight s'est fait racheter par Apple
- mes amis de Cupertino étant particulièrement pénibles avec le déploiement de versions beta d'application
- HockeyApp étant payant (ce que je comprends parfaitement)
En fait, je n'ai pas fait grand chose : le mécanisme étant décrit grossièrement depuis 2010 et s'étant raffiné depuis, les informations sont disponible et je n'ai fait que les rassembler pour me guider dans mon développement.
Avant de coder quoi que ce soit, j'ai analysé HockeyKit pour en conclure que si la doc a l'air bien faite, le logiciel n'est pas à la hauteur car apparemment rien ne fonctionne correctement.
Ainsi, dans la droite lignée des autres posts de ce site, j'ai tout refait. L'intérêt de la chose est que cela m'a permis de comprendre comment fonctionne le packaging d'une application iOS et son déploiement. Ce post est là pour partager ces informations de la manière la plus claire et concise qui soit.
Comment tout à commencé
L'histoire commence apparemment en 2010 quand Hunter Hillegas publie un petit logiciel tout simple tournant sous OSX et permettant de transformer un fichier IPA issu d'Xcode en une série de fichiers qu'il suffit de déposer sur un serveur pour pouvoir ensuite le télécharger et l'installer sur un appareil iOS qui y est autorisé.
Le problème c'est que ce logiciel est fait en Objective-C et peut se ramener à quelques lignes de Bash très simples.
Il faut tout d'abord savoir qu'une application iOS, présentée sous la forme d'un fichier IPA, est en fait un fichier ZIP et qu'un fichier dont le chemin est toujours Payload/<appname>.app/Info.plist contient moults informations sur la dite application. Il reste à analyser ce fichier .plist et à extraire certaines de ces informations. C'est justement ce que se charge de réaliser le logiciel précédent.
De notre côté, nous devrons donc partir à la recherche d'un analyseur de fichier plist utilisables depuis Bash sous Linux, et nous tomberons immanquablement sur LibPList qui se compile facilement et fonctionne très bien.
Cela permettra d'écrire la ligne ci-dessous pour extraire le nom du fichier contenant l'icône de l'application :
plistutil -i tmp/Payload/appname.app/Info.plist | xml2 | grep -A 1 CFBundleIconFile | tail -n1 | cut -d '=' -f 2
Ce qui est rigolo c'est que TestFlight et Hockey ont apparemment créé leurs services à partir du logiciel tout simple de Hunter Hillegas (les commentaires sur la page semblent l'indiquer) et ce logiciel très simple a lancé la grande course à l'analyse de ces fichiers. Bien d'autres personnes se sont intéressées au sujet...
Schéma de fonctionnement
En fait, ce logiciel, iOS Beta Builder, contient toutes les informations importantes pour réaliser une plate-forme de déploiement, ça n'est donc pas un hasard si sa publication a généré beaucoup de bruit. Voici comment fonctionne ce déploiement de manière schématique :
- Envoi du fichier IPA produit par Xcode sur un serveur. Ce fichier est en fait une archive ZIP.
- Extraction du fichier Payload/<appname>.app/Info.plist de l'archive
- Analyse de ce fichier plist pour extraire le nom de l'application, le nom du fichier de l'icône, la version de l'application etc.
- Analyse d'un autre fichier, embedded.mobileprovision, pour extraire quelques autres informations comme les entitlements ou la liste des UUIDs des appareils iOS autorisés à installer l'application.
- Construction d'un fichier manifest.plist à partir des informations extraites pour permettre le téléchargement. Sans ce fichier rien ne fonctionnera.
- Construction d'un fichier index.html à partir des informations extraites. Ce fichier sert essentiellement à fournir une interface agréable aux utilisateurs/beta-testeurs.
manifest.plist ?
Si les opérations 1 à 4 ne sont pas spécialement compliquées, il faut juste être rigoureux, l'étape 5 mérite quelques explications. L'installation d'une application sur un appareil iOS se fait par téléchargement direct, mais comment un iPhone fait-il ? Il ne télécharge pas le fichier IPA directement, il utilise un fichier intermédiaire contenant des méta-données de l'application, le fameux manifest.plist. �a n'est pas fini : ce fichier est utilisé par l'appareil via un protocol qui n'est pas du HTTP mais du itms-services, ce qui permet à une page web de communiquer avec le bas-niveau d'iOS. Ainsi un lien de téléchargement typique sera :
<a href='itms-services://?action=download-manifest&url=%MANIFEST_PLIST_URL%'>Téléchargement</a>
Ainsi, quand l'utilisateur clique sur ce lien, l'appareil va provoquer une requête GET sur le fichier manifest.plist passé en paramètre, va l'analyser sur l'appareil afin de vérifier que tout est valide, va afficher une fenêtre d'alerte pour que l'utilisateur confirme vouloir installer l'application, une requête HEAD sur le fichier IPA dont il a extrait l'URL (la taille du fichier lui permettant d'afficher la progression du téléchargement), une requête GET pour l'icône et enfin une dernière requête GET pour le fichier IPA.
Il est important de noter que le premier téléchargement, celui du fichier manifest.plist, DOIT se passer en HTTPS. Il n'y a pas le choix, et ce, depuis iOS 7.1. Le serveur hébergeant la plate-forme de déploiement devra donc impérativement tourner en HTTPS avec tout la joie SSL que cela suppose (J'ai trouvé des Israéliens qui fournissent des certificats gratuits, c'est déjà ça d'économisé).
Note : il y a une astuce consistant à utiliser DropBox pour héberger le manifest.plist avec tout le reste des fichiers sur un serveur personnel, mais même si j'ai testé que tout fonctionnait bien, le déploiement s'en trouverait particulièrement compliqué, j'ai donc préféré être autonome.
Note 2 : seul le fichier manifest.plist doit être en HTTPS, les autres peuvent être téléchargés en HTTP classique.
embedded.mobileprovision ?
Le point 4 de mon schéma précédent mentionnait un fichier embedded.mobileprovision, mais de quoi s'agit-il ?En fait c'est un fichier plist classique, comme Info.plist précédemment, qui contient des informations intéressantes comme les entitlements ou la liste des UDIDs des appareils autorisés à utiliser l'application. Le seul point étrange est que ce fichier contient des données binaires avant (62 octets dirait-on) et après ce contenu XML classique d'un fichier plist. Il importe donc de le nettoyer avant de l'analyser sinon les parseurs XML vont tousser et puisque je n'aime pas pondre des tonnes de lignes de code, voici le résultat :
cat tmp/Payload/<appname>.app/embedded.mobileprovision | php -r '$C=file_get_contents("php://stdin"); $C=substr($C, strpos($C, "<?xml")); $C=substr($C, 0, strpos($C, "</plist>)+8); echo "$C\n";'Conclusion
J'ai rassemblé toutes ces infos sur cette page car elles m'ont permises de concevoir ma plate-forme dont j'ai partagé le code sur GitHub. A priori, je ne dois pas être le seul à avoir besoin de ce genre de services.
