Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Geleitwort
Vorwort
1 Hello iPhone
2 Die Reise nach iOS
3 Sehen und anfassen
4 Alles unter Kontrolle
5 Daten, Tabellen und Controller
6 Models, Layer, Animationen
7 Programmieren, aber sicher
8 Datenserialisierung und Internetzugriff
9 Multimedia
10 Jahrmarkt der Nützlichkeiten
Stichwort

Jetzt Buch bestellen
Ihre Meinung?

Spacer
Apps programmieren für iPhone und iPad von Klaus M. Rodewig, Clemens Wagner
Das umfassende Handbuch
Buch: Apps programmieren für iPhone und iPad

Apps programmieren für iPhone und iPad
Rheinwerk Computing
1172 S., geb., mit DVD
49,90 Euro, ISBN 978-3-8362-2734-6
Pfeil 10 Jahrmarkt der Nützlichkeiten
Pfeil 10.1 Ein Rundgang durch Xcode
Pfeil 10.1.1 Die Navigatorspalte
Pfeil 10.1.2 Der Utilitybereich
Pfeil 10.1.3 Der Editor
Pfeil 10.1.4 Autovervollständigung und Code-Schnipsel
Pfeil 10.1.5 Tastaturkurzbefehle
Pfeil 10.1.6 Tabs und Fenster
Pfeil 10.1.7 Simulatoren und Dokumentation nachladen
Pfeil 10.1.8 No Country for Old Man-Pages
Pfeil 10.1.9 Projekte dokumentieren
Pfeil 10.1.10 Snapshots
Pfeil 10.2 Refactoring
Pfeil 10.2.1 Refactorings in Xcode
Pfeil 10.2.2 Methoden auslagern
Pfeil 10.2.3 Oberklassen erzeugen und Methoden verschieben
Pfeil 10.3 Der Organizer
Pfeil 10.3.1 Die Geräteverwaltung
Pfeil 10.3.2 Archive und die Archivverwaltung
Pfeil 10.3.3 Projektverwaltung
Pfeil 10.4 Das Buildsystem
Pfeil 10.4.1 Workspaces, Projekte und Targets
Pfeil 10.4.2 Klassen in Bibliotheken auslagern
Pfeil 10.4.3 Bibliotheken wiederverwenden
Pfeil 10.4.4 Konfigurationen
Pfeil 10.4.5 Targets
Pfeil 10.4.6 Schemata
Pfeil 10.5 Ad-hoc-Distributionen
Pfeil 10.5.1 Geräteregistrierung
Pfeil 10.5.2 App-Installation über das iPhone-Konfigurationsprogramm
Pfeil 10.5.3 Ad-hoc-Distributionen über einen Webserver
Pfeil 10.6 Versionsverwaltung mit Git
Pfeil 10.6.1 Lokale Git-Repositorys
Pfeil 10.6.2 Arbeiten mit verteilten Git-Repositorys
Pfeil 10.6.3 Git-Integration in Xcode
Pfeil 10.6.4 Einbindung existierender Git-Repositorys
Pfeil 10.6.5 GitX
Pfeil 10.6.6 SourceTree
Pfeil 10.6.7 Das Kommandozeilenprogramm von Git
Pfeil 10.6.8 Der eigene Git-Server

Rheinwerk Computing - Zum Seitenanfang

10.4Das BuildsystemZur nächsten Überschrift

Das Buildsystem sorgt dafür, dass aus den Quelldateien Ihrer Projekte die gewünschten Produkte entstehen. Die häufigsten Produkte sind natürlich Programme; Xcode kann indes auch Programmbibliotheken, Testrunner oder beliebige andere Dateien und Verzeichnisse aus den Quelldateien der Projekte erzeugen.


Rheinwerk Computing - Zum Seitenanfang

10.4.1Workspaces, Projekte und TargetsZur nächsten ÜberschriftZur vorigen Überschrift

Ein Projekt enthält alle notwendigen Daten, um Produkte zu erstellen. Da sind zum einen natürlich die Dateien mit dem Quellcode, Storyboards oder XIB-Dateien, Bilder, Sounds usw. Zum anderen enthält das Projekt Targets. Dabei fasst ein Target die Erzeugungsregeln und Einstellungen für jeweils ein Produkt zusammen. Über die Konfigurationen kann das Projekt dabei Einstellungen für die enthaltenen Targets vorgeben, die jedes Target allerdings noch überschreiben kann. Außerdem enthält das Projekt Schemata, über die Sie Targets und weitere Aktionen ausführen lassen können. Schließlich können Sie mehrere Projekte in einem Workspace zusammenfassen. Abbildung 10.43 zeigt eine Übersicht über die gesamte Struktur.

In der Regel fassen Sie in einem Workspace voneinander abhängige Projekte zusammen. Ein typisches Beispiel dafür ist ein Projekt für ein Programm und ein oder mehrere Projekte für die benutzten Bibliotheken. Zwischen den Targets der verschiedenen Projekte können Sie Abhängigkeiten definieren. So darf Xcode beispielsweise erst das Programm erzeugen, wenn es alle Bibliotheken erstellt hat. Die Projekte in einem Workspace verwenden außerdem den gleichen Symbolindex. Dieser erlaubt Ihnen bei abhängigen Projekten auch ein Debuggen in den Quellen der verschiedenen Projekte.

Abbildung

Abbildung 10.43 Die Komponenten des Buildsystems


Rheinwerk Computing - Zum Seitenanfang

10.4.2Klassen in Bibliotheken auslagernZur nächsten ÜberschriftZur vorigen Überschrift

Um den Umgang mit Workspaces zu verstehen, sollen Sie im Folgenden schrittweise das Projekt AlarmClock von der beiliegenden DVD in eine Bibliothek und eine Applikation aufteilen. Legen Sie dazu eine Kopie des Projekts auf Ihrer Festplatte an, und öffnen Sie diese Kopie. Führen Sie ruhig das Programm einmal im Simulator aus, um sich zu vergewissern, dass die Kopie vollständig ist.

Zunächst müssen Sie zu dem Projekt einen Workspace erzeugen, wozu Sie den Menüpunkt FileSave as Workspace... aufrufen. Speichern Sie den Workspace unter dem Namen ClockWorkspace in dem Ordner ab, der den Ordner des Projekts enthält. Nach dem Speichern hat sich in der Anzeige nicht viel verändert. Lediglich die Werkzeugleiste zeigt den Namen des Workspace mit einem Icon an (siehe Abbildung 10.44). Außerdem hat Xcode die Datei ClockWorkspace.xcworkspace angelegt.

Abbildung

Abbildung 10.44 Anzeige des Workspace in der Werkzeugleiste

Für die Bibliothek müssen Sie ein eigenes Projekt anlegen. Dazu rufen Sie das Menü FileNewProject... auf und wählen als Vorlage für die Bibliothek Cocoa Touch Static Library aus Framework & Library (siehe Abbildung 10.45).

Abbildung

Abbildung 10.45 Anlegen einer statischen Bibliothek

Das neue Projekt speichern Sie unter dem Namen ClockLibrary im gleichen Ordner wie die Workspace-Datei. Wählen Sie außerdem im Dateiauswahldialog unter Add To den Eintrag ClockWorkspace aus. Nach der Auswahl erscheint ein weiteres Auswahlmenü mit dem Namen Group, in dem Sie ebenfalls den Eintrag ClockWorkspace auswählen (siehe Abbildung 10.46).

Abbildung

Abbildung 10.46 Zuordnung des neuen Projekts

Damit die Bibliothek auch zu den anderen Projekten im Workspace passt, sollten Sie das Deployment-Target in den Projekteinstellungen der Bibliothek auf 5.0 stellen. Ansonsten kann es mit einer Bibliothek mit Deployment-Target 7.0 unter älteren iOS-Versionen zu Laufzeitfehlern kommen, die perfiderweise auch nur mit der Release-Konfiguration auftreten, d. h., Sie bekommen diese Fehler häufig erst in der Version der App zu sehen, die Sie in den App Store laden.

Statische und dynamische Bibliotheken

Eine Programmbibliothek fasst binäre Objektdateien mit Klassen, Methoden und Funktionen zu einer Datei zusammen. Sie können die Symbole (z. B. Methoden, Funktionen, globale Variablen) einer Bibliothek in beliebigen Programmen verwenden. Bei einer statischen Bibliothek verbindet der Linker die Symbole fest mit dem Programm. Es enthält dann die Symbole aus der Bibliothek. Im Gegensatz dazu lädt ein Programm die Symbole aus einer dynamischen Bibliothek erst zur Laufzeit, und die Bibliothek muss deshalb natürlich zur Laufzeit für das Programm verfügbar sein.

Zwar unterstützt Cocoa Touch sowohl statische als auch dynamische Bibliotheken. Sie können jedoch nur statische Bibliotheken erzeugen, da es keine Möglichkeit gibt, dynamische Bibliotheken auf Ihren oder gar fremden iOS-Geräten zu installieren, und Sie können keine Bibliotheken über den App Store oder die anderen Distributionswege verteilen.

Cocoa-Touch-Frameworks enthalten eine Bibliothek, die zugehörigen Headerdateien und gegebenenfalls weitere Dateien. In der Regel bindet die Applikation die enthaltene Bibliothek dynamisch. Es sind indes auch Frameworks mit statischen Bibliotheken möglich.

Nach dem Anlegen enthält der Workspace ein weiteres Projekt für die Bibliothek (siehe Abbildung 10.47).

Abbildung

Abbildung 10.47 Struktur des Projekts nach dem Anlegen des Subprojekts

Das Subprojekt enthält in der Gruppe ClockLibrary die Dateien ClockLibrary.h und ClockLibrary.m, die Sie nicht brauchen und deshalb löschen können. Stattdessen fügen Sie die Klasse ClockView und die Kategorie UIView+AlarmClock aus dem übergeordneten Projekt Clock zum Bibliotheksprojekt hinzu, indem Sie auf die Gruppe ClockLibrary rechtsklicken und die Dateien über den Punkt Add Files to "ClockLibrary"... des Kontextmenüs auswählen. Stellen Sie dabei über Copy items into destinations group folder (if needed) sicher, dass Xcode die Dateien auch kopiert (siehe Abbildung 10.48).

Abbildung

Abbildung 10.48 Kopieren der Dateien in das Bibliotheksprojekt

Danach können Sie diese Dateien aus dem Clock-Projekt löschen, indem Sie die Dateien auswählen und die Entf- oder Lösch-Taste drücken. Da Sie diese Dateien ja bereits kopiert haben, können Sie sie ruhig über den Button Move to Trash endgültig löschen. Damit auch wirklich keine Reste mehr von dem Clockview oder der Kategorie in dem Projekt AlarmClock übrig bleiben, rufen Sie den Menüpunkt ProductClean auf. Xcode entfernt dadurch alle Zwischendateien, die es bei älteren Erzeugungsvorgängen angelegt hat.

Wenn Sie jetzt das Projekt übersetzen, erhalten Sie zwei Fehlermeldungen:

Lexical or Preprocessor Issue
'UIView+AlarmClock.h' file not found

und

Lexical or Preprocessor Issue
'ClockView.h' file not found

Das liegt daran, dass das AlarmClock-Projekt noch nichts von dem Bibliotheksprojekt weiß. Sie fügen das Bibliotheksprojekt über den Plus-Button unter Linked Frameworks and Libraries in den Target-Einstellungen zu dem Projekt AlarmClock hinzu. Die Bibliothek finden Sie unter dem Namen libClockLibrary.a in der Gruppe Workspace (siehe Abbildung 10.49).

Abbildung

Abbildung 10.49 Bibliotheksprojekt hinzufügen

Außerdem muss das Bibliotheksprojekt die Headerdateien exportieren, die es anderen Projekten zur Verfügung stellen will. Dazu öffnen Sie in den Target-Einstellungen des Bibliotheksprojekts den Reiter Build Phases und drücken in der Rubrik Copy Files auf den Plus-Button. In dem darauf erscheinenden Dialog können Sie die beiden Dateien ClockView.h und UIView+AlarmClock.h auswählen und über den Button Add zu den Exportdateien hinzufügen.

Xcode quittiert allerdings neuerliche Übersetzungsversuche immer noch mit den gleichen schnöden Fehlern. Durch das Auslagern der Klassen in eine Bibliothek haben sich auch die Pfade geändert, die Sie für die Importanweisungen dieser Dateien verwenden müssen. Cocoa Touch verwendet für die Headerdateien der Frameworks immer den Namen des Frameworks als Ordner, wie Sie das bereits von den Standard-Frameworks kennen, wie beispielsweise UIKit/UIKit.h oder QuartzCore/QuartzCore.h.

Abbildung

Abbildung 10.50 Headerdateien für den Export festlegen

Ändern Sie also die Importanweisungen in #import <ClockLibrary/ClockView.h> beziehungsweise #import <ClockLibrary/UIView+AlarmClock.h> um. Sie können hierbei übrigens die Notation mit den spitzen Klammern verwenden, da der Compiler jetzt nur noch in den Systemverzeichnissen und nicht mehr im Projektverzeichnis nach diesen Headerdateien suchen muss.

Bei der Ausführung zeigt das Programm indes keine Uhren an. Stattdessen finden Sie in der Konsole folgende Nachricht:

Unknown class ClockView in Interface Builder file.

Das Programm findet die Klasse nicht; sie steht zwar beim Linken zur Verfügung, allerdings weiß der Linker nicht, dass sie auch gebraucht wird. Da er sich streng an den sechsten Artikel des Kölschen Grundgesetzes »Kenne mer nit, bruche mer nit, fott domet.« [Anm.: »Kennen wir nicht, brauchen wir nicht, fort damit.«] hält, schmeißt er die sorgsam importierte Klasse einfach weg. Glücklicherweise ist jedoch der Linker wie auch der Kölner ein an und für sich toleranter Zeitgenosse [Anm.: Zumindest in seiner Selbsteinschätzung – Westfalen sehen das häufig anders.] , der sich durch den Schalter -ObjC (beziehungsweise ein paar Kölsch) leicht umstimmen lässt.

Um dieses Flag an den Linker zu bringen, wählen Sie das AlarmClock-Projekt aus und öffnen den Reiter Build Settings in den Projekteinstellungen. Als Suchbegriff verwenden Sie diesmal »Linker« (siehe Abbildung 10.51).

Abbildung

Abbildung 10.51 Eingabe eines Linkerflags

In der Liste finden Sie nun die Einstellung Other Linker Flags. Wählen Sie diese Zeile aus, und klicken Sie einmal in deren rechten Bereich. Dadurch können Sie den Wert dieser Einstellung verändern. Geben Sie dort »-ObjC« ein, und beenden Sie die Eingabe durch Drücken der ¢-Taste. Wenn Sie nun die App erstellen und ausführen lassen, sehen Sie die Ziffernblätter auf dem Bildschirm.

Sie haben durch diese Schritte die Klasse ClockView in eine statische Bibliothek ausgelagert. Sie können das überprüfen, indem Sie im Organizer das Icon Projects auswählen. Dort finden Sie in der linken Spalte neben Ihren Projekten auch den eben erstellten Workspace. Wenn Sie ihn auswählen, zeigt Xcode Ihnen unter Derived Data den Pfad zu den Ausgabedateien an. Sie können den Pfeil neben dem Pfad anklicken, um das Verzeichnis im Finder zu öffnen (siehe Abbildung 10.52).

Abbildung

Abbildung 10.52 Öffnen des Ausgabeverzeichnisses

In diesem Ordner befinden sich die Unterorder Build/Products/Debug-iphonesimulator und/oder Build/Products/Debug-iphoneos, die jeweils drei Dateien und einen Ordner enthalten:

  1. Das Programm befindet sich in der Datei AlarmClock.app.
  2. Die Datei AlarmClock.app.dSYM enthält Symbole des Programms für den Debugger.
  3. Die dritte Datei, libClockLibrary.a, ist schließlich die statische Bibliothek. Unter UNIX ist es üblich, dass statische Bibliotheken das Präfix lib und die Dateiendung .a haben.
  4. Der Ordner include enthält die exportierten Headerdateien des Bibliotheksprojekts.

Rheinwerk Computing - Zum Seitenanfang

10.4.3Bibliotheken wiederverwendenZur nächsten ÜberschriftZur vorigen Überschrift

Jetzt haben Sie die Klasse erfolgreich in eine Bibliothek ausgelagert. Für ein einzelnes Projekt ist der dafür notwendige Aufwand natürlich wenig sinnvoll. Aus diesem Grund legen Sie jetzt ein weiteres Projekt an. Klicken Sie dazu mit der rechten Maustaste in den grauen Bereich der Navigationsspalte, und wählen Sie aus dem Kontextmenü den Punkt New Project... aus. Wählen Sie die Vorlage Single View Application, und als Projektnamen und Klassenpräfix verwenden Sie jeweils »SecondClock«. Das neue Projekt speichern Sie im gleichen Verzeichnis wie die Workspace-Datei und das andere Programmprojekt ab.

Im Storyboard legen Sie einen quadratischen View mit der Klasse ClockView an. Obwohl das neue Projekt noch nicht die Bibliothek importiert hat, bietet Ihnen der Identitätsinspektor bereits diese Klasse an, und auch beim Übersetzen erhalten Sie keine Fehlermeldung. Allerdings zeigt das Programm schon wieder keine Uhr an; stattdessen erscheint in der Konsole die bekannte Nachricht

Unknown class ClockView in Interface Builder file.

Analog zum Hauptprojekt AlarmClock fügen Sie die Bibliothek zum neuen Projekt hinzu (siehe Abbildung 10.49) und setzen in den Build Settings wieder das Linkerflag wie in Abbildung 10.51. Nach diesen Änderungen zeigt Ihnen auch die neue App das Ziffernblatt mit den Zeigern an, und wenn Sie ein entsprechendes Outlet für den Clockview anlegen und die Animation in der Methode viewDidAppear: starten, bewegen sich auch die Zeiger.

Zwar ist der Anblick einer stehenden Uhr zeitlos schön, jedoch der einer laufenden Uhr ist im Allgemeinen sinnvoller. Um sie zu starten, ziehen Sie, wie in Kapitel 3, »Sehen und anfassen«, beschrieben, eine Outlet-Verbindung vom View in die anonyme Kategorie des Viewcontrollers. Außerdem importieren Sie die Headerdatei ClockLibrary/ClockView.h in die Headerdatei des Controllers, wie Listing 10.11 es zeigt:

#import <UIKit/UIKit.h>
#import <ClockLibrary/ClockView.h>

@interface SecondClockViewController()

@property (weak, nonatomic) IBOutlet ClockView *clockView;

@end

Listing 10.11 Anonyme Kategorie des Viewcontrollers


Rheinwerk Computing - Zum Seitenanfang

10.4.4KonfigurationenZur nächsten ÜberschriftZur vorigen Überschrift

Häufig soll sich der Programmcode für die Entwicklung von dem Programmcode für den App Store unterscheiden. Beispielsweise soll der Compiler den Code für die Veröffentlichung optimieren. Für die Entwicklung ist hingegen ein unoptimierter Programmcode mit Debug-Informationen geeigneter, da der Compiler beim Optimieren die Reihenfolge von Programmanweisungen verändern kann.

Vielleicht ist Ihnen bei den Änderungen an den Projekteinstellungen aufgefallen, dass Sie jede Einstellung aufklappen können. Sie finden darin jeweils die zwei Unterpunkte, mit denen Sie die Einstellungen für die Entwicklung (Debug) und Veröffentlichung (Release) getrennt festlegen können. Die Namen der beiden Unterpunkte bezeichnen die Konfigurationen, die das Projekt enthält. Xcode erzeugt die beiden Konfigurationen Debug und Release automatisch, wenn Sie das Projekt anlegen.

Copy-King Karl

Sie können zu Ihrem Projekt weitere Konfigurationen hinzufügen, indem Sie die bestehenden kopieren. Dazu wählen Sie ein Applikationsprojekt aus und öffnen den Reiter Info. Unter der Rubrik Configurations finden Sie alle bestehenden Konfigurationen. Über den Plus-Button können Sie Ihr Projekt um weitere Konfigurationen erweitern. Das ist indes nur in seltenen Fällen notwendig, da in der Regel die zwei Standardkonfigurationen für ein Projekt ausreichen.

In der Regel haben beide Konfigurationen die gleichen Einstellungen, und es gibt nur wenige Unterschiede. Die Einstellung Optimization Level ist ein typisches Beispiel für unterschiedliche Werte für die Entwicklung und die Veröffentlichung (siehe Abbildung 10.53). Für die Entwicklung verwendet der Compiler die Optimierungsstufe 0 – also keine Codeoptimierung. Für die Veröffentlichung benutzt er hingegen die Stufe s, die einen möglichst kurzen Code erzeugt.

Abbildung

Abbildung 10.53 Einstellungen für die bedingte Übersetzung

Sie können das Verhalten Ihres Programms ebenfalls von der benutzten Konfiguration abhängig machen. Dazu verwenden Sie ein Präprozessor-Makro, über das Sie Teile des Codes ein- oder ausblenden. Ein typisches Beispiel sind Log-Meldungen, die nur die Entwicklungsversion ausgibt. Das realisieren Sie beispielsweise über das Makro DEBUG, das nur die Debug-Konfiguration setzt.

Öffnen Sie dazu die Build Settings des Projekts ClockLibrary, und suchen Sie nach der Einstellung Preprocessor Macros. Wenn Sie diese über das Dreieck aufklappen, sehen Sie, dass dort das Feld Debug den Wert DEBUG=1 enthält, und in die Headerdatei ClockView.h fügen Sie vor den Implementierungsblock die folgenden Zeilen ein:

#if DEBUG
#define DEBUG_LOG(MESSAGE, ...) NSLog(MESSAGE, __VA_ARGS__)
#else
#define DEBUG_LOG(MESSAGE, ...) /**/
#endif

Listing 10.12 Konfigurationsabhängige Makrodefinition

Das Makro DEBUG_LOG gibt also nur Meldungen über NSLog aus, wenn das Makro DEBUG einen Wert ungleich 0 hat. Die Klasse ClockView verwendet diese Log-Ausgabe am Ende der Methode drawRect:, um den Wert der Property time über die Anweisung DEBUG_LOG(@"time = %@", self.time); auszugeben.

Die Qual der Wahl

Wie Sie gesehen haben, bietet Xcode Ihnen eine Fülle von Einstellungen an. In den meisten Fällen brauchen Sie diese Werte jedoch nicht zu verändern, da Apple hier schon sinnvolle Werte voreingestellt hat. Wenn Sie eine Änderung vornehmen, sollten Sie allerdings genau wissen, was Sie tun.

Um die Bibliothek in beiden Apps verwenden zu können, mussten Sie in den Konfigurationen beider Projekte die Einstellungen anpassen. Bei einer Bibliothek mit mehreren Einstellungen, die Sie in viele Projekte einbinden möchten, kann das jedoch sehr lästig sein. Sie können als Basis für Ihre Projekte und Targets auch eigene Basiskonfigurationen festlegen. Über eine Basiskonfiguration lassen sich Einstellungen in den Projekt- und Target-Einstellungen vorbelegen.

Legen Sie dazu über den Menüpunkt FileNewNew File... eine neue Konfigurationsdatei an. Als Vorlage verwenden Sie Configuration Settings File, die Sie in der Rubrik Other unter iOS finden. Wählen Sie den Dateinamen ClockLibrary und als Gruppe ClockWorkspace aus (siehe Abbildung 10.54).

Abbildung

Abbildung 10.54 Anlegen der Konfigurationsdatei

Xcode legt die neue Datei in der obersten Ebene in der Navigationsspalte ab. Wenn Sie die Datei öffnen, sehen Sie, dass es sich um eine einfache Textdatei handelt. Schreiben Sie die folgende Zeile an das Ende dieser Datei:

OTHER_LDFLAGS = -ObjC

Listing 10.13 Konfigurationsdatei mit Basiseinstellungen

Das ist genau die Einstellung, die beide Applikationen brauchen, um die Bibliothek zu verwenden. Als Nächstes fügen Sie die Datei zu Ihrem AlarmClock-Projekt über einen Rechtsklick auf das Projekt und den Menüpunkt Add Files to »Clock«... hinzu. Schalten Sie dabei jedoch die Option Copy items to destination groups folder (if needed) und alle Zugehörigkeiten zu den Targets über die entsprechenden Checkboxen aus.

Die Kopieroption

Durch das Ausschalten der Kopieroption Copy items to destination groups folder (if needed) stellen Sie sicher, dass es die Konfigurationsdatei ClockLibrary.xcconfig nur einmal im gesamten Workspace gibt, und Sie brauchen die Konfigurationen nur in einer Datei zu verwalten. Alle Projekte, die diese Datei referenzieren, übernehmen dann die Einstellungen automatisch.

Als Nächstes legen Sie diese Konfigurationsdatei als Basis des AlarmClock-Projekts fest. Dazu wählen Sie im Projekt den Reiter Info aus und klappen über die Dreiecke die beiden Konfigurationen Debug und Release in der Rubrik Configurations auf. Durch Anklicken der Doppelpfeile in der Spalte Based on Configuration File können Sie nun die neu angelegte Konfigurationsdatei als Basis auswählen (siehe Abbildung 10.55).

Abbildung

Abbildung 10.55 Festlegen der Basiskonfiguration

Öffnen Sie nun den Reiter Build Settings, und suchen Sie die Einstellung »Other Linker Flags« über das Suchfeld. Xcode stellt die Zeile in fetter Schrift dar, weil Sie den Standardwert überschrieben haben. Die Einstellung hat immer noch den Wert -ObjC. Wählen Sie die Zeile aus, ohne die Texteingabe zu aktivieren, und drücken Sie die ___æ-Taste. Dadurch entfernen Sie den Wert. Xcode zeigt die Zeile nun in normaler Schrift an, die jedoch immer noch den Wert -ObjC hat, da das Projekt jetzt die Einstellung aus der neuen Konfigurationsdatei verwendet.

Sie können das ganz einfach überprüfen, indem Sie die Schritte vertauschen. Löschen Sie also im SecondClock-Projekt zuerst die beiden Einstellungswerte aus den Target-Einstellungen. Beide Felder enthalten dadurch keine Werte mehr. Danach fügen Sie die Konfigurationsdatei zu diesem Projekt hinzu und wählen schließlich die Konfigurationsdatei als Basis für das Projekt. Auch hier sollten Sie beim Hinzufügen die Kopieroption ausschalten. Jetzt zeigen die Linkerflags den Wert -ObjC in normaler Schrift an.

Einstellungsnamen und -werte ermitteln

In der Konfigurationsdatei müssen Sie festgelegte Namen für die Einstellungen verwenden. Sie können sich diese Namen über den Menüpunkt EditorShow Setting Names anzeigen lassen, wenn Sie die Build Settings eines Projekts oder eines Targets geöffnet haben. Über den Menüpunkt EditorShow Setting Titles können Sie wieder auf die Standardansicht zurückschalten.

Viele Werte legt Xcode über Variablen fest. Sie sehen stattdessen in der Regel jedoch die endgültigen Werte, bei denen Xcode die Variablen durch ihre Werte ersetzt. Die Definitionen lassen sich über den Menüpunkt EditorShow Definitions anzeigen, und über EditorShow Values wechseln Sie wieder in die ursprüngliche Darstellung zurück.

Sie können übrigens im Suchfeld sowohl Titel als auch Namen oder Definitionen als Werte verwenden. Xcode zeigt Ihnen immer die passenden Zeilen an.

Beim Einbinden der Konfigurationsdatei in die Projekte sollten Sie die Datei nicht kopieren. Dadurch verwenden beide Projekte die gleiche Konfigurationsdatei als Basis. Wenn Sie sie verändern, verändern Sie die Einstellungen in beiden Projekten.

Es ist beispielsweise eine gute Idee, die Compiler-Warnungen wie Fehler zu behandeln. Normalerweise können Sie ein Projekt mit Warnungen übersetzen und ausführen. Das können Sie jedoch durch einen Schalter unterbinden. Dazu fügen Sie die Zeile

GCC_TREAT_WARNINGS_AS_ERRORS = YES

in die Konfigurationsdatei ein. Dadurch erzeugt der Compiler anstatt Warnungen Fehler. Nach dieser Änderung zeigt Xcode diese Einstellung auch in den beiden Projekten an.


Rheinwerk Computing - Zum Seitenanfang

10.4.5TargetsZur nächsten ÜberschriftZur vorigen Überschrift

In Abschnitt 4.4.2, »Eine Projektvariante erstellen«, haben Sie bereits ein neues Target zu einem Projekt hinzugefügt, um eine Variante Ihrer Applikationen zu erzeugen. Programmvarianten kommen häufig in folgenden Fällen zum Einsatz:

  • bei Lite- und Pro-Versionen
  • bei getrennten Apps für iPad und iPhone mit ähnlicher Codebasis
  • bei speziellen Versionen für den Auftraggeber mit erweitertem Funktionsumfang gegenüber der Endnutzerversion
  • bei einer Anpassung der App an verschiedene Partnerfirmen (»Cobranding«) oder Marken (»White-Label-Apps«). Die gleiche App wird mit unterschiedlichem Aussehen und unterschiedlichen Namen vertrieben.

Im letzten Fall besitzen die Varianten also lediglich andere Benutzerschnittstellen, haben jedoch im Wesentlichen die gleiche Funktionalität. Die App im Beispielprojekt hat einen grauen Hintergrund. Eine einfache Variante soll nun einen orangen Hintergrund bekommen. Dabei geht es natürlich nicht darum, wie Sie die Farbe ändern können, sondern darum, wie Sie die Varianten über unterschiedliche Targets verwalten.

Im Gegensatz zum vierten Kapitel legen Sie diesmal das neue Target nicht über eine Kopie, sondern von Grund auf neu an. Dazu klicken Sie auf den Button Add Target... in der Spalte Project, die Sie über das Projekt SecondClock finden (siehe Abbildung 10.56).

Abbildung

Abbildung 10.56 Anlegen eines neuen Targets

Wählen Sie als Vorlage Single View Application und als Namen sowie Klassenpräfix »OrangeClock« aus. Xcode legt darauf zwei neue Targets mit den Namen OrangeClock und OrangeClockTests an.

Von dem OrangeClock-Target benötigen Sie jedoch nur das Storyboard, die Dateien OrangeClock-Info.plist und InfoPlist.strings sowie die Image-Assets. Löschen Sie also alle Header- und Implementierungsdateien und die Datei OrangeClock-Prefix.pch endgültig aus der Gruppe OrangeClock. Diese Dateien ersetzen Sie jeweils durch die entsprechende Datei im Target SecondClock. Dazu selektieren Sie alle Implementierungsdateien des Targets, öffnen den Dateiinspektor und aktivieren unter Target Membership die Option OrangeClock (siehe Abbildung 10.57).

Abbildung

Abbildung 10.57 Dateien einem Target zuordnen

Die Datei für die vorkompilierten Headerdateien ersetzen Sie über die Target-Einstellungen, indem Sie in den Build Settings nach »Prefix« suchen und den Wert für Prefix Header wie in Abbildung 10.58 in »SecondClock/SecondClock-Prefix.pch« ändern.

Abbildung

Abbildung 10.58 Datei für vorübersetzte Header anpassen

Das neue Storyboard ändern Sie folgendermaßen ab:

  1. Dem Viewcontroller weisen Sie die Klasse SecondClockViewController zu.
  2. Die Hintergrundfarbe des Hauptviews setzen Sie auf Orange.
  3. In den Hauptview fügen Sie einen quadratischen View mit der Klasse ClockView und Clear Color als Hintergrundfarbe ein.
  4. Verbinden Sie das Outlet clockView des Viewcontrollers mit dem Clockview.

Außerdem müssen Sie die Bibliothek libClockLibrary.a zu dem neuen Target hinzufügen. Dazu öffnen Sie den Reiter General in den Target-Einstellungen, drücken den Plus-Button in der Rubrik Linked Frameworks and Libraries und wählen die Bibliothek aus der Liste aus (analog zu Abbildung 10.49).

Sie können nun das neue Target ausführen, indem Sie im Popup-Button in der Toolbar den Eintrag OrangeClock auswählen. Nach dem Start zeigt Ihnen die App eine laufende Uhr vor orangenem Hintergrund an, genauso wie es die SecondClock-App macht. Wenn Sie die Ausführung in Xcode stoppen, sehen Sie im Springboard des Simulators die neue App mit dem Namen OrangeClock.

Sie können natürlich die Unterschiede der Projektvarianten nicht nur über unterschiedliche Dateien, sondern auch über Unterschiede in den Konfigurationen festlegen. Schließlich besitzt ja jedes Target eigene Konfigurationen. Sie können den Programmcode also beispielsweise über Präprozessor-Makros beeinflussen, wie Sie das bereits für die Unterscheidung zwischen der Debug- und der Release-Konfiguration gemacht haben.

Öffnen Sie dazu den Reiter Build Settings des Targets OrangeClock, und suchen Sie nach der Einstellung »Preprocessor Macros«. Dort finden Sie den Eintrag DEBUG=1 für die Debug-Konfiguration. Der Wert für die Konfiguration Release ist hingegen leer. Für das neue Target definieren Sie ein neues Makro ORANGE_CLOCK=1. Sie können die Werte für beide Konfigurationen getrennt festlegen. Allerdings können Sie dann die Werte nicht mehr über die Konfiguration des Projekts beeinflussen. Solange Sie nur Werte zu einer Einstellung hinzufügen wollen, können Sie die Variable $(inherited) verwenden. Xcode ersetzt sie bei der Übersetzung durch den Wert der übergeordneten Konfiguration – also den entsprechenden Wert aus der Standardkonfiguration, des Projekts oder aus der Basiskonfiguration.

Wenn Sie also für die Einstellung Preprocessor Macros den Wert ORANGE_CLOCK=1 $(inherited) eintragen, dann zeigt die Debug-Einstellung den Wert ORANGE_CLOCK=1 DEBUG=1 und die Release-Einstellung den Wert ORANGE_CLOCK=1 an. Beide Einstellungen haben zwar in diesem Target die gleiche Definition, jedoch unterschiedliche Werte (siehe Abbildung 10.59).

Abbildung

Abbildung 10.59 Einstellungen erben

Konfigurationshierarchien

Die Werte für die Einstellungen kommen aus unterschiedlichen Konfigurationen. Dabei gibt es eine feste Suchreihenfolge:

  • Target
  • Projekt
  • projektdefinierte Basiskonfiguration
  • Standardkonfiguration

Wenn Xcode also den Wert für eine Einstellung sucht, geht es diese Liste von oben nach unten durch, bis es einen Wert findet. Wenn der Einstellungswert die Variable $(inherited) verwendet, sucht Xcode deren Wert nur in den folgenden Konfigurationen in der Liste. In der Konfiguration des OrangeClock-Targets haben Sie diese Variable verwendet. Xcode sucht deren Wert also zunächst im Projekt, dann in der Basiskonfiguration ClockLibrary.xcconfig und schließlich in der Standardkonfiguration.

Das Makro ORANGE_CLOCK können Sie jetzt dazu verwenden, die beiden Varianten der App auch im Programmcode zu unterscheiden. Beispielsweise können Sie damit den Hintergrund des Ziffernblatts der OrangeClock-App abrunden. Dazu fügen Sie die Methode viewDidLoad so wie in Listing 10.14in die Klasse SecondClockViewController ein. Außerdem müssen Sie die Headerdatei des QuartzCore-Frameworks einfügen und dieses Framework auch beim Linken des Targets OrangeClock einbinden.

#import "SecondClockViewController.h"
#import <ClockLibrary/ClockView.h>

#ifdef ORANGE_CLOCK
#import <QuartzCore/QuartzCore.h>
#endif

@interface SecondClockViewController ()

@property (weak, nonatomic) IBOutlet ClockView *clockView;

@end

@implementation SecondClockViewController

- (void)viewDidLoad {
[super viewDidLoad];
#ifdef ORANGE_CLOCK
CALayer *theLayer = self.clockView.layer;

theLayer.cornerRadius = 20.0;
theLayer.masksToBounds = YES;
#endif
}

Listing 10.14 Programmvarianten über Präprozessor-Makros

Damit Sie die Änderungen auch sehen können, sollten Sie den Clockview im Storyboard des OrangeClock-Projekts etwas verkleinern (z. B. auf die Werte X = 10, Y = 10, Width = 300, Height = 300) und seine Hintergrundfarbe auf Rot setzen. Durch diese Änderung zeigt die OrangeClock-App das Ziffernblatt in einem roten Quadrat mit abgerundeten Ecken an (siehe Abbildung 10.60).

Abbildung

Abbildung 10.60 Das Zifferblatt im Target »OrangeClock«

Ja, wo laufen sie denn?

Ein weiterer Aspekt der Variantenbildung ist die Unterstützung unterschiedlicher iOS-Versionen. Hierbei erzeugen Sie in der Regel jedoch keine Varianten zur Übersetzungs-, sondern zur Laufzeit. Sie können diese Varianten also nicht über Targets, Konfigurationen und Makros unterscheiden, sondern müssen die App dynamisch zur Laufzeit anpassen.

Im General-Reiter des Targets können Sie über Deployment Target (siehe Abbildung 10.61) die minimal notwendige iOS-Version für das Produkt festlegen. Wenn Ihre App beispielsweise auch unter iOS 5 laufen soll, sollten Sie in dem Menü 5.0 auswählen. Sie müssen dann allerdings auch darauf achten, dass Ihr Code keine Eigenschaften (z. B. Collectionviews, Labels mit Texten mit Hervorhebungen) einer neueren iOS-Version verwendet. Beim OrangeClock-Projekt sollten Sie also die Base-Internationalisierung und das Autolayout ausschalten.

Sie können entweder ganz auf diese Funktionen verzichten oder über die Funktion NSClassFromString oder die Methoden respondsToSelector: oder instancesRespondsToSelector: zur Laufzeit testen, ob die gewünschte Klasse beziehungsweise Methode vorhanden ist. Beispielsweise entscheiden Sie über

if(NSClassFromString("UICollectionView") == nil) {
// Gut, machen wir es anders
}
else {
// Hurra, es gibt Collectionviews
}

, ob die Cocoa-Touch-Version Collectionviews unterstützt, oder überprüfen mit

if([theLabel respondsToSelector: 
@selector(setAttributedText:)]) {
NSAttributedString *theAttributedText = ...;

[theLabel setAttributedText:theAttributedText];
}
else {
NSString *theString = ...;
[theLabel setText:theText];
}

ob Sie in einem Label einen Text mit Hervorhebungen zuweisen können. Dabei gilt generell das, was auch für Foren gilt: Fragen Sie das, was Sie wissen wollen. Schließen Sie nicht von dem einen Feature auf ein anderes, nur weil Apple sie in der gleichen Betriebssystemversion eingeführt hat. Beispielsweise hat Apple die Methode [NSStringDrawingContext actualTrackingAdjustment] in iOS 6.0 eingeführt und in iOS 7.0 als veraltet markiert. Sie können diese Methode also noch unter iOS 7 verwenden, sich jedoch nicht darauf verlassen, dass es sie auch noch unter iOS 8 oder 9 gibt. Falls Sie diese Methode also verwenden müssen, sollten Sie also nicht aus der Betriebssystemversion auf ihre Existenz schließen.

Bevor Sie eine App im Store einreichen, sollten Sie sie immer ausgiebig auf den Endgeräten testen. Wenn Ihre App auch ältere Betriebssystemversionen unterstützt, sind Tests auf Geräten mit diesen Versionen höchst empfehlenswert.

Abbildung

Abbildung 10.61 Auswahl des Deployment-Targets


Rheinwerk Computing - Zum Seitenanfang

10.4.6SchemataZur vorigen Überschrift

Über ein Schema legen Sie die Parameter für die Aktionen in Xcode fest. Eine Aktion ist eine festgelegte Aufgabe, die auf einem oder mehreren Targets basiert. Es gibt folgende Aktionen:

  • Build: Erzeugt das Produkt eines oder mehrerer Targets.
  • Run: Führt das Produkt eines Targets aus.
  • Test: Führt die Unit-Tests eines Targets aus.
  • Profile: Startet Instruments mit dem Produkt eines Targets.
  • Analyze: Untersucht den Quellcode auf Fehler.
  • Archive: Archiviert ein Produkt.

Über den Menüpunkt ProductManage Schemes... können Sie sich die Schemata in Ihrem Workspace ansehen. Der Beispiel-Workspace sollte vier Schemata enthalten (siehe Abbildung 10.62).

Abbildung

Abbildung 10.62 Verwaltung der Schemata

Da Sie das Target ClockLibrary nicht manuell ausführen müssen, können Sie es über die Checkbox in der Spalte Show unsichtbar schalten. Es erscheint dann nicht mehr in dem Scheme-Popup-Button in der Xcode-Toolbar. Xcode legt zu jedem neuen Target automatisch ein eigenes Schema an. Dieses Verhalten lässt sich über die Checkbox Autocreate schemes ausschalten. Über den Button Autocreate Schemes Now können Sie die Erzeugung manuell aufrufen.

Wählen Sie das Schema OrangeClock aus, und klicken Sie auf den Button Edit Scheme... Xcode öffnet einen Dialog mit der Run-Aktion des Schemas (siehe Abbildung 10.63). Diese Aktion führt Xcode aus, wenn Sie auf den Button Run in der Toolbar klicken. Sie können in dieser Aktion die Konfiguration, das auszuführende Programm und den Debugger auswählen und somit die Konfiguration ändern, mit der Sie Ihr Programm debuggen. Das ist beispielsweise dann sinnvoll, wenn bestimmte Fehler nur in der Release-Konfiguration auftreten.

Abbildung

Abbildung 10.63 Bearbeiten der »Run«-Aktion eines Schemas

Über die Einstellung Launch legen Sie fest, wie Xcode die Ausführung des Programms und des Debuggers startet. Wenn Sie die Standardoption Automatically auswählen, startet der Debugger das Programm, wie Sie es gewohnt sind. Bei der zweiten Option, Wait for OrangeClock.app to be launched manually, müssen Sie das Programm manuell starten, und der Debugger hängt sich automatisch ein.

Programmstarts debuggen

Sie können diese Option verwenden, um das Verhalten Ihres Programms zu analysieren, wenn das Betriebssystem es startet. Beispielsweise ermöglicht Ihnen diese Option, das Verhalten der Methode application:didReceiveLocalNotification: zu untersuchen, ohne dass Ihr Programm bereits läuft.

Auf der linken Seite des Dialogfensters sehen Sie eine Liste mit allen Aktionen. Außer bei der Build-Aktion steht unter allen Aktionen die verwendete Konfiguration, die Sie jeweils über ein Menü Build Configuration auswählen können. Das funktioniert bei allen Aktionen gleich, weswegen wir hier nicht näher darauf eingehen.

Wenn Sie die Build-Aktion durch Anklicken öffnen, sehen Sie den in Abbildung 10.64 gezeigten Dialog. Über die Tabelle auf der rechten Seite können Sie die Targets festlegen, die erzeugt werden sollen. Über die Checkboxen bestimmen Sie, für welche Aktionen Xcode das Produkt dabei erzeugen soll. Wenn Sie die Checkbox ausschalten, verwendet die Aktion gegebenenfalls das bereits vorhandene Produkt eines früheren Builds.

Abbildung

Abbildung 10.64 Die Einstellungen der »Build«-Aktion

Sie können über den Plus-Button am unteren Rand des Dialogs weitere Targets in die Tabelle einfügen. Beispielsweise können Sie das Produkt des SecondClock-Targets immer zusammen mit dem des OrangeClock-Targets bauen. Klicken Sie dazu den Plus-Button an, und wählen Sie das SecondClock-Target aus der Liste aus. Danach erscheint in der Tabelle eine Zeile mit dem gewählten Eintrag. Auch hier können Sie wieder festlegen, für welche Aktionen Xcode das Produkt erzeugen soll.

Es kann nur eins geben

Falls das Schema zu einer App gehört, die Sie über den iTunes Store oder eine Distribution verteilen möchten, darf die Build-Aktion nur ein Target enthalten; andernfalls erzeugt Xcode nicht den passenden Archivtyp. Da Sie von der OrangeClock-App später noch eine Ad-hoc-Distribution erstellen, sollten Sie das Target SecondClock wieder aus Build-Aktion löschen.

Diese Möglichkeit, weitere Targets explizit in die Build-Aktion aufzunehmen, ist in erster Linie für Targets gedacht, die voneinander unabhängig sind. Das ist bei der OrangeClock- und SecondClock-App der Fall, da Sie jeweils das eine Programm ohne das andere erzeugen können. Im Gegensatz dazu können Sie die OrangeClock-App erst dann erstellen, wenn Sie vorher die Bibliothek erzeugt haben. Das Target für die App ist also abhängig von dem Target für die Bibliothek. Über die Checkbox Find Implicit Dependencies können Sie festlegen, dass Xcode alle Targets sucht und erzeugt, von denen die aufgelisteten Targets in der Tabelle abhängen. Für das OrangeClock-Target bedeutet das beispielsweise, dass Xcode vor der App die Bibliothek erstellt. Wenn Sie die Checkbox Parallelize Builds einschalten, versucht Xcode, voneinander unabhängige Targets parallel zu erstellen. Die Entwicklungsumgebung nutzt dann also mehrere Prozessoren, um die Produkte der Targets zu bauen, und sollte so die Erstellung schneller durchführen.

Wenn Sie den Punkt Test in der linken Spalte auswählen, können Sie die Targets mit Ihren Testfällen konfigurieren. Sie finden hier eine Tabelle, die die Targets dieser Aktion auflistet. Auch hier können Sie wie bei der Build-Aktion weitere Targets über den Plus-Button hinzufügen. Die Targets lassen sich aufklappen, um die darin enthaltenen Klassen anzuzeigen. Unter den Klassen finden Sie dabei jeweils deren Testmethoden. Abbildung 10.65 stellt die Test-Aktion des entsprechenden Targets aus der Games-App aus Kapitel 6, »Models, Layer, Animationen«, dar.

Mit den Checkboxen in der rechten Spalte schalten Sie einzelne Komponenten (Targets, Klassen und Methoden) für das Testen an oder ab. Wenn Sie eine Komponente abschalten, führt Xcode bei einem Testlauf keinerlei Tests für sie aus. Wenn Sie beispielsweise die Tests für die Klasse GamesTests ausschalten, führt die Testumgebung keine der aufgeführten Methoden aus.

Abbildung

Abbildung 10.65 Einstellungen für die Test-Aktion

Im Dialog für die Profile-Aktion wählen Sie unter dem Punkt Executable das Programm, das Sie untersuchen möchten. Außerdem können Sie die Vorlage auswählen, die Instruments für die Untersuchung verwenden soll (siehe Abbildung 10.66). Dadurch können Sie also den Startdialog von Instruments umgehen.

Abbildung

Abbildung 10.66 Vorauswahl eines Instruments in der Profile-Aktion

Sie brauchen die Archive-Aktion, um eine App in den App Store hochzuladen oder um sie über eine Ad-hoc- oder In-House-Distribution zu verteilen. Für die Archive-Aktion können Sie den Namen des Archivs auswählen. Xcode verwendet dafür standardmäßig den Namen des Targets. Über die Checkbox Reveal Archive in Organizer können Sie festlegen, dass Xcode das Archiv eines Produkts nach dem Archivieren im Organizer anzeigt (siehe Abbildung 10.67).

Abbildung

Abbildung 10.67 Archiveinstellungen eines Schemas

Nomen est omen

Der Name eines Archivs für den App Store sollte keine Leer- oder Sonderzeichen enthalten, da das zu Problemen beim Upload in den App Store führen kann. Falls der Name des Targets solche Zeichen enthält, sollten Sie über diese Einstellungen einen unproblematischen Archivnamen wählen.



Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück




Copyright © Rheinwerk Verlag GmbH, Bonn 2014
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern


  Zum Rheinwerk-Shop
Zum Katalog: Apps programmieren für iPhone und iPad






Neuauflage: Apps programmieren für iPhone und iPad
Jetzt Buch bestellen


 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Einstieg in Objective-C 2.0 und Cocoa






Einstieg in Objective-C 2.0 und Cocoa


Zum Katalog: Spieleprogrammierung mit Android Studio






Spieleprogrammierung mit Android Studio


Zum Katalog: Android 5






Android 5


Zum Katalog: iPhone und iPad-Apps entwickeln






iPhone und iPad-Apps entwickeln


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo