Kostentreiber bei der iOS-Entwicklung

Out of Cash!
Kommentare

Die Entwicklung einer App für ein iPhone oder iPad ist im Grunde ganz einfach: Man stellt die benötigten grafischen Elemente im Interface Builder per Drag and Drop zusammen, programmiert die gewünschte Funktionalität dazu und bindet im Idealfall bereits bestehende Umsysteme oder Datenquellen an, fertig ist der nächste Verkaufsschlager für den App Store. Leider müssen viele Projektteams die Erfahrung machen, dass es so einfach eben doch nicht geht, und am Ende der Entwicklung Aufwand und Kosten weit höher sind als ursprünglich geplant.

Anhand eines fiktiven Beispiels zeigen wir, welche Faktoren die Entwicklung verkomplizieren und somit verteuern können. Wir identifizieren verschiedene Kostentreiber und beschreiben, wie man diese umgehen kann und so von ungeplanten Kosten bei der iOS-Entwicklung verschont bleibt.

Eine App für EurAir

Die renommierte Fluggesellschaft EurAir will das bestehende Angebot ausbauen und ihren Kunden eine iPhone-App zur Verfügung stellen. Diese soll das Suchen sowie Buchen und Verwalten von Flügen direkt auf dem iPhone möglich machen. Für die Umsetzung der App wurde aufgrund von mangelndem, internem Know-how ein externer Partner gesucht. Erst letzte Woche haben wir nun den definitiven Zuschlag bekommen, zu zweit sollen wir in den nächsten Wochen die mobile Lösung entwickeln. Da die App noch vor den lukrativen Sommermonaten im App Store verfügbar sein soll, wurde vereinbart, mit der Umsetzung so rasch als möglich zu beginnen.
Der Scope und die Rahmenbedingungen des Projekts wurden wie folgt vereinbart: Die EurAir betreibt bereits einen Verkauf ihrer Flüge über ein Webportal. Kunden können sich dort ein Profil anlegen und damit Flüge buchen, bereits gebuchte Flüge anschauen oder gegebenenfalls umbuchen. Diese Funktionalität soll analog in einer App umgesetzt werden. Es wurde uns im Vorfeld versichert, dass alle benötigten Schnittstellen bereits vorhanden und vorbereitet sind, da sie vom Webportal seit einiger Zeit verwendet werden. Außerdem müssen wir uns nicht um Fragen betreffend Usability und die Definition der User Interfaces kümmern, da EurAir dafür eine eigene Abteilung unterhält. Ausgenommen ist auch das Testing der App, dafür ist ein Standardprozess sowie geschultes Personal verfügbar. Somit umfasst das Projekt die Umsetzung der gewünschten User Interfaces, die Implementierung der Funktionalität des Buchens, Verwaltens und Umbuchens von Flügen analog dem Webportal sowie die Anbindung an die bestehenden Serverschnittstellen der EurAir.

Interaktion und Design

Die Designabteilung hat große Vorarbeit geleistet, und so stehen sowohl Wireframes wie auch Visual Designs für die komplette App bereits an unserem ersten Arbeitstag zur Verfügung.
Das in den Wireframes angedachte Interaktionskonzept der App ist ungewöhnlich für eine iOS-Lösung. Über ein Drop-down-Menü lassen sich verschiedene Funktionalitäten wie beispielsweise das Buchen von Flügen, das Verwalten der Buchungen, das persönliche Profil oder News zu EurAir erreichen (Abb. 1). Das Menü lässt sich über einen Menü-Button in der rechten Ecke der UINavigationBar aufrufen und schiebt sich dann von oben nach unten über den aktuellen Inhalt.

Abb. 1: Drop-down-Menü

Die Visual Designs für die einzelnen Screens sind detailliert ausgearbeitet und enthalten genaue Vermessungen aller Elemente (Abb. 2). Laut einer Notiz sind die Vermessungen exakt einzuhalten, da den Benutzern so eine einheitliche und dem Corporate Design entsprechende visuelle Wahrnehmung geboten werden kann.

Abb. 2: Visual Designs

Wir beschließen, zuerst das Interaktionskonzept prototypisch umzusetzen und dann um die einzelnen Screens zu ergänzen. Erst später wollen wir die eigentliche Funktionalität hinzufügen. Schon bald finden wir uns jedoch in einer Diskussion über die Umsetzung des Interaktionskonzepts, insbesondere des Menüs wieder. Leider gibt es dazu keine Standardkomponente von Apple und so sind wir zu einer eigenen Implementierung gezwungen. Doch welchen UIViewController soll man dafür verwenden? Der UINavigationController ist wohl am naheliegendsten, doch müsste man ihn für unsere Verwendung zweckentfremden. Beim Anwählen eines Menüpunkts wäre man nämlich gezwungen, den kompletten Stack an UIViewControllern im UINavigationController auszuwechseln. Oder wollen wir doch lieber eine komplett eigene Implementierung eines UIViewControllers verwenden? Wie auch immer wir das Interaktionskonzept umsetzen, der Aufwand und die Kosten dafür werden höher als notwendig und geplant.

Auch mit den Visual Designs sind wir nicht besonders glücklich, insbesondere die Umsetzung der eingetragenen Vermessungen wird einiges an Aufwand mit sich bringen. Die Designer haben dabei nämlich keinerlei Rücksicht auf Einrückungen und Formatierungen der Standard-GUI-Elemente, wie zum Beispiel einer UITableViewCell genommen. Eine genaue Umsetzung der Visual Designs hätte somit zur Folge, dass diverse GUI-Elemente von uns überschrieben und angepasst werden müssten.
Da diese Lösung für uns jedoch nicht befriedigend ist und außerdem den Aufwand unnötig vergrößert, beschließen wir, uns mit den Designern abzusprechen, um allenfalls Anpassungen vornehmen zu dürfen. Doch leider stoßen wir nicht auf große Resonanz, das Designteam ist bereits an seinem nächsten Projekt und kann keine Ressourcen mehr für die Erstellung der App abstellen.

Wir beschließen, uns mit den identifizierten Problemen an den Projektleiter zu wenden. In einem Gespräch wollen wir ihn darum bitten, ein Standardinteraktionskonzept prototypisch umsetzen zu dürfen und später mit ihm und den Designern zu diskutieren. Außerdem wollen wir ihn darum ersuchen, zusätzliche Designressourcen zu bewilligen. Gerne würden wir mit einem Designer in agiler Vorgehensweise iterativ die erhaltenen Visual Designs optimieren. Durch das Aufzeigen der versteckten Aufwände und Kosten können wir den Projektleiter von unserem Vorhaben überzeugen. In wenigen Tagen erstellen wir den versprochenen Prototypen des Interaktionskonzepts (Abb. 3).

Abb. 3: Prototyp des Interaktionskonzepts

Unter Verwendung des UITabBarControllers haben wir mit vergleichsweise wenig Aufwand eine Lösung bereit, die sowohl die Projektleitung als auch die Designer zu überzeugen vermag und uns mehrere Tage Entwicklungsaufwand erspart. Die einzelnen Screens realisieren wir Stück für Stück, wo möglich gemäß den Visual Designs, wo nötig mit kleinen Anpassungen, um unnötige Aufwände zu vermeiden. Regelmäßige Reviews mit den Designern und der Projektleitung stellen sicher, dass unsere Umsetzung den Vorstellungen entspricht und wir zusätzliche Aufwände nur dort investieren, wo es ausdrücklich gewünscht ist.

Aufmacherbild: Square shiny icon with white design on bricks wall background von Shutterstock / Urheberrecht: Valentin Tabirca

[ header = Technische Aspekte ]

Technische Aspekte

Nach einigen Tagen Entwicklung ist eine erste Version des visuellen Grundgerüsts der App implementiert. Jegliche Funktionalität fehlt aber noch komplett und alle dargestellten Daten und Informationen sind statisch. So beginnen wir mit dem zweiten großen Arbeitspaket, der Anbindung der Umsysteme. (Die komplette Trennung von Implementierung der visuellen Komponenten und Anbindung der Umsysteme ist in der Praxis wahrscheinlich nicht geeignet und dient hier der Strukturierung des Artikels.)
Ein Backend-Entwickler der EurAir soll uns dabei helfen. Er kennt die bestehenden Schnittstellen und kann uns allfällige Fragen dazu beantworten. Während einer ersten Einführung wird uns klar, dass die bestehenden Schnittstellen auf die heutige Weblösung zugeschnitten sind.
Als wir zusammen mit dem Backend-Entwickler versuchen, die verfügbaren Schnittstellen auf die Use Cases der App abzubilden, stellen wir fest, dass es eine relativ große Diskrepanz gibt. Wir sind gezwungen, in der App eine Zwischenschicht einzubauen, um die Serverschnittstelle verwenden zu können. Die Implementierung dieser zusätzlichen Logik generiert Aufwand, der nicht vorgesehen und abgeschätzt war.

Der Backend-Entwickler ist nach kurzer Diskussion mit uns einverstanden, dass die Anpassung nicht in der App, sondern serverseitig geschehen soll. Er beantragt dies bei der Projektleitung. Kurze Zeit später wird für die Anpassung grünes Licht gegeben, die Schnittstellen werden in den kommenden Wochen zu feingranularen REST-Services umgebaut, die von beliebigen Clients benutzt werden können.
Da die Bereitstellung der neuen Schnittstellen einige Tage in Anspruch nehmen wird, beginnen wir mit der Implementierung des Log-ins und der Authentifizierung. Die Kunden der EurAir sollen in der Lage sein, sich mit ihrem Profil anzumelden, damit sie Buchungen zu einem späteren Zeitpunkt anschauen und gegebenenfalls anpassen können. Auch hier sollen wir die bereits bestehende Infrastruktur anbinden. Die Weblösung implementiert die Authentifizierung gegenüber dem Server mittels Cookie. Ein Log-in-Aufruf mit Benutzername und Passwort liefert ein Session-Cookie zurück, das für 30 Minuten gültig ist und bei allen weiteren Serveraufrufen mitgeschickt werden soll. Bleibt die Webseite länger als die definierten 30 Minuten inaktiv und versucht danach Daten abzufragen oder an den Server zu schicken, erscheint eine Meldung, dass die Session abgelaufen ist und der Benutzer wird automatisch zum Log-in umgeleitet.

Diese Lösung ist für eine iOS-App jedoch ungeeignet. Einem Benutzer nach 30 Minuten Inaktivität ein erneutes Log-in anzuzeigen, ist aus Sicht der Benutzerfreundlichkeit einer App inakzeptabel. Ist man trotzdem gezwungen, die Authentifizierung bei Serveraufrufen mittels Cookie zu implementieren, sollte zumindest das Ablaufen des Cookies und der danach nötige Log-in vor dem Benutzer verborgen werden. Wir erreichen das durch ein Abfangen von Serverantworten mit dem HTTP-Status-Code 401. Diesen verwendet der Server, um eine fehlgeschlagene Authentifizierung beim Abfragen oder Senden von Daten zu melden. Erhalten wir in der App auf eine Anfrage eine solche Antwort, so führen wir vom Benutzer unbemerkt im Hintergrund mit zu Beginn abgespeichertem Benutzername und Passwort ein erneutes Log-in durch. Damit bekommen wir ein neues Session-Cookie und führen damit die ursprüngliche Anfrage an den Server erneut durch (Abb. 4).

Abb. 4: Anfrage an den Server via Session-Cookie

Diese Lösung ist jedoch höchstens temporär akzeptabel, bis eine passendere Möglichkeit zur Authentifizierung bei Serveraufrufen verfügbar ist. Negativ auswirken können sich dabei die zusätzlich benötigten Serveraufrufe. Um eine einfache Datenabfrage zu machen, benötigen wir nun drei Aufrufe, wenn das Cookie abgelaufen ist (ursprüngliche Abfrage, Log-in im Hintergrund und erneut die ursprüngliche Abfrage). Im Falle einer schlechten Datenverbindung kann das zu unnötig langen Warte- und Ladezeiten führen.
Die Benutzung des Session-Cookies als Authentifizierungsmittel ist für eine iOS-App nicht empfehlenswert. Sie generiert zusätzlichen Aufwand bei der Implementierung, da der Aspekt des abgelaufenen Cookies dem Benutzer verborgen bleiben soll. Ausserdem führt sie dazu, dass bei einer schlechten Verbindung die App als langsam wahrgenommen wird, was die Gefahr der Ablehnung der App vergrössert.

Die von uns umgesetzte Adaption der Authentifizierung des Session-Cookies ist zwar nicht optimal und technisch hässlich, wird aber von der Projektleitung für eine erste Version akzeptiert. Auf unser Drängen hin wird aber bereits ein Change Request für die nächste Version der App verfasst. Dieser sieht eine Authentifizierung mittels nicht ablaufender Token vor und macht dadurch das in einer App unnötige Behandeln von Sessions überflüssig.

[ header = Rückwärtskompatibilität, Testing, Wartung ]

Rückwärtskompatibilität

Die App soll den Kunden der EurAir die Möglichkeit bieten, attraktive Angebote mittels einer Sharing-Funktionalität weiter zu empfehlen. Die EurAir verfolgt bei ihrem Onlineauftritt die Richtlinie, alle Benutzer mit jeglicher Hard- und Software möglichst optimal zu bedienen. Dieser Grundsatz gilt auch für uns und hat zur Folge, dass die App auf iOS-Betriebssystemen bis zurück zu Version 4.0 einwandfrei laufen und alle Funktionen anbieten muss. Insbesondere bei der Sharing-Funktionalität bringt das einen großen zusätzlichen Aufwand mit sich. Die systemweite Integration von Facebook (erst ab iOS 6 verfügbar) und Twitter (ab iOS 5) kann somit nicht oder nur teilweise genutzt werden, dasselbe gilt auch für die bekannte Sharing-Funktion, die den UIActivityViewController (ab iOS 6) voraussetzt. Uns bleibt die Entscheidung, entweder die neuen Frameworks dort zu verwenden, wo sie verfügbar sind, und für ältere iOS-Versionen einen Fallback zu programmieren oder als Alternative gleich vollständig auf die Frameworks zu verzichten. So oder so entsteht großer zusätzlicher Aufwand für die alternative Implementierung. Alleine im Falle von Twitter werden je nach Implementierung drei externe Bibliotheken benötigt, die App muss bei Twitter registriert werden und das User Interface muss komplett selbst gebaut werden. Eine Integration von Facebook ist nicht minder komplex, außerdem müssen zusätzliche Kanäle wie Mail oder Message separat angebunden werden. Dem gegenüber steht die Sharing-Funktionalität unter iOS 6. Deren Umsetzung für das Teilen von Inhalten über alle auf dem Gerät verfügbaren und registrierten Kanälen benötigt gut ein Dutzend Codezeilen.

Eine Intervention bei der Projektleitung bringt in diesem Streitpunkt für uns jedoch keine Verbesserung. Die EurAir hält an ihrer Richtlinie fest. Immerhin gelingt es uns, die Projektleitung für das Thema zu sensibilisieren und unsere zusätzlichen Aufwände aufzuzeigen. Wir beschließen gemeinsam, eine Bibliothek zur Messung von Verwendungszahlen in die App zu integrieren. Anhand dieser Zahlen will die EurAir in Zukunft entscheiden können, ob sich die Unterstützung von älteren iOS-Versionen noch lohnt. Grundsätzlich lässt sich sagen, dass sich neue iOS-Versionen extrem schnell durchsetzen. Einen Monat nach dem Release war iOS 6 bereits auf über 60 Prozent der Geräte der Benutzer installiert, die bestimmte populäre Webseiten in Nordamerika besucht haben [1]. Ein manuelles Nachbauen eines neuen API für alte iOS-Versionen lohnt sich nur in ganz bestimmten Fällen, zum Beispiel wenn die Funktionalität für den Anbieter absolut zentral und unumgänglich ist. Wo immer möglich halten wir uns bezüglich Rückwärtskompatibilität an Matt Gemmell: Er empfiehlt nur die jeweils letzte Version von iOS zu unterstützen [2].

Testing

Einige Wochen später sind wir mit der Entwicklung der App bis auf wenige Details fertig. Wie zu Beginn vereinbart, wird nun parallel zur Fertigstellung die Testing-Abteilung involviert, um die Implementierung auf Herz und Nieren zu prüfen. Da jedoch die REST-Schnittstellen noch nicht komplett fertig und die Backend-Server aufgrund der laufenden Anpassungen temporär schlecht verfügbar sind, ist es den Testern beinahe unmöglich, das vorbereitete Testhandbuch abzuarbeiten. Regelmäßig erhalten wir Tickets aus dem Bug-Tracking-System, die keine Probleme in der App, sondern ausgefallene Server betreffen. Dadurch wird das Testing in die Länge gezogen und unsere Entwicklung gebremst.

Nach einigen Tagen erhalten wir Rückmeldung über einen Bug, der den Grundablauf der App betrifft. Beim Zeichnen der Wireframes wurde ein Spezialfall ausgelassen, der nun bei der Entwicklung nicht berücksichtigt wurde. Die Nachentwicklung dieses Spezialfalls hat größere Umstellungen zur Folge, ein gesamter Use Case muss von Grund auf neu implementiert werden.

Um solche Probleme in Zukunft früher erkennen zu können, vereinbaren wir mit der Projektleitung und dem Testing-Team, bei zukünftigen Entwicklungen den Prozess anzupassen. Parallel zum nächtlichen Build aller Umsysteme soll auch die App auf der Continuous-Integration-(CI) Infrastruktur gebaut werden. Tools wie beispielsweise Testflight [3] ermöglichen das automatisierte und von der CI initiierte Versenden der App an ausgewählte Tester. Somit ist es möglich, dass fortlaufend der neueste Stand der App getestet wird und Probleme so früh als möglich detektiert werden.
Wenige Tage vor dem Einreichen der App bei Apple zeigt uns der Projektleiter in einem Meeting die eingefrorene App. Er hat sie auf dem Arbeitsweg im Zug ausprobiert und in einem Tunnel plötzlich seltsames Verhalten entdeckt. Wenig später ist die App komplett eingefroren. Die Analyse des Problems bringt rasch die Ursache ans Licht: Ein kleiner Fehler bei der Behandlung von verlorener Netzverbindung hat die App in einen ungewünschten Zustand versetzt, der dann in einem kompletten Einfrieren des Bildschirms gegipfelt hat. Überrascht stellen wir fest, dass trotz beinahe abgeschlossener Testphase niemand diesen grundlegenden Fehler gefunden hat.

Wartung

Trotz dem nicht optimalen Testverfahren konnte die Testing- und Bugfixing-Phase nach kurzer Zeit erfolgreich abgeschlossen werden. Auch die neuen Schnittstellen konnten wir problemlos integrieren und die fertige App wenige Wochen vor dem Start der für EurAir wichtigen Saison bei Apple zum Review einreichen.
Beim Abschlussmeeting zeigt sich die Projektleitung froh, dass die App jetzt fertig entwickelt ist und kein weiterer Aufwand mehr auf die EurAir zukommt. Doch da widersprechen wir vehement: Wird eine iOS-App nicht gewartet, verliert sie schnell an Attraktivität. Bereits nach einem Jahr wirkt eine nicht gewartete App veraltet und nach zwei Jahren erinnert sie mehr an das verstaubte Hochzeitsfoto von Onkel Gerhard als an eine dem aktuellen Stand der Technik entsprechende App. Anhand von Beispielen versuchen wir deshalb aufzuzeigen, wie rasch sich die iOS-Plattform weiterentwickelt, und argumentieren, dass eine App diese Entwicklung mitmachen muss. Neue Frameworks und visuelle Elemente von neuen iOS-Versionen müssen evaluiert und falls passend integriert werden, ansonsten werden die Benutzer möglicherweise ein Konkurrenzprodukt vorziehen

Wir können die Projektleitung schlussendlich überzeugen, ein jährliches Wartungsbudget in der Höhe von 15 Prozent des Projektbudgets zu beantragen. Damit ist es möglich, die App aktuell und am Puls der Zeit zu halten.

Fazit

Viele iOS-Entwicklungen resultieren in höheren Kosten als ursprünglich geplant. Anhand des Beispiels der EurAir haben wir aufgezeigt, welche Faktoren dafür verantwortlich sein können. Viele aufgezeigte Kostentreiber hängen mit Besonderheiten der mobilen Plattform und im Speziellen mit iOS zusammen. Um zusätzliche Aufwände zu umgehen, ist es wichtig, dass auch Designer, Projektleitung und Tester von diesen Eigenheiten wissen, beziehungsweise sie sogar detailliert kennen. Noch besser ist es, wenn die Zusammenarbeit mit den iOS-Entwicklern eng gestaltet wird und im Idealfall auf agile Art stattfindet. Speziell wenn Designer, Projektleitung und Tester keine große Erfahrung mit iOS-Projekten haben oder wenig fundiertes Wissen über die Besonderheiten der Plattform mit sich bringen, kann das plattformspezifische und dringend benötigte Wissen der Entwickler auf diese Weise während der gesamten Dauer des Projekts eingesetzt werden.
Ein iOS-Projekt unterscheidet sich doch in einigen Bereichen von anderen Entwicklungsprojekten und kann nur erfolgreich und im Rahmen des Budgets durchgeführt werden, wenn diesen Unterschieden Rechnung getragen wird.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -