{"id":21084,"date":"2018-08-08T12:55:09","date_gmt":"2018-08-08T10:55:09","guid":{"rendered":"http:\/\/www.inovex.de\/blog\/?p=12810"},"modified":"2024-01-09T07:31:27","modified_gmt":"2024-01-09T06:31:27","slug":"continuous-delivery-ios-apps-gitlab-ci-fastlane","status":"publish","type":"post","link":"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/","title":{"rendered":"Continuous Delivery von iOS Apps mit GitLab CI und fastlane"},"content":{"rendered":"<p>In diesem Artikel zeige ich den von uns eingesetzten Workflow zur Continuous Delivery von iOS Apps am Beispiel unserer Entwicklungsumgebung bei inovex \u2013 mit GitLab als Webfrontend f\u00fcr die Repositoryverwaltung, GitLab CI als Continuous Integration Umgebung, sowie fastlane als Build-Tool.<!--more--><\/p>\n<p>Als IT-Projekthaus entwickeln wir direkt und eigenst\u00e4ndig iOS Apps f\u00fcr unsere Kunden, unterst\u00fctzen jedoch auch bestehende Entwicklungsteams bei internen Kundenprojekten, wie die <a href=\"https:\/\/www.inovex.de\/de\/referenzen\/case-studies\/entwicklung-von-android-und-ios-apps-fuer-streaming-service-waiputv\/\">Case Study der waipu.tv App<\/a>\u00a0zeigt. In den einzelnen Projekten schwankt allerdings je nach Projektstand die Anzahl und Zusammensetzung der Entwickler:innen.<\/p>\n<p>Dadurch wird es n\u00f6tig, dass die zu entwickelnde iOS App unabh\u00e4ngig von bestimmten Entwickler:innen f\u00fcr den App Store gebaut, signiert und hochgeladen werden kann. Da die App-Entwicklung meist agil und in mehreren Iterationen abl\u00e4uft, sollten Product Owner und Tester:innen in regelm\u00e4\u00dfigen Abst\u00e4nden lauff\u00e4hige Versionen der App zur Verf\u00fcgung gestellt bekommen. Auch dies darf nicht auf einer Schulter lasten und die Entwickler:innen m\u00f6glichst wenig in ihrem Arbeitsprozess besch\u00e4ftigen. W\u00e4hrend der Entwicklung kann zudem kontinuierliches Testen auf den Feature-Branches sicherstellen, dass Entwickler:innen sich nur lauff\u00e4hige Merge Requests von Kolleg:innen anschauen.<\/p>\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_79_2 counter-hierarchy ez-toc-counter ez-toc-custom ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\"><p class=\"ez-toc-title\" style=\"cursor:inherit\"><\/p>\n<\/div><nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#Entwicklungsumgebung-fuer-die-Continuous-Delivery-von-iOS-Apps\" >Entwicklungsumgebung f\u00fcr die\u00a0Continuous Delivery von iOS Apps<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#Konfiguration-CICD\" >Konfiguration CI\/CD<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#Runner\" >Runner<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#Verwendung\" >Verwendung<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#Bauen-einer-iOS-App\" >Bauen einer iOS App<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#Bauen-der-App-mit-fastlane\" >Bauen der App mit fastlane<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#Build-Konfiguration\" >Build-Konfiguration<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#Zertifikatsmanagement\" >Zertifikatsmanagement<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#Distribution-der-iOS-App\" >Distribution der iOS App<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#Fazit\" >Fazit<\/a><\/li><\/ul><\/nav><\/div>\n<h3><span class=\"ez-toc-section\" id=\"Entwicklungsumgebung-fuer-die-Continuous-Delivery-von-iOS-Apps\"><\/span>Entwicklungsumgebung f\u00fcr die\u00a0Continuous Delivery von iOS Apps<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Wie <a href=\"https:\/\/www.inovex.de\/blog\/author\/mwiedemann\/\">Maximilian<\/a> in seinem <a href=\"https:\/\/www.inovex.de\/blog\/modern-cicd-with-jenkins-2-and-gitlab-ci-comparison\/\" target=\"_blank\" rel=\"noopener\">Blog-Artikel<\/a> bereits erl\u00e4utert hat, setzen wir bei inovex GitLab EE als Web Frontend f\u00fcr unsere Git-Repositories ein und haben uns entschieden, das GitLab CI Modul bei unseren Projekten f\u00fcr das kontinuierliche Bauen und Integrieren von Softwarekomponenten zu verwenden. Diese Kombination erm\u00f6glicht es, innerhalb des GitLab Frontends auf Ereignisse wie z. B. den Push oder Merge eines Branches zu reagieren.<\/p>\n<p>F\u00fcr das automatisierte kontinuierliche Bauen und Verteilen der iOS App ist man jedoch nicht auf diese Kombination beschr\u00e4nkt. Auch andere L\u00f6sungen, wie z. B. auf Basis von Jenkins, k\u00f6nnen verwendet werden. Daf\u00fcr kann der Abschnitt <em>Bauen der iOS App<\/em> unabh\u00e4ngig betrachtet werden.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Konfiguration-CICD\"><\/span>Konfiguration CI\/CD<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Um innerhalb des Git-Repositories auf Ver\u00e4nderung der Branches, wie einen Push von Neuerungen, zu reagieren, m\u00fcssen wir dies unserer CI\/CD Pipeline mitteilen. F\u00fcr GitLab CI wird dies in einer <code>.gitlab-ci.yml<\/code> Konfigurationsdatei gemacht, die im Root-Verzeichnis des Repositories platziert werden muss.<\/p>\n<p>Der Grundaufbau so einer Konfigurationsdatei f\u00fcr einen iOS Build Job kann wie folgt aussehen. Eine komplette Dokumentation ist unter <a href=\"https:\/\/docs.gitlab.com\/ce\/ci\/yaml\/\">Configuration of your jobs with .gitlab-ci.yml<\/a> direkt bei GitLab zu finden.<\/p>\n<pre><code># 1\r\n\r\nbefore_script:\r\n\r\n  - bundle install\r\n\r\n# 2\r\n\r\nafter_script:\r\n\r\n  - killall Simulator || true\r\n\r\n# 3\r\n\r\nstages:\r\n\r\n  - test\r\n\r\n  - build\r\n\r\n# 4\r\n\r\nbuild:\r\n\r\n  stage: build\r\n\r\n#  5\r\n\r\n  script:\r\n\r\n    - fastlane release_build\r\n\r\n# 6\r\n\r\n  only:\r\n\r\n    - \/^release\\\/.+$\/\r\n\r\n  except:\r\n\r\n    - branches\r\n\r\n# 7\r\n\r\n  tags:\r\n\r\n    - Xcode9.2\r\n\r\n# 8\r\n\r\n  artifacts:\r\n\r\n    paths:\r\n\r\n      - fastlane\/test_output\/\r\n\r\n    expire_in: 10 days\r\n\r\ntest:\r\n\r\n#  ....\r\n\r\n<\/code><\/pre>\n<ol>\n<li>Um bestimmte Aktionen vor einem Job auszuf\u00fchren, kann man unter dem\u00a0<span style=\"caret-color: #222222; color: #222222; font-family: monospace; background-color: #e9ebec;\">before_script<\/span>-Tag die gew\u00fcnschten Befehle auflisten. F\u00fcr einen iOS Build, der Cocoapods als Dependency Management und fastlane als Buildtool einsetzt, kann man hier z. B. die notwendigen <em>Ruby Gems\u00a0<\/em>aus dem <code>Gemfile<\/code> mit <code>bundle inst<\/code>installieren.<\/li>\n<li>Analog zum <code>before_script<\/code>-Tag kann man auch Skripte nach dem Ausf\u00fchren eines Jobs laufen lassen. Wie hier zu sehen k\u00f6nnte man beispielsweise sicherstellen, dass der iOS-Simulator beendet wird, sollte er noch laufen: <code>killall Simulator || true<\/code><\/li>\n<li>\u00dcber <em>stages<\/em> kann man unterschiedliche Build-Schritte definieren, die in einer gewissen Reihenfolge ausgef\u00fchrt werden k\u00f6nnen. Wie hier die Schritte <em>test<\/em> und <em>build<\/em>, die einmal die Unittests ausf\u00fchren und dann die iOS App f\u00fcr die Distribution bauen.<\/li>\n<li>Konkrete Build-Tasks werden in einem eigenen <code>yaml<\/code>-Root Tag mit einem eigenen Namen definiert und \u00fcber <code>stage<\/code> einem gewissen Build-Schritt zugeteilt.<\/li>\n<li>Im <code>script<\/code>-Block kann man die f\u00fcr den Build-Schritt notwendigen Skripte aufrufen. In unserem Setup besteht der Aufruf nur aus der Ausf\u00fchrung einer <em>fastlane<\/em>\u00a0Lane. Mehr dazu im Abschnitt <em>Bauen der App mit fastlane.<\/em><\/li>\n<li>\u00dcber <code>only<\/code> und <code>except<\/code> kann man definieren, f\u00fcr welche Git-Referenzen man einen bestimmten Job ausf\u00fchrt. In diesem Beispiel wird durch\u00a0<code>except branches<\/code> der Job nur f\u00fcr Git Tags ausgef\u00fchrt, die mit dem Prefix <code>release<\/code> anfangen, z. B. <code>release\/1.2.5<\/code>.<\/li>\n<li>Innerhalb des <code>tags<\/code>-Blocks definiert man die Verbindung zum Build Server. Nur Build Server, bzw. die auf ihm laufenden Runner, die den gleichen Tag haben, werden f\u00fcr den Build verwendet. Im n\u00e4chsten Abschnitt mehr dazu.<\/li>\n<li>Nach einem erfolgreichen Test oder Build ist es m\u00f6glich, dass man Artefakte wie Testreports oder die IPA-Datei aus den <code>paths<\/code>-Ordnern ans GitLab Web Frontend schickt. Diese Dateien k\u00f6nnen auch zeitlich begrenzt durch\u00a0<code>expires_in<\/code> Parameter persistiert werden. Diese M\u00f6glichkeit zum Speichern von Artefakten eignet sich, wenn man eine h\u00e4ndische Nachverarbeitung, wie z. B. den manuellen App Store Upload mit dem Application Loader, durchf\u00fchren m\u00f6chte.<\/li>\n<\/ol>\n<h3><span class=\"ez-toc-section\" id=\"Runner\"><\/span>Runner<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Um einen Build Job konkret ausf\u00fchren zu k\u00f6nnen, muss nun eine Verbindung zwischen dem GitLab Web Frontend mit dem GitLab-CI-Modul und einem macOS Build Server vorhanden sein.<\/p>\n<p>Dies wird mit einem GitLab Runner realisiert \u2013 einem Executable, das als Service auf dem Build Server l\u00e4uft und eine Verbindung zum GitLab-CI-Modul hat. Die Einrichtung wird <a href=\"https:\/\/docs.gitlab.com\/runner\/\">hier<\/a> erl\u00e4utert.<\/p>\n<p>Ein Runner kann dabei jeder beliebige macOS-Rechner sein. Wir setzen beispielsweise mehrere Mac Minis f\u00fcr die Continuous Delivery von iOS Apps ein.<\/p>\n<p>Bei der Einrichtung oder einer sp\u00e4teren Konfiguration im GitLab Web Frontend kann man dem GitLab Runner die <code>tags<\/code> aus Punkt 7 des vorherigen Abschnittes zuweisen, die dann der Build Job referenzieren kann. Man kann einem Runner mehrere Tags zuweisen. So l\u00e4sst sich, dass auf einem Build Server unterschiedliche Xcode-Versionen installiert sind und ein iOS-Projekt eine bestimmte Xcode-Version ben\u00f6tigt.<\/p>\n<figure id=\"attachment_12817\" aria-describedby=\"caption-attachment-12817\" style=\"width: 300px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-12817 size-medium\" src=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2018\/03\/DraggedImage-300x50.png\" alt=\"Abbildung mehrerer Tags bei einem GitLab Runner\" width=\"300\" height=\"50\" \/><figcaption id=\"caption-attachment-12817\" class=\"wp-caption-text\">Mehrere Tags bei einem GitLab Runner.<\/figcaption><\/figure>\n<p>Die Auswahl der im Build Job konkret verwendeten Xcode-Version entscheidet sich dann jedoch in der <em>fastlane<\/em> lane.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Verwendung\"><\/span>Verwendung<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Als Entwicklungsworkflow in Git setzen wir je nach Projekt auf GitFlow oder eine leicht angepasste, vereinfachte Variante. Innerhalb des Workflows lassen wir uns von GitLab CI unterst\u00fctzen.<\/p>\n<p>W\u00e4hrend der Entwicklung eines Features auf einem dedizierten Featurebranch k\u00f6nnen wir den aktuellen Stand zum <code>origin<\/code> pushen. Zudem werden f\u00fcr diesen Branch \u00fcber eine <em>fastlane<\/em> lane die Unit-Tests ausgef\u00fchrt. Das Resultat des Build Jobs ist f\u00fcr Entwickler:innen des Features direkt im GitLab Web Frontend sichtbar. Dadurch kann er sich sicher sein, dass alle notwendigen Dateien eingecheckt und Konfigurationen durchgef\u00fchrt wurden, sodass die App nicht nur bei ihm erfolgreich gebaut wird.<\/p>\n<figure id=\"attachment_12812\" aria-describedby=\"caption-attachment-12812\" style=\"width: 300px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-12812 size-medium\" src=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2018\/03\/DraggedImage-1-300x78.png\" alt=\"Abbildung des Ergebnis eines Build Jobs\" width=\"300\" height=\"78\" \/><figcaption id=\"caption-attachment-12812\" class=\"wp-caption-text\">Ergebnis eines Build Jobs<\/figcaption><\/figure>\n<p>Ein Feature landet bei uns immer \u00fcber einen Merge Request, der von einem\/einer anderen Entwickler:in begutachtet wird, im <code>develop<\/code>-Branch. Den Merge Request stellen wir auch \u00fcber das GitLab Web Fronted, weil uns dort direkt angezeigt wird, ob der Feature-Branche gebaut werden konnte und die Unit-Tests alle erfolgreich durchgef\u00fchrt wurden.<\/p>\n<figure id=\"attachment_12813\" aria-describedby=\"caption-attachment-12813\" style=\"width: 300px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-12813 size-medium\" src=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2018\/03\/DraggedImage-2-300x105.png\" alt=\"Abbildung eines Merge Request f\u00fcr Feature im GitLab Web Frontend\" width=\"300\" height=\"105\" \/><figcaption id=\"caption-attachment-12813\" class=\"wp-caption-text\">Merge Request f\u00fcr Feature im GitLab Web Frontend<\/figcaption><\/figure>\n<p>Das spart dem\/der begutachtenden Entwickler:in Zeit und \u00c4rger, weil er sich nur Merge Requests anschaut, die bauen und deren Unit-Test erfolgreich durchgelaufen sind. Man kann sich also auf die Begutachtung des konkreten Features konzentrieren.<\/p>\n<p>Nach dem Merge eines Features auf\u00a0<code>develop<\/code>, wird in der Regel eine App-Version f\u00fcr die Distribution an den Product Owner gebaut und automatisch an diesen verteilt.<\/p>\n<p>Ein Release f\u00fcr den App Store mit passendem Zertifikat und Mobile Provisioning Profile wird bei der Erstellung eines Git Tags erstellt und dann direkt an iTunes Connect \u00fcbermittelt. Die Build Pipeline des Jobs enth\u00e4lt dann mehrere Stages.<\/p>\n<figure id=\"attachment_12814\" aria-describedby=\"caption-attachment-12814\" style=\"width: 300px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-12814 size-medium\" src=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2018\/03\/DraggedImage-3-300x68.png\" alt=\"Abbildung einer Build Pipeline mit mehreren Stages\" width=\"300\" height=\"68\" \/><figcaption id=\"caption-attachment-12814\" class=\"wp-caption-text\">Build Pipeline mit mehreren Stages<\/figcaption><\/figure>\n<h2><span class=\"ez-toc-section\" id=\"Bauen-einer-iOS-App\"><\/span>Bauen einer iOS App<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Der \u00e4lteste und klassischste Weg eine iOS App f\u00fcr die Distribution zu bauen \u2013\u00a0<em>Ad-Hoc<\/em>, <em>In-House<\/em> oder <em>App Store \u2013<\/em>, f\u00fchrt direkt in <em>Xcode<\/em> \u00fcber <code>Product &gt; Archive<\/code>. Daf\u00fcr m\u00fcssen Entwickler:innen jedoch das passende Zertifikat mit privatem Key und das Mobile Provisioning Profile f\u00fcr die App lokal auf seinem Rechner haben. Neben der Frage des Vertrauens bei Zertifikaten von Kunden stellt sich auch die Frage, wie man diesen Ansatz besser skalieren kann, wenn die betroffenen Entwickler:innen nicht zur Verf\u00fcgung stehen. Und die wenigstens Entwickler:innen, da leidgepr\u00fcft, m\u00f6chten Xcode das Verwalten der Zertifikate \u00fcberlassen. Diese L\u00f6sung eignet sich daher nur f\u00fcr Projekte, die von Entwickler:innen f\u00fcr eine eigene App betreut werden.<\/p>\n<p>Eine Erweiterung und Verbesserung davon ist, dass man die Zertifikate und Provisioning Profiles f\u00fcr die Distribution zentral auf einem Build Server bereitstellt und das Bauen der App \u00fcber die Xcode Command Line Tools als Shell-Befehl durchf\u00fchrt. Ein klassischer Befehl zum Bauen des Archives kann dann wie folgt lauten:<\/p>\n<pre><code>xcodebuild clean archive -workspace .\/iOSApp.xcworkspace -scheme ${SCHEME} -sdk iphoneos11.0 -archivePath .\/ -configuration Release ONLY_ACTIVE_ARCH=\"NO\" OTHER_CODE_SIGN_FLAGS=\" DEVELOPMENT_TEAM=\"${DEV_TEAM}\"  PROVISIONING_PROFILE_SPECIFIER=\"iOSApp.mobileprovisioning\" CODE_SIGN_IDENTITY=\"${SIGN_ID}\"\r\n\r\n<\/code><\/pre>\n<p>Und die IPA kann man aus dem Archive folgenderma\u00dfen generieren:<\/p>\n<pre><code>xcodebuild -exportArchive -exportOptionsPlist ${EXPORT_PLIST} -archivePath .\/${BUILD_DIR}${ARCHIVE_DIR}${SCHEME}.xcarchive -exportPath .\/${BUILD_DIR}${IPA_DIR}\/\r\n\r\n<\/code><\/pre>\n<p>Der Ansatz scheint erstmal sehr praktikabel und reicht f\u00fcr viele Anwendungsgebiete bereits aus, da man jetzt unabh\u00e4ngig von Entwickler:innen die App f\u00fcr die Distribution bauen kann.<\/p>\n<p>Ein Nachteil ist jedoch, dass man selbst komplexe Shell-Skripte erzeugt, sowohl um Unit-Tests auszuf\u00fchren als auch um die App zu bauen. Zudem gibt es durch Apple in unregelm\u00e4\u00dfigen Abst\u00e4nden \u00c4nderungen am Command Line Tool Interface, wie z. B. das Hinzuf\u00fcgen des <code>exportOptionsPlist<\/code>-Flags oder das Anpassen des Formats der zugeh\u00f6rigen <em>.plist<\/em> Datei. Diese \u00c4nderungen muss man in seinen Skripten selbst nachpflegen.<\/p>\n<p>Auch die Frage des Zertifikatsmanagements ist noch nicht zufriedenstellend gekl\u00e4rt. Wenn man nur eine App auf einem Build Server baut, kann man die Zertifikate und Mobile-Provisioning-Profile manuell dort installieren. Gibt es jedoch mehrere Build Server, auf denen unterschiedliche Projekte gebaut werden sollen, ist ein manuelles Management nicht mehr handlich und aus Kundensicht nicht mehr erw\u00fcnscht.<\/p>\n<p>F\u00fcr beide Probleme stelle ich nachfolgend m\u00f6gliche L\u00f6sungen vorstellen.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Bauen-der-App-mit-fastlane\"><\/span>Bauen der App mit fastlane<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p><a href=\"https:\/\/fastlane.tools\/\">fastlane<\/a> ist eine Ansammlung von Ruby-Skripten, die mittels einer Ruby-DSL das Bauen, Signieren und Verteilen einer iOS App f\u00fcr Entwickler:innen deutlich vereinfacht. Dabei ist es nur ein Frontend zu den oben genannten Kommandozeilenbefehlen, abstrahiert diese jedoch sehr komfortabel und k\u00fcmmert sich durch kontinuierliche Updates um gewisse CLI-\u00c4nderungen sogar selbst. Die ggf. n\u00f6tigen Anpassungen der <em>fastlane-<\/em>Skripte in einem l\u00e4ngeren Projektverlauf halten sich dadurch in Grenzen und sind f\u00fcr Entwickler:innen einfacher durchzuf\u00fchren.<\/p>\n<p>Zus\u00e4tzlich zu diesen Basisfunktionalit\u00e4ten werden eine Reihe von <a href=\"https:\/\/docs.fastlane.tools\/actions\/\">Actions<\/a> angeboten, die bereits L\u00f6sungen f\u00fcr andere allt\u00e4gliche Problemstellungen bieten. Dazu z\u00e4hlen z.B. das Inkrementieren von Build-Nummern oder das Erzeugen eines Icon Overlays f\u00fcr Testversionen. Sollte es f\u00fcr einen Anwendungsfall noch keine Action geben, so kann man eine eigene Action \u00fcber die vorhandene <a href=\"https:\/\/docs.fastlane.tools\/plugins\/create-plugin\/\">Plugin-Schnittstelle<\/a> selbst erstellen und analog zu den eingebauten Actions nutzen.<\/p>\n<p>Das Hinzuf\u00fcgen von <em>fastlane<\/em> zu einem iOS-Projekt ist auf der <a href=\"https:\/\/docs.fastlane.tools\/getting-started\/ios\/setup\/\">Projektseite<\/a> erkl\u00e4rt. Da es sich um ein Ruby Gem handelt, f\u00fcgen wir es in unseren Projekten zum <em>Gemfile<\/em> des iOS-Projekts hinzu und installieren es wie <em>Cocoapods<\/em> vor jedem Build mit einem <code>bundle install<\/code>. Damit stellen wir sicher, dass der Build Server nur eine Minimalkonfiguration ben\u00f6tigt und alle zus\u00e4tzlichen Abh\u00e4ngigkeiten w\u00e4hrend des Build Jobs aufgel\u00f6st werden.<\/p>\n<p>Nach dem Einrichten von <em>fastlane<\/em> f\u00fcr ein Projekt wird eine <em>Fastfile-<\/em>Datei erstellt. In dieser Datei befinden sich die Beschreibungen der einzelnen Build Jobs. Jeder Block mit dem Keyword <code>lane<\/code> ist dabei ein Job, der gezielt aufgerufen werden werden kann. Ein <em>Fastfile<\/em> kann wie folgt aussehen.<\/p>\n<pre><code># 1\r\n\r\nfastlane_version \"2.84.0\"\r\n\r\n# 2\r\n\r\nxcversion(version: \"&gt; 9.2\")\r\n\r\n# 3\r\n\r\ncocoapods(repo_update: true)\r\n\r\n# 4\r\n\r\nlane :release_build do\r\n\r\n  # 5\r\n\r\n  update_info_plist(plist_path: \".\/iOSApp\/Info.plist\",\r\n\r\n    display_name: \u201ciOSApp\",\r\n\r\n    app_identifier: \"com.example.iOSApp\")\r\n\r\n  #6\r\n\r\n  run_tests(scheme: \"iOSAppTests\",\r\n\r\n    clean: true)\r\n\r\n  # 7\r\n\r\n  build_ios_app(scheme: 'iOSApp', method: 'app-store')\r\n\r\n  # 8\r\n\r\n  hockey(api_token: 'xxxxxxxxxx',\r\n\r\n    ipa: Actions.lane_context[SharedValues::IPA_OUTPUT_PATH],\r\n\r\n    notes: \"Changelog\")\r\n\r\nend\r\n\r\n<\/code><\/pre>\n<p>\u00dcber alle Lanes hinweg kann man globale Konfigurationen festlegen, wenn man die jeweiligen Ruby-Methoden au\u00dferhalb der Lanes aufruft, wie in den Punkten 1 bis 3 zu sehen.<\/p>\n<ol>\n<li>Mit <code>fastlane_version<\/code> kann man die minimal zu verwendende Version setzen. Beachten sollte man, dass man \u00fcber Ruby Gems eine passende Version installiert.<\/li>\n<li>Die zu verwendende Xcode-Version kann man mittels <code>xcversion<\/code> setzen. Besonders wenn man mehrere Xcode-Versionen auf dem Build Server installiert hat, sollte man darauf achten, dass die richtige Version angegeben wird. <em>fastlane<\/em> setzt dabei auf ein Namensschema f\u00fcr die Xcode-Binary-Benamung, <code>Xcode_x.x<\/code>, f\u00fcr die unterschiedlichen Versionen.<\/li>\n<li>Vor jeder Lane f\u00fchren wir auch\u00a0eine Installation der Dependencies aus, wobei jeweils noch mal das Cocopods-Repo nach neuen Pods und Versionen abgefragt wird. Auch dies soll sicherstellen, dass ein Build Server nur minimal konfiguriert werden muss.<\/li>\n<\/ol>\n<p>Ab Punkt 4 ist eine <em>fastlane<\/em> lane definiert, die man \u00fcber die Kommandozeile direkt mit ihrem Namen ansprechen kann, wie es auch in der <code>.gitlab-ci.yml<\/code> geschieht.<\/p>\n<pre><code>$ fastlane release_build\r\n\r\n<\/code><\/pre>\n<ol>\n<li>Mit dem Keyword <code>lane<\/code> und einem eindeutigen Namen (<em>release_build<\/em>) definiert man einen Build Job.<\/li>\n<li>Am Anfang jedes Build Jobs kann man das Projekt noch \u00fcber verschiedenste Helfer-Actions f\u00fcr den Build konfigurieren, wie z. B. die <code>Info.plist<\/code> des Projekts anpassen.<\/li>\n<li>Mittels <a href=\"https:\/\/docs.fastlane.tools\/actions\/run_tests\/\">run_tests<\/a> kann man auf einfache Weise die Unit-Tests eines Target ausf\u00fchren.<\/li>\n<li>Der eigentliche Bau und das Paketieren der <em>IPA-<\/em>Datei wird \u00fcber\u00a0<a href=\"https:\/\/docs.fastlane.tools\/actions\/build_ios_app\/\">build_ios_app<\/a> \u00a0durchgef\u00fchrt. Dort kann auch je nach Anwendungsfall die Methode angeben, ob man einen Ad-Hoc-, einen Enterprise-In-House, oder App-Store-Build erstellen m\u00f6chte.<\/li>\n<li>Nach der Paketierung kann man die erstellte <em>IPA-<\/em>Datei per Umgebungsvariable ansprechen und beispielsweise mit einer vordefinierten Action (<a href=\"https:\/\/docs.fastlane.tools\/actions\/hockey\/\">hockey<\/a>) an HockeyApp \u00fcbertragen.<\/li>\n<\/ol>\n<h3><span class=\"ez-toc-section\" id=\"Build-Konfiguration\"><\/span>Build-Konfiguration<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>In einem Projekt k\u00f6nnen je Anforderungen unterschiedliche Build-Versionen anfallen: Ad-Hoc, In-House oder App Store.<\/p>\n<p>Xcode bietet \u00fcber die <em>Configurations<\/em> in einem App Target die M\u00f6glichkeit an, jeweils das zu verwendende Zertifikat und Mobile Provisioning Profile anzugeben.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-12815 size-medium\" src=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2018\/03\/DraggedImage-4-300x105.png\" alt=\"Abbildung der Configurations-Optionen\" width=\"300\" height=\"105\" \/><\/p>\n<p>Dabei m\u00fcssen die angegebenen Zertifikate und Provisioning-Profile nicht f\u00fcr alle Build-Konfigurationen bei den Entwickler:innen vorliegen. So kann man beispielsweise f\u00fcr die Release-Variante schon die Enterprise- oder App-Store-Konfigurationen setzen, deren Zertifikate und Profile nur auf dem Build Server zur Verf\u00fcgung stehen.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-12816 size-medium\" src=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2018\/03\/DraggedImage-5-300x237.png\" alt=\"Abbildung von Zertifikat-Optionen\" width=\"300\" height=\"237\" \/><\/p>\n<p>Die Konfiguration im Projekt durchzuf\u00fchren, bringt den Vorteil, dass sie unabh\u00e4ngig von <em>fastlane<\/em>\u00a0ist und im eigentlichen Build Job nichts mehr angepasst werden muss. \u00dcber den <code>configuration<\/code> Parameter in <a href=\"https:\/\/docs.fastlane.tools\/actions\/build_ios_app\">build_ios_app<\/a>\u00a0kann man die f\u00fcr den Build passende Konfiguration setzen.<\/p>\n<p><em>fastlane<\/em> bietet auch die M\u00f6glichkeit, das zu verwendende Zertifikat und Provisioning Profile mit anzugeben. Dies geschieht direkt als Argument in der\u00a0<a href=\"https:\/\/docs.fastlane.tools\/actions\/build_ios_app\">build_ios_app<\/a>\u00a0Action.<\/p>\n<pre><code>export_options(\r\n\r\n  method: \"app-store\",\r\n\r\n  provisioningProfiles: {\r\n\r\n\"com.example.bundleid\" =&gt; \"Provisioning Profile Name\",\r\n\r\n\"com.example.bundleid2\" =&gt; \"Provisioning Profile Name 2\"\r\n\r\n  }\r\n\r\n)\r\n\r\n<\/code><\/pre>\n<p>\u00dcber <a href=\"https:\/\/docs.fastlane.tools\/advanced\/#appfile\">Appfiles<\/a> kann man die unterschiedlichen Konfigurationen zus\u00e4tzlich noch \u00fcbersichtlich verwalten. Man sollte bei der Build-Konfiguration jedoch darauf achten, dass man sich f\u00fcr einen Weg entscheidet, um inkonsistente Konfiguration zu vermeiden.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"Zertifikatsmanagement\"><\/span>Zertifikatsmanagement<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Die Verwaltung der Zertifikate und Mobile-Provisioning-Dateien f\u00fcr das Signieren der gebauten Apps ist wohl noch eins der meistbesprochenen und diskutierten Themen der iOS-Entwicklung.<\/p>\n<p>Auch hier bietet <em>fastlane<\/em> eine M\u00f6glichkeit, die Zertifikate und Profile zu verwalten. Das Modul <a href=\"https:\/\/docs.fastlane.tools\/actions\/sync_code_signing\/\">sync_code_signing<\/a> erm\u00f6glicht es, f\u00fcr Teams die Zertifikate und Profile verschl\u00fcsselt in einem gesonderten Git-Repository zu verwalten. Bei einem neuen Build werden diese dann aus dem Repo abgerufen und auf dem ausf\u00fchrenden Client installiert.<\/p>\n<p>Der Vorteil dieser L\u00f6sung ist, dass die Zertifikate und Profile nur von den Personen mit den n\u00f6tigen Git-Rechten betrachtet und verwaltet werden k\u00f6nnen. So kann sichergestellt werden, dass beispielsweise nur der Build Server eine App-Store-Variante der App bauen kann. Ein Nachteil bei <a href=\"https:\/\/docs.fastlane.tools\/actions\/sync_code_signing\/\">sync_code_signing<\/a> ist, dass bei der Einrichtung neue Zertifikate und Profile angelegt werden. Eine Migration von bestehenden Zertifikaten und Profilen ist offiziell nicht vorgesehen.<\/p>\n<p>Da wir unterschiedliche Kunden mit unterschiedlichen iOS-Projekten haben, die auch von anderen IT-Dienstleistern betreut werden k\u00f6nnen, haben wir uns entschieden, eine \u00e4hnliche Variante analog zu <em>match\u00a0<\/em>umzusetzen, bei der wir jedoch bereits bestehende Zertifikate und Profile verwenden k\u00f6nnen.<\/p>\n<p>Inspiriert durch den Blog-Post <a href=\"https:\/\/www.objc.io\/issues\/6-build-tools\/travis-ci\/#encrypt-certificates-and-profiles\">Travis CI for iOS<\/a> speichern wir die Zertifikate und Profile verschl\u00fcsselt im Projekt-Repository oder in einem gesonderten Repo. Bei jedem Build werden dann folgende Schritte zuerst durchgef\u00fchrt:<\/p>\n<ul>\n<li>Entschl\u00fcsselung von Zertifikaten und Profilen<\/li>\n<li>Erstellung eines tempor\u00e4ren Schl\u00fcsselbundes<\/li>\n<li>Importieren der Zertifikate in den Schl\u00fcsselbund<\/li>\n<li>Kopieren der Provisioning-Profile in den <code>MobileDevice Provisioning<\/code>-Ordner<\/li>\n<\/ul>\n<p>Nach einem Build \u2013 egal ob erfolgreich oder fehlerhaft \u2013 werden sowohl der tempor\u00e4re Schl\u00fcsselbund als auch die entschl\u00fcsselten Zertifikate und Profile wieder gel\u00f6scht. Die einzelnen Schritte f\u00fcr das Einrichten der Umgebung auf dem Build Server k\u00f6nnen einfach \u00fcber die Shell-Skripte im oben genannten Blogpost umgesetzt werden.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Distribution-der-iOS-App\"><\/span>Distribution der iOS App<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Bei der Verteilung der App an In-House-Tester oder den App Store setzen wir auf die vielf\u00e4ltigen Actions, die <em>fastlane<\/em> direkt mitliefert.<\/p>\n<p>Wenn die App zum Testen erstmal nur in-house verteilt werden soll, eignen sich Dienste wie HockeyApp oder <a href=\"http:\/\/try.crashlytics.com\/beta\/\">Beta by Crashlytics<\/a><em>,<\/em>\u00a0von deren Web-Oberfl\u00e4che man die Apps direkt installieren kann. F\u00fcr beide gibts es Actions von <em>fastlane<\/em>. M\u00f6chte man seine Distribution einfacher halten, so kann man sich auch \u00fcber die <a href=\"https:\/\/docs.fastlane.tools\/actions\/s3\/\">s3<\/a> Action die n\u00f6tigen Dateien f\u00fcr die In-House-Verteilung erzeugen lassen und auf einem S3 Bucket ver\u00f6ffentlichen.<\/p>\n<p>Ein direkter iTunes Connect Upload wird \u00fcber die <a href=\"https:\/\/docs.fastlane.tools\/actions\/upload_to_testflight\/\">upload_to_testflight<\/a> Action erm\u00f6glicht. So kann man direkt eine App-Version an seine TestFlight-Nutzer:innen verteilen.<\/p>\n<p>Auf der <a href=\"https:\/\/fabric.io\/features\/distribution\">Distribution<\/a>-Seite finden sich Code-Beispiele f\u00fcr die unterschiedlichen M\u00f6glichkeiten.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Fazit\"><\/span>Fazit<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Die Kombination von GitLab CI und fastlane erlaubt es, dass sich iOS-Entwickler:innen mehr auf die reine Entwicklung konzentrieren k\u00f6nnen, anstatt sich mit der Build-Infrastruktur und der Verteilung von Apps gro\u00df auseinander setzen zu m\u00fcssen. Gerade fastlane bietet f\u00fcr allt\u00e4gliche Probleme bereits eine Reihe von <a href=\"https:\/\/docs.fastlane.tools\/actions\/\">Actions<\/a> an. Eine Integration von CI-Pipelines in Merge Requests unterst\u00fctzen zudem schon w\u00e4hrend der Entwicklungsphase. Die durchgef\u00fchrte Trennung zwischen dem CI-Service und dem eigentlichen Bau und der Signierung der App w\u00fcrde zudem einen reibungslosen Wechsel zu anderen CI-Diensten wie <a href=\"https:\/\/travis-ci.com\/\">Travis CI<\/a> oder <a href=\"https:\/\/circleci.com\/\">CircleCI<\/a>\u00a0erm\u00f6glichen. Daher werden wir uns auch in zuk\u00fcnftigen Projekten f\u00fcr diesen Ansatz entscheiden.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In diesem Artikel zeige ich den von uns eingesetzten Workflow zur Continuous Delivery von iOS Apps am Beispiel unserer Entwicklungsumgebung bei inovex \u2013 mit GitLab als Webfrontend f\u00fcr die Repositoryverwaltung, GitLab CI als Continuous Integration Umgebung, sowie fastlane als Build-Tool.<\/p>\n","protected":false},"author":68,"featured_media":13636,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"ep_exclude_from_search":false,"footnotes":""},"tags":[510],"service":[420],"coauthors":[{"id":68,"display_name":"Sebastian Me\u00dfingfeld","user_nicename":"smessingfeld"}],"class_list":["post-21084","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","tag-apps-2","service-apps"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Continuous Delivery von iOS Apps mit GitLab CI und fastlane - inovex GmbH<\/title>\n<meta name=\"description\" content=\"Am Beispiel unserer Entwicklungsumgebung bei inovex mit GitLab als Webfrontend f\u00fcr die Repositoryverwaltung, GitLab CI als Continuous Integration Umgebung, sowie fastlane als Build-Tool zeige ich den von uns eingesetzten Workflow zur Continuous Delivery von iOS Apps.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Continuous Delivery von iOS Apps mit GitLab CI und fastlane - inovex GmbH\" \/>\n<meta property=\"og:description\" content=\"Am Beispiel unserer Entwicklungsumgebung bei inovex mit GitLab als Webfrontend f\u00fcr die Repositoryverwaltung, GitLab CI als Continuous Integration Umgebung, sowie fastlane als Build-Tool zeige ich den von uns eingesetzten Workflow zur Continuous Delivery von iOS Apps.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/\" \/>\n<meta property=\"og:site_name\" content=\"inovex GmbH\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/inovexde\" \/>\n<meta property=\"article:published_time\" content=\"2018-08-08T10:55:09+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-01-09T06:31:27+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2018\/08\/Continuous-Delivery-von-iOS-Apps.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1920\" \/>\n\t<meta property=\"og:image:height\" content=\"1080\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Sebastian Me\u00dfingfeld\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2018\/08\/Continuous-Delivery-von-iOS-Apps-1024x576.png\" \/>\n<meta name=\"twitter:creator\" content=\"@inovexgmbh\" \/>\n<meta name=\"twitter:site\" content=\"@inovexgmbh\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Sebastian Me\u00dfingfeld\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"15\u00a0Minuten\" \/>\n\t<meta name=\"twitter:label3\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data3\" content=\"Sebastian Me\u00dfingfeld\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/\"},\"author\":{\"name\":\"Sebastian Me\u00dfingfeld\",\"@id\":\"https:\/\/www.inovex.de\/de\/#\/schema\/person\/98fff9f09e7abd0bca8f6dd8c3cbaa02\"},\"headline\":\"Continuous Delivery von iOS Apps mit GitLab CI und fastlane\",\"datePublished\":\"2018-08-08T10:55:09+00:00\",\"dateModified\":\"2024-01-09T06:31:27+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/\"},\"wordCount\":2800,\"commentCount\":4,\"publisher\":{\"@id\":\"https:\/\/www.inovex.de\/de\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.inovex.de\/wp-content\/uploads\/2018\/08\/Continuous-Delivery-von-iOS-Apps.png\",\"keywords\":[\"Apps\"],\"articleSection\":[\"Applications\",\"General\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/\",\"url\":\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/\",\"name\":\"Continuous Delivery von iOS Apps mit GitLab CI und fastlane - inovex GmbH\",\"isPartOf\":{\"@id\":\"https:\/\/www.inovex.de\/de\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.inovex.de\/wp-content\/uploads\/2018\/08\/Continuous-Delivery-von-iOS-Apps.png\",\"datePublished\":\"2018-08-08T10:55:09+00:00\",\"dateModified\":\"2024-01-09T06:31:27+00:00\",\"description\":\"Am Beispiel unserer Entwicklungsumgebung bei inovex mit GitLab als Webfrontend f\u00fcr die Repositoryverwaltung, GitLab CI als Continuous Integration Umgebung, sowie fastlane als Build-Tool zeige ich den von uns eingesetzten Workflow zur Continuous Delivery von iOS Apps.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#primaryimage\",\"url\":\"https:\/\/www.inovex.de\/wp-content\/uploads\/2018\/08\/Continuous-Delivery-von-iOS-Apps.png\",\"contentUrl\":\"https:\/\/www.inovex.de\/wp-content\/uploads\/2018\/08\/Continuous-Delivery-von-iOS-Apps.png\",\"width\":1920,\"height\":1080,\"caption\":\"Continuous Delivery von iOS Apps visualisiert durch eine Pipeline\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.inovex.de\/de\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Continuous Delivery von iOS Apps mit GitLab CI und fastlane\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.inovex.de\/de\/#website\",\"url\":\"https:\/\/www.inovex.de\/de\/\",\"name\":\"inovex GmbH\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/www.inovex.de\/de\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.inovex.de\/de\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.inovex.de\/de\/#organization\",\"name\":\"inovex GmbH\",\"url\":\"https:\/\/www.inovex.de\/de\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\/\/www.inovex.de\/de\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.inovex.de\/wp-content\/uploads\/2021\/03\/inovex-logo-16-9-1.png\",\"contentUrl\":\"https:\/\/www.inovex.de\/wp-content\/uploads\/2021\/03\/inovex-logo-16-9-1.png\",\"width\":1921,\"height\":1081,\"caption\":\"inovex GmbH\"},\"image\":{\"@id\":\"https:\/\/www.inovex.de\/de\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/inovexde\",\"https:\/\/x.com\/inovexgmbh\",\"https:\/\/www.instagram.com\/inovexlife\/\",\"https:\/\/www.linkedin.com\/company\/inovex\",\"https:\/\/www.youtube.com\/channel\/UC7r66GT14hROB_RQsQBAQUQ\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.inovex.de\/de\/#\/schema\/person\/98fff9f09e7abd0bca8f6dd8c3cbaa02\",\"name\":\"Sebastian Me\u00dfingfeld\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\/\/www.inovex.de\/de\/#\/schema\/person\/image\/e27735ac8b505ec9595a8fd52f8fc959\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/bb75713104b82a7c2057688c4099e94bb8bb332d973101bd4ff36ef89146824e?s=96&d=retro&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/bb75713104b82a7c2057688c4099e94bb8bb332d973101bd4ff36ef89146824e?s=96&d=retro&r=g\",\"caption\":\"Sebastian Me\u00dfingfeld\"},\"url\":\"https:\/\/www.inovex.de\/de\/blog\/author\/smessingfeld\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Continuous Delivery von iOS Apps mit GitLab CI und fastlane - inovex GmbH","description":"Am Beispiel unserer Entwicklungsumgebung bei inovex mit GitLab als Webfrontend f\u00fcr die Repositoryverwaltung, GitLab CI als Continuous Integration Umgebung, sowie fastlane als Build-Tool zeige ich den von uns eingesetzten Workflow zur Continuous Delivery von iOS Apps.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/","og_locale":"de_DE","og_type":"article","og_title":"Continuous Delivery von iOS Apps mit GitLab CI und fastlane - inovex GmbH","og_description":"Am Beispiel unserer Entwicklungsumgebung bei inovex mit GitLab als Webfrontend f\u00fcr die Repositoryverwaltung, GitLab CI als Continuous Integration Umgebung, sowie fastlane als Build-Tool zeige ich den von uns eingesetzten Workflow zur Continuous Delivery von iOS Apps.","og_url":"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/","og_site_name":"inovex GmbH","article_publisher":"https:\/\/www.facebook.com\/inovexde","article_published_time":"2018-08-08T10:55:09+00:00","article_modified_time":"2024-01-09T06:31:27+00:00","og_image":[{"width":1920,"height":1080,"url":"https:\/\/www.inovex.de\/wp-content\/uploads\/2018\/08\/Continuous-Delivery-von-iOS-Apps.png","type":"image\/png"}],"author":"Sebastian Me\u00dfingfeld","twitter_card":"summary_large_image","twitter_image":"https:\/\/www.inovex.de\/wp-content\/uploads\/2018\/08\/Continuous-Delivery-von-iOS-Apps-1024x576.png","twitter_creator":"@inovexgmbh","twitter_site":"@inovexgmbh","twitter_misc":{"Verfasst von":"Sebastian Me\u00dfingfeld","Gesch\u00e4tzte Lesezeit":"15\u00a0Minuten","Written by":"Sebastian Me\u00dfingfeld"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#article","isPartOf":{"@id":"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/"},"author":{"name":"Sebastian Me\u00dfingfeld","@id":"https:\/\/www.inovex.de\/de\/#\/schema\/person\/98fff9f09e7abd0bca8f6dd8c3cbaa02"},"headline":"Continuous Delivery von iOS Apps mit GitLab CI und fastlane","datePublished":"2018-08-08T10:55:09+00:00","dateModified":"2024-01-09T06:31:27+00:00","mainEntityOfPage":{"@id":"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/"},"wordCount":2800,"commentCount":4,"publisher":{"@id":"https:\/\/www.inovex.de\/de\/#organization"},"image":{"@id":"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#primaryimage"},"thumbnailUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/2018\/08\/Continuous-Delivery-von-iOS-Apps.png","keywords":["Apps"],"articleSection":["Applications","General"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/","url":"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/","name":"Continuous Delivery von iOS Apps mit GitLab CI und fastlane - inovex GmbH","isPartOf":{"@id":"https:\/\/www.inovex.de\/de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#primaryimage"},"image":{"@id":"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#primaryimage"},"thumbnailUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/2018\/08\/Continuous-Delivery-von-iOS-Apps.png","datePublished":"2018-08-08T10:55:09+00:00","dateModified":"2024-01-09T06:31:27+00:00","description":"Am Beispiel unserer Entwicklungsumgebung bei inovex mit GitLab als Webfrontend f\u00fcr die Repositoryverwaltung, GitLab CI als Continuous Integration Umgebung, sowie fastlane als Build-Tool zeige ich den von uns eingesetzten Workflow zur Continuous Delivery von iOS Apps.","breadcrumb":{"@id":"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#primaryimage","url":"https:\/\/www.inovex.de\/wp-content\/uploads\/2018\/08\/Continuous-Delivery-von-iOS-Apps.png","contentUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/2018\/08\/Continuous-Delivery-von-iOS-Apps.png","width":1920,"height":1080,"caption":"Continuous Delivery von iOS Apps visualisiert durch eine Pipeline"},{"@type":"BreadcrumbList","@id":"https:\/\/www.inovex.de\/de\/blog\/continuous-delivery-ios-apps-gitlab-ci-fastlane\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.inovex.de\/de\/"},{"@type":"ListItem","position":2,"name":"Continuous Delivery von iOS Apps mit GitLab CI und fastlane"}]},{"@type":"WebSite","@id":"https:\/\/www.inovex.de\/de\/#website","url":"https:\/\/www.inovex.de\/de\/","name":"inovex GmbH","description":"","publisher":{"@id":"https:\/\/www.inovex.de\/de\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.inovex.de\/de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Organization","@id":"https:\/\/www.inovex.de\/de\/#organization","name":"inovex GmbH","url":"https:\/\/www.inovex.de\/de\/","logo":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/www.inovex.de\/de\/#\/schema\/logo\/image\/","url":"https:\/\/www.inovex.de\/wp-content\/uploads\/2021\/03\/inovex-logo-16-9-1.png","contentUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/2021\/03\/inovex-logo-16-9-1.png","width":1921,"height":1081,"caption":"inovex GmbH"},"image":{"@id":"https:\/\/www.inovex.de\/de\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/inovexde","https:\/\/x.com\/inovexgmbh","https:\/\/www.instagram.com\/inovexlife\/","https:\/\/www.linkedin.com\/company\/inovex","https:\/\/www.youtube.com\/channel\/UC7r66GT14hROB_RQsQBAQUQ"]},{"@type":"Person","@id":"https:\/\/www.inovex.de\/de\/#\/schema\/person\/98fff9f09e7abd0bca8f6dd8c3cbaa02","name":"Sebastian Me\u00dfingfeld","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/www.inovex.de\/de\/#\/schema\/person\/image\/e27735ac8b505ec9595a8fd52f8fc959","url":"https:\/\/secure.gravatar.com\/avatar\/bb75713104b82a7c2057688c4099e94bb8bb332d973101bd4ff36ef89146824e?s=96&d=retro&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/bb75713104b82a7c2057688c4099e94bb8bb332d973101bd4ff36ef89146824e?s=96&d=retro&r=g","caption":"Sebastian Me\u00dfingfeld"},"url":"https:\/\/www.inovex.de\/de\/blog\/author\/smessingfeld\/"}]}},"_links":{"self":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts\/21084","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/users\/68"}],"replies":[{"embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/comments?post=21084"}],"version-history":[{"count":2,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts\/21084\/revisions"}],"predecessor-version":[{"id":50785,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts\/21084\/revisions\/50785"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/media\/13636"}],"wp:attachment":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/media?parent=21084"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/tags?post=21084"},{"taxonomy":"service","embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/service?post=21084"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/coauthors?post=21084"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}