I. Qu'est-ce que « Fastlane »▲
Fastlane est un outil open source qui permet de faire du Continuous Delivery sous IOS et Android. Il permet d'automatiser un certain nombre de tâches fastidieuses comme gérer les screenshots, les certificats, déployer votre application.
Vous l'avez compris, cet outil va changer votre vie.
Voici la liste des bibliothèques à disposition avec Fastlane :
- Deliver : télécharge des captures d'écran, des métadonnées et votre application sur l'App Store en utilisant une seule commande ;
- Snapshot : automatise la prise des captures d'écran localisées de votre application iOS sur tous les périphériques ;
- Frameit : place rapidement vos captures d'écran dans les cadres appropriés (iphone, ipad…) ;
- Pem : génère et renouvelle automatiquement vos profils de notification push ;
- Sigh : crée, renouvelle, télécharge et répare des profils de provisionnement ;
- Produce : crée de nouvelles applications iOS sur le portail développeur Apple et iTunes Connect avec les informations minimales requises ;
- Cert : crée automatiquement et conserve des certificats de signature ;
- Scan : facilite l'exécution des tests de votre application iOS et Mac sur un simulateur ou un périphérique connecté ;
- Gym : construit et crée un package de votre application ;
- Match : synchronise facilement vos certificats et profils de provisionnement à travers votre équipe en utilisant Git.
Se rajoute également à cette liste :
- Pilot: gère vos testeurs TestFlight à partir de votre terminal ;
- Boarding: permet la création d'une page d'inscription simple pour les testeurs bêta TestFlight :
Personnellement, quand j'ai vu ce que Fastlane était capable de faire, j'ai limite versé une petite larmichette ! Fini le temps de tout faire à la main !
Note : J'ai créé un projet « bidon » avec différents écrans et tests pour cet article.
II. Premiers pas▲
II-A. Installation▲
Prérequis : ruby >= 2.0.
Je vous recommande de créer un Gemfile pour définir les éventuelles dépendances de Fastlane :
source "https://rubygems.org"
gem "fastlane"
$
bundle install
II-B. Initialisation▲
Lancer la commande suivante :
$
bundle exec fastlane init
On vous demandera votre Apple ID, mot de passe et dans mon cas un digit code via mon Iphone. Fastlane vous récapitulera vos informations dans Summary for produce et créera votre application dans Itunes Connect et Dev Center. Il générera également une configuration pour vous, en fonction des informations fournies :
Appfile: l'Appfile stocke des informations utiles qui sont utilisées dans toutes les bibliothèques Fastlane comme votre Apple ID ou le Bundle Identifier, pour déployer vos lanes (voies) plus rapidement, adaptées aux besoins de votre projet.
Par défaut, ce fichier ressemble à :
app_identifier "com.eleven.fastlane"
# Bundle identifier de votre app
apple_id "obiwan@kenobi.com"
# Votre Apple adresse email
Si vous avez des identifiants différents pour Itunes Connect et Apple Developer, vous pouvez utiliser le code suivant :
2.
3.
4.
5.
6.
7.
app_identifier "tools.fastlane.app"
# Bundle identifier de votre app
apple_dev_portal_id "portal@company.com"
# Votre Apple adresse email
itunes_connect_id "tunes@company.com"
# Votre Itunes Connect adresse email
team_id "Q2CBPJ58CA"
# Le team ID du Developer Portal
itc_team_id "18742801"
# Le team ID du iTunes Connect
Si vous souhaitez accéder à ces valeurs :
identifier = CredentialsManager::AppfileConfig.try_fetch_value(:app_identifier)
team_id = CredentialsManager::AppfileConfig.try_fetch_value(:team_id)
Fastfile: fichier ruby qui définit toutes vos lanes. Une lane est un ensemble d'instructions que vous souhaitez faire exécuter par Fastlane :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
# Fastfile - J'ai nettoyé le fichier et j'ai créé une lane test
fastlane_version "2.39.2"
default_platform :ios
platform :ios
do
desc "Runs all the tests"
lane :test
do
scan # lib Fastlane qui lance vos tests.
end
end
Si vous lancez un :
$
bundle exec fastlane test
il lance alors tous les unit/ui tests de votre projet. Bien évidemment, Scan peut générer des rapports au format HTML, JSON et JUnit :
Magique non ?
Nous allons partir du principe que je travaille en collaboration avec plein de devs mobiles sur ma super application. Le problème se pose au niveau des certificats et profils de provisionnement. Heureusement Fastlane nous met à disposition Match.
III. Match▲
Match implémente le concept de codesigning guide. Il permet de créer tous vos certificats et profils de provisionnement dans un compte Git distinct. Chaque membre de l'équipe ayant accès au repo peut utiliser ces credentials pour la signature de code.
Match répare également automatiquement les credentials brisés et expirés. Ceci est bien évidemment sécurisé.
Note : l'implémentation de match vous oblige à révoquer vos certificats existants.
Si vous ne voulez pas révoquer vos certificats existants, mais souhaitez toujours une configuration automatisée, cert et sigh sont là pour vous.
- Cert : veillera à ce que vous ayez un certificat valide et sa clé privée installée sur votre machine.
- Sigh : veillera à ce que vous ayez un provisioning profile valide installé sur votre machine, qui correspond au certificat installé.
III-A. Configuration▲
$
bundle exec fastlane match init
Cela vous demandera d'entrer l'URL de votre repo Git. Vous pouvez aussi utiliser un lien git@ si votre machine peut se connecter en SSH sur ce repo.
Note : cette commande ne lit ou ne modifie pas les certificats ou profils.
Cela va créer un fichier Matchfile qui ressemble à :
2.
3.
4.
5.
6.
git_url "https://gitlab.com/prims47/fastlane_article.git"
type "development"
# Le type par défaut, il peut être appstore, adhoc, enterprise ou development
# app_identifier ["tools.fastlane.app", "tools.fastlane.app2"]
# username "user@fastlane.tools" # Votre Apple Developer Portal username
Match prend également en charge le stockage des certificats de plusieurs équipes dans un repo, en utilisant des branches. Si vous travaillez dans plusieurs équipes, assurez-vous de définir le paramètre git_branch à une valeur unique par équipe. De là, la correspondance créera et utilisera automatiquement la branche spécifiée pour vous :
2.
3.
#Fastfile
match
(
git_branch: "team1"
, username: "user@team1.com"
)
match
(
git_branch: "team2"
, username: "user@team2.com"
)
Avant d'exécuter match pour la première fois, vous devriez envisager de supprimer vos profils et certificats existants à l'aide de la commande match nuke.
Exécuter match :
$
bundle exec fastlane match appstore
Cela créera un nouveau certificat et un profil de provisionnement (si nécessaire) et les stockera dans votre repo Git. Si vous avez précédemment exécuté match, il installera automatiquement les profils existants à partir du repo Git.
Les provisioning profiles sont installés dans ~ / Library / MobileDevice / Provisioning Profiles alors que les certificats et les clés privées sont installés dans votre Keychain.
Comment faire si vous devez ajouter un nouveau device ?
Non, on ne va pas se connecter et le faire à la main ! On va utiliser l'action register_devices en combinaison avec match :
2.
3.
4.
lane :recette
do
register_devices(
devices_file:
"./devices.txt"
)
match(
type:
"adhoc"
, force_for_new_devices:
true
)
end
2.
3.
4.
#devices.txt
Device ID Device Name
A123456789012345678901234567890123456789 NAME1
B123456789012345678901234567890123456789 NAME2
En utilisant le paramètre force_for_new_devices, match va vérifier si le nombre de devices a changé depuis la dernière exécution et régénère automatiquement le profil de provisionnement si nécessaire.
Vous pouvez utiliser aussi force: true pour générer le profil de provisionnement à chaque exécution.
III-B. Paramétrer Xcode▲
Avec Xcode 8 vous pouvez définir un profil de provisionnement pour chaque target au lieu d'un provisioning profile UUID. En faisant ça, XCode sélectionne automatiquement le dernier provisioning profile correspondant à son nom. De cette manière, vous n'aurez pas à mettre à jour Xcode à chaque fois que vous générez un profil de provisionnement (ex. : quand vous ajoutez un nouveau device).
Vous pouvez spécifier quel profil de provisionnement utiliser dans General tab après avoir désactivé Automatically manage signing :
On vient de voir avec quelle facilité on gère les certificats et profils de provisionnement. Maintenant on va s'attaquer au push notification profile.
IV. PEM▲
Si vous avez lu mon précédent article Envoyer des push notifications via Amazon SNS en Swift 3, vous avez vite compris que c'était super « galère » de faire ceci à la main.
Mais ça c'était avant !
Pem est venu me sauver de cette tâche fastidieuse !
pem permet de :
- créer un nouveau signing request ;
- créer un certificat push ;
- télécharger le certificat ;
- générer le nouveau fichier .pem dans le dossier courant.
pem ne permet pas de :
- couvrir la partie upload sur votre serveur/service ;
- révoquer vos certificats existants ;
- télécharger n'importe quel certificat existant, car la clé privée n'est disponible que sur la machine sur laquelle elle a été créée.
Si vous avez déjà activé un certificat push pendant au moins 30 jours, pem ne créera pas de nouveau certificat. Si vous voulez quand même en créer un, vous pouvez utiliser l'option force :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
#Fastfile
desc "Generate Push Certificate"
lane :pushCertificat
do
#pem(new_profile: Proc.new do |value|
# Code pour télécharger votre fichier pem sur votre serveur/service.
#end)
pem(
generate_p12:
true
, save_private_key:
true
, p12_password:
"1234"
,
pem_name:
"fastlane_pem_file"
, output_path:
"/Users/prims47/Desktop/blog/Fastlane/push_certificates"
, development:
true
)
end
$
bundle exec fastlane pushCertificat
Et hop, un jeu d'enfant !
V. Scan▲
Comme vous l'avez vu au début de l'article, scan permet de lancer tous les tests (sur simulateur ou device) de votre projet. Dans le projet que je me suis créé, j'ai bien évidemment ajouté quelques tests afin de vous montrer ce qu'on peut faire.
Première chose à faire c'est d'initialiser le fichier Scanfile afin de configurer les paramètres par défaut :
$
bundle exec fastlane scan init
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
#Scanfile
#workspace "FastlaneArticle.xcodeproj"
scheme "FastlaneArticle"
clean true #Clean le projet à chaque exécution
code_coverage true
#slack_only_on_failure true
#Vous pouvez spécifier sur quel iphone ou ipad vous souhaitez lancer les tests.
#devices [
# "iPhone 6s",
# "iPhone 7"
#]"]
Une bonne pratique est de notifier Slack lorsque les tests sont failed. J'ai donc ajouté dans mon fichier Fastfile la configuration Slack :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
#...
platform :ios
do
before_all do
ENV
[
"SLACK_URL"
]
=
"https://hooks.slack.com/services/XXXXXX"
# URL Webhook créé via Slack.
end
desc "Runs all the tests"
lane :test
do
scan()
end
#...
end
Résultat sur Slack :
VI. Snapshot - Frameit▲
Si vous devez manuellement créer des screenshots pour 20 langues x 6 devices x 5 screenshots (car 5 écrans différents) = 600 screenshots. On voit tout de suite la quantité de travail énorme que demandent les screenshots.
Les screenshots sont une partie importante, car cela influence beaucoup l'utilisateur sur votre application.
Fastlane nous met à disposition deux bibliothèques géniales, snapshot et frameit :
- Snapshot : comme son nom l'indique, cela génère localement les screenshots pour différents devices et langues. On verra par la suite comment les uploader en utilisant deliver ;
- Frameit : permet de mettre votre screenshot créé par snapshot dans un cadre de périphérique tel qu'un iphone, ipad.
VI-A. Snapshot▲
Installation de snapshot :
$
bundle exec fastlane snapshot init
Cette commande va créer deux fichiers :
- SnapshotHelper.swift : helper pour effectuer les screenshots ;
- Snapfile : pour configurer snapshot.
Il faut tout d'abord ajouter le fichier SnapshotHelper.swift à notre target d'UI Tests. Puis dans notre classe d'ui test, dans la méthode setUp, il faut initialiser snapshot :
2.
3.
4.
5.
6.
7.
8.
9.
override
func
setUp
()
{
super
.
setUp
()
// In UI tests it is usually best to stop immediately when a failure occurs.
continueAfterFailure
=
false
let
app
=
XCUIApplication
()
setupSnapshot
(
app
)
app
.
launch
()
}
Dans chaque test il faut faire appel à la méthode snapsot
(
"Nom du screen"
)
:
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
func
testCarList
()
{
let
app
=
XCUIApplication
()
//Take screenshot
snapshot
(
"01CarList"
)
}
func
testCarDetail
()
{
let
app
=
XCUIApplication
()
app
.
collectionViews
.
cells
.
otherElements
.
containing
(.
staticText
,
identifier
:
"Dodge"
).
children
(
matching
:
.
other
).
element
.
forceTap
()
//Take screenshot
snapshot
(
"02CarDetail"
)
}
Attention !
Pour une raison étrange sur certains devices (dans mon cas iphone 5) avec certaines langues le tap() ne marche pas. Il faut créer une méthode qui va forcer celui-ci :
2.
3.
4.
5.
6.
7.
8.
9.
10.
extension
XCUIElement
{
func
forceTap
()
{
if
self
.
isHittable
{
self
.
tap
()
}
let
coordinate
:
XCUICoordinate
=
self
.
coordinate
(
withNormalizedOffset
:
CGVector
(
dx
:
0
.
0
,
dy
:
0
.
0
))
coordinate
.
tap
()
}
}
Configuration de votre Snapfile :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
# Snapfile
# Liste des devices sur lesquels je souhaite avoir des screenshots
devices
(
[
"iPhone 5s"
,
"iPhone 6"
,
"iPhone 6 Plus"
,
"iPhone 7"
,
"iPhone 7 Plus"
])
# En anglais et en français
languages
(
[
"en-US"
,
"fr-FR"
])
scheme "FastlaneArticle"
# Super important sinon vous allez avoir une erreur
project "../FastlaneArticle.xcodeproj"
# Plaçons-les dans ./screenshots
output_directory "./screenshots"
# Et avant chaque exécution, on nettoie
clear_previous_screenshots true
Lancer la commande :
$
bundle exec fastlane snapshot
Bon c'est le moment d'aller prendre un café ou de manger une pomme ! (Oui les pommes c'est la vie !)
Quinze minutes plus tard…
Fastlane vous crée une page HTML récapitulant tous les devices par langue.
Cool non ?
Vous pouvez fournir des arguments supplémentaires à votre application lors du lancement. En effet, vous pouvez ajouter des valeurs à votre UserDefaults.standard :
2.
3.
launch_arguments
([
"-firstName Hatem -lastName Ben Arfa"
])
name
.
text
=
UserDefaults
.
standard
.
string
(
forKey
:
"firstName"
)
// name.text = "Hatem"
Fastlane inclut FASTLANE_SNAPSHOT, qui permet de définir temporairement un UserDefaults.standard. Vous pouvez l'utiliser pour détecter quand l'application est exécutée par Snapshot.
2.
3.
if
UserDefaults
.
standard
.
bool
(
forKey
:
"FASTLANE_SNAPSHOT"
)
{
// Vérifier si vous utilisez le mode snapshot
}
Bon, maintenant qu'on a nos beaux screenshots, on va ajouter de beaux cadres non ? Allez c'est parti.
VI-B. Frameit▲
Prérequis
Pour utiliser Frameit, il faut avoir sur votre machine ImageMagick.
Sur mac, il suffit de faire :
$
brew install imagemagick
Vous pouvez dès à présent essayer ce petit bijou via :
# Exécutez cette commande dans votre dossier où se situent les screenshots.
$
bundle exec fastlane frameit
Vous avez normalement de nouvelles images qui sont arrivées telles que :
Et si vous avez envie d'avoir l'iphone rose on fait comment ?
2.
3.
4.
# Tout d'abord, mettez à jour votre liste de frames
$
bundle exec fastlane frameit download_frames
$
bundle exec fastlane frameit rose_gold
Vous trouverez la liste des frames ici.
Bon, c'est pas mal, mais on peut encore faire mieux. Comment ? En ajoutant un titre à ce beau screenshot.
Dans la version 2.0 de Frameit, vous pouvez maintenant ajouter un fond custom, un titre et des couleurs à vos screenshots.
Créer un fichier Framefile.json dans le dossier screenshots :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
{
"device_frame_version"
:
"latest"
,
"default"
:
{
"title"
:
{
"color"
:
"#545454"
},
"background"
:
"./background.jpg"
,
// Ajoute un background
"padding"
:
50
,
"show_complete_frame"
:
true,
// Rétrécit le device et le cadre afin de tout afficher
"stack_title"
:
false // Spécifie si frameit doit afficher le mot-clé au-dessus du titre lorsque le mot-clé et le titre sont définis.
},
"data"
:
[
{
"filter"
:
"01CarList"
,
// Si votre screenshot se nomme Iphone iPhone6-01CarList-*.png alors il utilisera ses paramètres
"title"
:
{
"text"
:
"Mes voitures"
,
"padding"
:
100
,
"color"
:
"#d21559"
}
},
{
"filter"
:
"02CarDetail"
,
"title"
:
{
"text"
:
"Ma mustang"
,
"padding"
:
100
}
}
]
}
VII. Gym▲
Gym construit et crée des packages de vos applications iOS pour vous. Il génère un fichier ipa ou app.
Installation de gym afin de configurer les paramètres par défaut :
$
bundle exec fastlane gym init
Configuration de votre Gymfile
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
scheme "FastlaneArticle"
clean true
sdk "iphoneos10.3"
# Le SDK qui devra être utilisé pour le build de l'application
output_directory "../build"
# Chemin où nous allons stocker le fichier .ipa
output_name "Fastlane"
silent true # Cache toutes les informations qui ne sont pas nécessaires pendant le build
#configuration => Configuration utilisée pour le build. Par défaut la valeur est Release
Nous allons créer une lane pour construire notre application pour Apple TestFlight :
2.
3.
4.
5.
6.
desc "Submits to Apple TestFlight"
lane :buildTestFlight
do
match(
app_identifier:
"com.eleven.fastlane.debug"
, type:
"appstore"
)
increment_build_number # Incrémente le chiffre du build de votre projet
gym(
configuration:
"Debug"
)
end
$
bundle exec fastlane buildTestFlight
Note : il se peut que vous ayez une erreur due au fait que vous n'avez pas de bundle identifié sur Itunes Connect. Pour remédier à ça, il faut juste en créer un via :
$
bundle exec fastlane produce -u MAIL -a com.eleven.fastlane.debug --skip_itc
Avouez que c'est plus facile que de le faire soi-même. Maintenant, on va voir comment envoyer notre fichier ipa pour le tester via TestFlight.
VIII. Pilot▲
Pilot permet de :
- uploader et distribuer vos builds ;
- ajouter et supprimer vos testeurs ;
- récupérer des informations sur les testeurs et les devices ;
- importer/exporter tous les testeurs disponibles.
Pour uploader un nouveau build vous pouvez exécuter la commande :
$
bundle exec fastlane pilot upload
Cela recherchera automatiquement un ipa dans votre répertoire courant et tentera de récupérer les informations d'identification de connexion à partir de votre configuration Fastlane.
Note : pensez bien à créer votre application sur Itunes Connect avant. (Ou utilisez la bibliothèque produce.)
Nous pouvons reprendre notre lane précédemment créée et y ajouter l'instruction pilot :
2.
3.
4.
5.
6.
7.
desc "Submits to Apple TestFlight"
lane :buildTestFlight
do
match(
app_identifier:
"com.eleven-labs.testflight"
, type:
"appstore"
)
increment_build_number # Incrémente le chiffre du build de votre projet
gym(
configuration:
"Debug"
, include_bitcode:
true
)
pilot(
app_identifier:
"com.eleven-labs.testflight"
, ipa:
"../build/Fastlane.ipa"
)
end
$
bundle exec fastlane buildTestFlight
Note
- Si comme moi, vous avez une double identification via votre téléphone, il suffit de se connecter ici et de générer un password dans la section Security > APP-SPECIFIC PASSWORDS ;
- Vérifiez sur votre Itunes Connect le bundle identifier renseigné.
IX. Deliver▲
Il est temps de mettre en production votre application sur Itunes Connect :
# Comme d'habitude on initialise
$
bundle exec fastlane deliver init
# Deliverfile
username "yourItunesEmail@eleven-labs.com"
Il vous suffit dans votre lane d'ajouter l'instruction :
deliver(
force:
true
)
# Force: true pour skip le report HTML de vérification
Vous devriez avoir un nouveau dossier metadata qui vient d'apparaître.
À vous maintenant de mettre à jour ces metadata.
X. Conclusion▲
Si vous n'êtes pas tombé amoureux de Fastlane, je ne comprends pas, vous aimez souffrir. On voit très rapidement que ce petit bijou nous fait gagner un temps monstre et nous évite de faire une erreur humaine (l'avantage de l'automatisation). Après quelques tests et configurations, vous pouvez facilement reprendre vos scripts pour les réutiliser sur d'autres projets.
XI. Remerciements▲
Nous remercions Eleven Labs qui nous autorise à publier ce tutoriel.
Nous tenons également à remercier Winjerome pour la mise au gabarit et Claude LeLoup pour la relecture orthographique.