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

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 8 Datenserialisierung und Internetzugriff
Pfeil 8.1 Ich packe meine Texte
Pfeil 8.1.1 Serialisierung von Zeichenketten
Pfeil 8.1.2 Zeichenkodierungen
Pfeil 8.1.3 Unicode und UTF-8
Pfeil 8.1.4 Die Zeichenkodierung erkennen
Pfeil 8.1.5 Zeichenketten konvertieren
Pfeil 8.2 JSON und die URLonauten
Pfeil 8.2.1 Das JSON-Format
Pfeil 8.2.2 Einfacher YouTube-Zugriff mit JSON
Pfeil 8.2.3 URLs erstellen
Pfeil 8.2.4 JSON-Dokumente schreiben
Pfeil 8.2.5 Verwendung des JSONKits als Parser
Pfeil 8.3 XML
Pfeil 8.3.1 XML in Kürze
Pfeil 8.3.2 Property-Listen
Pfeil 8.3.3 SAX
Pfeil 8.3.4 DOM und XPath
Pfeil 8.3.5 Der Tag der Entscheidung
Pfeil 8.4 Daten, Daten, ihr müsst wandern
Pfeil 8.4.1 Synchrone Kommunikation
Pfeil 8.4.2 Komplexe Anfragen
Pfeil 8.4.3 Auf dem Webserver nichts Neues
Pfeil 8.4.4 Asynchrone Kommunikation
Pfeil 8.4.5 Große Datenmengen der Übertragung
Pfeil 8.4.6 Passwortabfragen
Pfeil 8.4.7 Sicher kommunizieren mit TSL (SSL)
Pfeil 8.4.8 Hier geht die POST ab
Pfeil 8.4.9 Dateiupload
Pfeil 8.4.10 Überprüfung der Erreichbarkeit
Pfeil 8.5 Karten
Pfeil 8.5.1 Karten darstellen
Pfeil 8.5.2 Koordinatensysteme
Pfeil 8.5.3 Geokoordinaten bestimmen
Pfeil 8.5.4 Eigene Kartenbeschriftungen
Pfeil 8.5.5 Routen

Rheinwerk Computing - Zum Seitenanfang

8.2JSON und die URLonautenZur nächsten Überschrift

Ein solches Format ist beispielsweise JSON (JavaScript Object Notation). Es ist auch für Menschen gut lesbar und benötigt nur geringe Verwaltungsinformationen – das sind die Teile in der Zeichenkette, die zur Strukturierung der Daten dienen. Außerdem ist es einfach zu parsen. Aus diesen Gründen setzen inzwischen sehr viele Webdienste (wie Google, YouTube und Twitter) JSON ein.

Der nächste Abschnitt beschreibt kurz den Aufbau des JSON-Formats, und danach soll anhand eines praktischen Beispiels auch die Anwendung nicht zu kurz kommen.


Rheinwerk Computing - Zum Seitenanfang

8.2.1Das JSON-FormatZur nächsten ÜberschriftZur vorigen Überschrift

Ein JSON-Ausdruck ist eine Zeichenkette und kann die folgenden einfachen Werte beschreiben:

  • die Konstante null
  • die booleschen Konstanten false und true
  • vorzeichenbehaftete ganze Zahlen, wie z. B. 1, 3 und -12
  • Fließkommazahlen: 3.14159, -22.5 oder 22.34e-12
  • durch Anführungszeichen begrenzte Zeichenketten: "Hallo"

Nur mit diesen einfachen Werten könnte ein JSON-Ausdruck auch immer nur einen einzelnen Wert beschreiben. Komplexere Daten lassen sich in Arrays oder Dictionarys unterbringen. Arrays beginnen mit einer öffnenden und enden mit einer schließenden eckigen Klammer. Zwischen diesen Klammern steht eine Liste von JSON-Ausdrücken, die durch Kommas getrennt sind. Zum Beispiel sind [], [1, 2, 3], ["A", "B", "C"] oder ["Fritz", 2, 9.8, true] gültige JSON-Ausdrücke für Arrays, und Sie dürfen sogar Arrays ineinander verschachteln: [[1, 2, 3], false, ["Haus"], [[2, 3], [4, 5]]].

Dictionarys enthalten beliebige Schlüssel-Wert-Paare; dabei ist jeder Schlüssel eine Zeichenkette, die pro Dictionary nur maximal einmal vorkommen darf. Als Wert dürfen Sie hingegen jeden gültigen JSON-Ausdruck verwenden. Ein einfaches Dictionary sieht beispielsweise so aus: { "name" : "Fritz", "age" : 25, "male" : true }. Natürlich können Sie auch Arrays und Dictionarys nahezu beliebig ineinander verschachteln; z. B. so:

{
"lottozahlen" : [1, 2, 13, 19, 27, 32, 49],
"zusatzzahl" : 22,
"gewinner" : [
{"name" : "Lieschen Müller", "zusatzzahl" : true},
{"name" : "Gustav Gans", "zusatzzahl" : false}
]
}

Listing 8.1 JSON-Ausdruck mit verschachtelten Arrays und Dictionarys

Wie das Beispiel aus Listing 8.1 zeigt, können Sie mit JSON relativ einfach komplexe Datenstrukturen beschreiben.

Mehr zum JSON-Format

Da es hier nicht um die Erstellung eines JSON-Parsers geht, beschränkt sich die Beschreibung des Formats nur auf das Notwendigste. Eine genauere Darstellung finden Sie unter http://www.json.org/json-de.html. Dort finden Sie auch eine formale Beschreibung für JSON-Ausdrücke.


Rheinwerk Computing - Zum Seitenanfang

8.2.2Einfacher YouTube-Zugriff mit JSONZur nächsten ÜberschriftZur vorigen Überschrift

Das Beispielprojekt YouTube dieses Abschnitts führt eine Suche in den YouTube-Videos aus und zeigt das Ergebnis (allerdings nur den Titel und die Beschreibung) in einem Tableview an. YouTube liefert das Suchergebnis als JSON-Ausdruck. Wie bereits erwähnt, brauchen Sie dazu keine eigenen JSON-Parser zu schreiben. Stattdessen verwendet das Beispielprojekt fertige Parser.

Projektinformation

Den Quellcode des folgenden Beispiels finden Sie auf der DVD unter Code/Apps/iOS5/YouTube oder im Github-Repository zum Buch im Unterverzeichnis https://github.com/Cocoaneheads/iPhone/tree/Auflage_3/Apps/iOS5/YouTube.

Apple bietet für die JSON-Serialisierung und -Deserialisierung die Klasse NSJSONSerialization an. Sie bildet dabei die JSON-Datentypen auf Objective-C-Klassen gemäß Tabelle 8.2 ab.

Tabelle 8.2 Abbildung der JSON-Datentypen auf Klassen

JSON-Datentyp bzw. -Konstante Objective-C-Klasse

null

NSNull

false/true

NSNumber

Ganzzahl

NSNumber

Fließkommazahl

NSNumber

Zeichenkette

NSString

Array

NSArray

Dictionary

NSDictionary

Die Applikation kann nun eine Suche auf YouTube ausführen, das Ergebnis in ein NSData-Objekt laden und es dann mit Hilfe dieser Methode deserialisieren, um es danach im Tableview darzustellen. Die Suche können Sie über einen URL-Aufruf ausführen. Beispielsweise finden Sie über die URL

http://gdata.youtube.com/feeds/api/videos?orderby=published&alt=json&q=iOS

alle Videos, die den Suchbegriff »iOS« enthalten. Die URL enthält den Suchbegriff im Parameter »q« [Anm.: Das »q« steht für »query« – das englische Wort für »Suchanfrage«.] am Ende, und über den Parameter »alt=json« geben Sie an, dass YouTube Ihnen das Ergebnis im JSON-Format liefern soll. Wenn Sie den alt-Parameter weglassen, erhalten Sie das Ergebnis stattdessen in einem XML-Format. Außerdem enthält die URL den Parameter »orderby=published«, der für eine Sortierung des Ergebnisses nach dem Veröffentlichungsdatum sorgt. Wenn Sie diese URL in Ihrem Browser eingeben, erhalten Sie eine weiße Seite mit sehr viel Text (siehe Abbildung 8.1).

Abbildung

Abbildung 8.1 Ergebnis einer YouTube-Suche

Wie Sie an der Abbildung sehen, liefert YouTube Ihnen auf der obersten Ebene ein Dictionary, das unter anderem den Schlüssel »feed« enthält. Unter diesem Schlüssel finden Sie einen weiteren »entry«, der ein Array mit den Einträgen in jeweils einem Dictionary enthält. Die Beispielapplikation braucht also nur den Wert zu diesem Schlüsselpfad über die Methode valueForKeyPath: auszulesen und das Array in einer Property zu speichern. Natürlich verwendet sie für die Datenabfrage keinen Browser, sondern ruft den Inhalt stattdessen über den Convenience-Konstruktor dataWithContentsOfURL: der Klasse NSData von YouTube ab.

Synchrone URL-Anfragen

Die Methode dataWithContentsOfURL: führt eine synchrone URL-Anfrage – auch synchroner Request genannt – durch. Dabei fragt die Methode die Daten komplett vom Server ab und liefert sie in einem Objekt zurück. Der Aufrufer der Methode muss also so lange warten, bis die Methode die Daten vollständig geladen hat, was bei großen Datenmengen oder langsamen Verbindungen etwas dauern kann. Die Applikation ist während eines solchen Methodenaufrufs in der Regel geblockt und kann keine anderen Operationen ausführen. Dieses Manko lässt sich durch asynchrone Requests umgehen, die allerdings etwas aufwendiger zu implementieren sind. Abschnitt 8.4.4, »Asynchrone Kommunikation«, geht auf die Implementierung asynchroner Requests genauer ein.

Verwenden Sie synchrone Requests nur, wenn die transferierte Datenmenge sehr klein ist. Eine produktive YouTube-App sollte die Suchergebnisse besser mit einem asynchronen Request laden, da sie mehrere Kilobyte groß sind. Die Beispielapplikation ist also hier ein schlechtes Vorbild.

Listing 8.2 enthält die Methode updateItems, die das Suchergebnis vom YouTube-Server lädt und deserialisiert. Sie liest aus dem Dictionary das Array mit den Videos aus und weist es der Property items zu. Dabei liefert die Methode createURL zunächst einfach die oben beschriebene URL zurück.

- (void)updateItems {
NSData *theData = [NSData dataWithContentsOfURL:
self.createURL];
NSError *theError = nil;
NSDictionary *theResult = [NSJSONSerialization
JSONObjectWithData:theData options:0
error:&theError];

if(theError == nil) {
self.items =
[theResult valueForKeyPath:@"feed.entry"];
[self.tableView reloadData];
}
else {
NSLog(@"updateItems %@", theError);
}
}

Listing 8.2 Laden und Deserialisieren der YouTube-Daten

Das Beispielprojekt verwendet ein Storyboard und einen Tableview mit einem Zellprototyp. Damit und mit der Property items lässt sich die Datenquelle für den Tableview relativ einfach implementieren:

- (NSInteger)numberOfSectionsInTableView:  
(UITableView *)inTableView {
return 1;
}

- (NSInteger)tableView:(UITableView *)inTableView
numberOfRowsInSection:(NSInteger)inSection {
return [self.items count];
}

- (UITableViewCell *)tableView:(UITableView *)inTableView
cellForRowAtIndexPath:(NSIndexPath *)inIndexPath {
UITableViewCell *theCell = [self.tableView
dequeueReusableCellWithIdentifier:@"Cell"];
NSDictionary *theItem = self.items[inIndexPath.row];

theCell.textLabel.text = [theItem
valueForKeyPath:@"title.$t"];
theCell.detailTextLabel.text =
[theItem valueForKeyPath:@"content.$t"];
return theCell;
}

Listing 8.3 Die Datenquelle für den Tableview der Videos

Der Titel und die Beschreibung liegen jeweils in einem eigenen Dictionary im Eintrag, weswegen Listing 8.3 auch diese Werte über valueForKeyPath: ausliest. Das ist fast der komplette Code, den Sie für die YouTube-Anbindung schreiben müssen. Die Einbindung eines JSON-Feeds ist relativ einfach zu bewerkstelligen.

Allerdings ist die Applikation jetzt noch nicht besonders komfortabel. Sie müssen sie neu starten, um die Videos zu aktualisieren, und Sie können den Suchbegriff nicht ändern. Glücklicherweise lassen sich diese beiden Funktionen relativ einfach nachrüsten.

Viele iOS-Applikationen verwenden das Bouncen am oberen Rand des Tableviews als Auslöser für eine Aktualisierung. Bouncen bezeichnet dabei das Ziehen des Inhalts über die Grenzen des Tableviews hinaus und das anschließende Zurückflitschen, so dass dabei der Tabellenhintergrund sichtbar wird. In der Beispielapplikation soll das Ziehen der Tabellenzellen nach unten die Aktualisierung auslösen. Das soll jedoch nur geschehen, wenn mindestens ein Viertel des Hintergrunds sichtbar ist. Dieser Zustand lässt sich über das Scrollview-Delegate feststellen. Da ja die Klasse UITableView eine Unterklasse von UIScrollView ist, ruft ein Tableview auch die Delegate-Methoden des Scrollviews auf.

Für die Abfrage des Bouncings bieten sich die Delegate-Methoden scrollViewDidScroll: und scrollViewDidEndDragging: an. Der Tableview ruft jedoch die Did-scroll-Methode kontinuierlich während des Scrollens auf. Damit die Applikation nicht ständig die Daten aktualisiert, müssten Sie bei Verwendung dieser Methode eine Sperre gegen zu häufiges Aktualisieren einbauen.

Die Beispielapplikation verwendet hingegen die End-Dragging-Methode, die der Tableview nur aufruft, wenn der Nutzer das Ziehen beendet, also den Scrollview losgelassen hat. Dadurch kann der Nutzer die Aktualisierung nicht zu schnell hintereinander auslösen. Die Implementierung in der App prüft, ob der Nutzer den Inhalt über ein Viertel der Höhe hinausgezogen hat, und löst in diesem Fall die Aktualisierung aus.

- (void)scrollViewDidEndDragging: 
(UIScrollView *)inScrollView
willDecelerate:(BOOL)inDecelerate {
CGPoint theOffset = inScrollView.contentOffset;

if(theOffset.y <
-CGRectGetHeight(inScrollView.frame) / 4.0) {
[self updateItems];
}
}

Listing 8.4 Aktualisierung des Tableviews durch Bouncen

Es hakt noch

Wenn Sie nun den Inhalt des Tableviews nach unten ziehen, erscheinen in den meisten Fällen kurz danach neue Einträge in den Tabellenzellen. Allerdings springt der Inhalt jetzt auch nicht mehr sofort nach oben, wenn Sie ihn loslassen. Das liegt am synchronen Request, der die Bildschirmaktualisierung der Applikation für einen kurzen Moment unterbricht. Wie Sie das unterbinden können, erfahren Sie in Abschnitt 8.4.4, »Asynchrone Kommunikation«.

Durch die Aktualisierung verändert sich allerdings in den meisten Fällen nicht der Tabelleninhalt, weil die YouTube-Nutzer ja nicht ständig neue Videos zu dem Suchbegriff hochladen.

Für die Überprüfung der Aktualisierung können Sie jedoch einen häufigeren Begriff verwenden, der auch nur aus einem häufigen Buchstaben (z. B. »a« oder »e«) bestehen darf. Wenn Sie nun außerdem mit der Aktualisierung einige Minuten warten, sollte der Tableview auch tatsächlich neue Einträge anzeigen.

Das Refresh-Control

Mit iOS 6 hat Apple für die Aktualisierung von Tableviews ein eigenes Control eingeführt, das zum einen das Aussehen vereinheitlicht und zum andern den Implementierungsaufwand vereinfacht. Das UIRefreshControl zeigt beim Herunterziehen einen länger werdenden Reload-Button und während der Aktualisierung einen Activity-Indicator mit einem optionalen Text (siehe Abbildung 8.2).

Abbildung

Abbildung 8.2 Das Refresh-Control beim Herunterziehen

Am einfachsten verwenden Sie das zusammen mit einem UITableViewController, bei dem Sie im Interface Builder nur die Verwendung des Refresh-Controls einzuschalten brauchen. Dazu öffnen Sie den Attributinspektor des Tableviewcontrollers und wählen unter der Einstellung Refreshing in der Rubrik Table View Controller den Eintrag Enabled aus. Mit dieser Auswahl zeigt der Inspektor zusätzliche Felder an, mit denen Sie das Aussehen des Controls beeinflussen können. Neben dem Text können Sie hier die Farbe festlegen (siehe Abbildung 8.3).

Projektinformation

Den Quellcode für diese Variante des YouTube-Projekts finden Sie auf der DVD unter Code/Apps/iOS6/UniversalYouTube oder im Github-Repository zum Buch im Unterverzeichnis https://github.com/Cocoaneheads/iPhone/tree/Auflage_3/Apps/iOS6/UniversalYouTube.

Abbildung

Abbildung 8.3 Tableviewcontroller mit Refresh-Control

Wie alle Controls kommuniziert auch das Refresh-Control über den Target-Action-Mechanismus mit dem Controller; es verwendet für die Auslösung der Aktualisierung das Value-Changed-Ereignis. Sie müssen also dafür im Verbindungsinspektor des Controls von diesem Ereignis eine Verbindung zur gewünschten Action-Methode des Controllers ziehen. Das Control finden Sie zwar nicht auf der Zeichenfläche des Interface Builders, jedoch in der Baumdarstellung unter dem Tableviewcontroller (siehe Abbildung 8.4).

Abbildung

Abbildung 8.4 Refresh-Control in der View-Hierarchie

Allerdings hat (zumindest) die erste Version von iOS 6 einen Bug, durch den das Refresh-Control die Action-Verbindung für das Value-Changed-Ereignis nicht aus dem Storyboard übernimmt. Stattdessen müssen Sie diese Verbindung in der Methode viewDidLoad von Hand setzen. Der Code der Beispielapplikation prüft allerdings zunächst, ob das Control nicht doch die Verbindung aus dem Storyboard übernommen hat, damit der Code auch unter iOS 7 korrekt funktioniert.

- (void)viewDidLoad {
[super viewDidLoad];
UIRefreshControl *theControl = self.refreshControl;

if([theControl actionsForTarget:self
forControlEvent:UIControlEventValueChanged]
== nil) {
[theControl addTarget:self action:@selector(refresh)
forControlEvents:UIControlEventValueChanged];
}
}

Listing 8.5 Action-Verbindung des Refresh-Controls setzen

Die Action-Methode muss nun nur noch das Neuladen auslösen; dazu ruft sie im Beispielprojekt einfach die Methode updateItems auf. Außerdem müssen Sie dem Control das Ende des Aktualisierungsprozesses über die Methode endRefreshing mitteilen, damit es den Aktivitätsindikator stoppt und wieder aus der Anzeige des Tableviews verschwindet. Im Beispielprojekt erfolgt dieser Aufruf am Ende der Methode updateItems.

Rückwärtskompatibilität

Wenn Sie das Refresh-Control in einer App einsetzen möchten, die auch unter iOS 5 oder älter laufen soll, sollten Sie es nicht über den Interface Builder konfigurieren. Hier ist eine Konfiguration in der viewDidLoad-Methode des Tableviewcontrollers sinnvoll. Dazu müssen Sie zuerst die Verfügbarkeit prüfen und Refresh-Control dann gegebenenfalls erstellen und konfigurieren. Danach können Sie es über die Property refreshControl dem Controller zuweisen:

if([self respondsToSelector:@selector(setRefreshControl:)]) {
UIRefreshControl *theControl =
[[UIRefreshControl alloc] init];

[theControl addTarget:self
action:@selector(refresh)
forControlEvents:UIControlEventValueChanged];
self.refreshControl = theControl;
}

Dieser Code funktioniert natürlich nur, wenn Sie ihn mit dem SDK für iOS 6 übersetzen, da der Compiler die Klassen und Methoden kennen muss.

Sie können das Refresh-Control auch aus dem Programmcode über die Methode beginRefreshing anzeigen lassen, wenn der Nutzer die Aktualisierung nicht durch einen Bounce ausgelöst hat.


Rheinwerk Computing - Zum Seitenanfang

8.2.3URLs erstellenZur nächsten ÜberschriftZur vorigen Überschrift

Wie wir bereits erwähnt haben, besteht ein weiteres Manko der Beispielapplikation darin, dass sie einen festen Suchbegriff verwendet. Mit einigen wenigen Änderungen lässt sich jedoch auch hier Abhilfe schaffen. Dazu fügen Sie als Erstes dem Tableview einen Tableheaderview in Form einer Suchleiste hinzu, indem Sie sie per Drag & Drop über der Prototypzelle platzieren (siehe Abbildung 8.5).

Abbildung

Abbildung 8.5 Suchleiste im Tableview

Legen Sie den Viewcontroller als Delegate dieser Suchleiste fest, indem Sie zu seiner Klasse das Protokoll UISearchBarDelegate hinzufügen und eine Verbindung vom Delegate-Outlet der Leiste zum Viewcontroller-Objekt im Interface Builder ziehen. Außerdem sollten Sie eine Outlet-Property searchBar vom Controller auf die Suchleiste anlegen. Der Code für die Integration ist relativ kurz. Die Applikation muss die Suche starten, wenn der Nutzer einen Suchbegriff eingegeben hat. Dazu implementieren Sie die Delegate-Methode searchBarSearchButtonClicked: wie folgt:

- (void)searchBarSearchButtonClicked: 
(UISearchBar *)inSearchBar {
[inSearchBar endEditing:YES];
[self updateItems];
}

Listing 8.6 Ausführung der Suche

Außerdem sollte die App die Suchleiste mit einem Begriff vorinitialisieren:

- (void)viewDidLoad {
[super viewDidLoad];
self.searchBar.text = @"iOS";
}

Listing 8.7 Initialisierung der Suchleiste

Schließlich muss sie die URL für die Suche noch anpassen, indem sie den Suchbegriff für den Parameter q einsetzt. Dabei erzeugt die Methode die Zeichenkette für die URL über stringWithFormat:

- (NSURL *)createURL {
NSString *theQuery = self.searchBar.text;
NSString *theURL = [NSString stringWithFormat:
@"http://gdata.youtube.com/feeds/api/videos?
orderby=published&alt=json&q=%@",
theQuery];

return [NSURL URLWithString:theURL];
}

Listing 8.8 Erzeugung der URL mit variablem Suchbegriff

Sie können jetzt nach unterschiedlichen Begriffen suchen, indem Sie diese Begriffe in das Suchfeld eingeben. Allerdings funktioniert die Suche nicht korrekt, wenn Sie bestimmte Sonderzeichen (z. B. das Kaufmanns-Und »&«) eingeben, weil dieses Zeichen ja die Parameter innerhalb einer URL trennt. Sie müssen diese Zeichen maskieren, wenn Sie sie in einem URL-Parameter verwenden. Die Klasse NSString bietet zwar die Methode stringByAddingPercentEscapesUsingEncoding: dafür an, die jedoch für komplette URLs gedacht ist. In einer URL trennt das Kaufmanns-Und Parameterzuweisungen voneinander, und somit kodiert diese Methode die Und-Zeichen nicht.

Allerdings bietet das Framework Core Foundation mit der Funktion CFURLCreateStringByAddingPercentEscapes eine Möglichkeit, diese Kodierung umzusetzen. Das Beispielprojekt enthält die Kategorie NSString(URLTools), die auf Basis dieser Funktion die Methode encodedStringForURLWithEncoding: bereitstellt, die als Parameter einen Wert mit dem Typ NSStringEncoding erhält. Da Core Foundation allerdings eigene Werte für Kodierungen verwendet, müssen Sie den Parameterwert erst umrechnen, was die Funktion CFStringConvertNSStringEncodingToEncoding übernimmt.

- (NSString *)encodedStringForURLWithEncoding:  
(NSStringEncoding)inEncoding {
CFStringEncoding theEncoding =
CFStringConvertNSStringEncodingToEncoding(
inEncoding);
CFStringRef theResult =
CFURLCreateStringByAddingPercentEscapes(
kCFAllocatorDefault,
(__bridge CFStringRef)self,
NULL,
(CFStringRef)@"!*'();:@&=+$,/?%#[]",
theEncoding);

return (__bridge_transfer NSString *)theResult;
}

Listing 8.9 Kodierung von Zeichenketten als URL-Parameter

Die Funktion CFURLCreateStringByAddingPercentEscapes hat fünf Parameter, wobei der erste einen Allokator enthält, mit dem die Funktion neuen Speicher reserviert. Im zweiten Parameter bekommt sie die Zeichenkette zum Kodieren übergeben, und der Wert hierfür ist self, da sich die Methode ja in einer Kategorie von NSString befindet. Der dritte Parameter enthält die Zeichen, die die Funktion keinesfalls kodieren soll. Der Wert ist NULL, da sie keine Sonderzeichen ausnehmen soll. Der nächste Parameter gibt die Zeichen an, die sie auf jeden Fall kodieren soll, und der letzte Parameter enthält die gewünschte Zeichenkodierung.

Objective-C meets C

In Listing 8.9 kommen die Regeln für die Toll-free Bridge mit automatischem Referenzzählen zum Einsatz, die wir in Kapitel 6, »Models, Layer, Animationen«, besprochen haben. Die Parameter 2 bis 4 der Funktion haben den Typ CFStringRef, weswegen ein entsprechender Cast vor den Werten steht. Hier müssen Sie den Modifizierer __bridge verwenden, da der Cast die Eigentümerschaft nicht ändern soll.

Die Methode bekommt jedoch die Eigentümerschaft an der neuen Zeichenkette, die sie über die Variable theResult referenziert. Bei der Umwandlung dieser Referenz in ein Objekt müssen Sie dem ARC-Compiler also mitteilen, dass er die Eigentümerschaft des Objekts übertragen soll, weswegen Sie hier __bridge_transfer verwenden müssen.

Die Maskierung von Sonderzeichen für die Verwendung in URLs nennt man auch URL-Kodierung. Sie ersetzt dabei die Sonderzeichen durch Escape-Sequenzen. Das sind in diesem Fall jeweils ein Prozentzeichen und eine zweistellige Hexadezimalzahl. [Anm.: http://de.wikipedia.org/wiki/Hexadezimalsystem] Die Funktion bestimmt also bei allen Sonderzeichen die Zahl oder Zahlenfolge entsprechend der angegebenen Kodierung und setzt dafür die entsprechende Escape-Sequenz beziehungsweise mehrere Escape-Sequenzen ein.

Tabelle 8.3 enthält die URL-Kodierungen für einige Beispielwörter zu unterschiedlichen Zeichenkodierungen. Da ISO Latin 1 (bzw. ISO-8859-1) nicht das Eurozeichen »€« enthält, liefert die Funktion für die Zeichenkette »10€« NULL zurück.

Tabelle 8.3 URL-Kodierung für unterschiedliche Zeichenkodierungen

Zeichenkette MacRoman ISO Latin 1 UTF-8

Dick&Doof

Dick%26Doof

Dick%26Doof

Dick%26Doof

Grüße

Gr%9F%A7e

Gr%FC%DFe

Gr%C3%BC%C3%9Fe

10€

10%DB

10%E2%82%AC

Die Frage ist nun, welche Kodierung müssen Sie für den URL-Aufruf verwenden? Der RFC 3986 [Anm.: http://tools.ietf.org/html/rfc3986] empfiehlt die Verwendung von UTF-8; viele Webseiten halten sich indes nicht daran und verwenden stattdessen ISO Latin 1. Im Zweifelsfall hilft hier nur Ausprobieren. Die YouTube-API unterstützt UTF-8, und so können Sie die Methode createURL folgendermaßen anpassen:

- (NSURL *)createURL {
NSString *theQuery = [self.searchBar.text
encodedStringForURLWithEncoding:
NSUTF8StringEncoding];
NSString *theURL = [NSString stringWithFormat:
@"http://gdata.youtube.com/feeds/api/videos
?orderby=published&alt=json&q=%@",
theQuery];

return [NSURL URLWithString:theURL];
}

Listing 8.10 URL-Erzeugung mit Parameterkodierung


Rheinwerk Computing - Zum Seitenanfang

8.2.4JSON-Dokumente schreibenZur nächsten ÜberschriftZur vorigen Überschrift

Die Klasse NSJSONSerialization erlaubt über die Klassenmethode dataWithJSONObject:options:error: auch die Erzeugung von JSON-Dokumenten, wobei der erste Parameter das zu serialisierende Objekt enthält. Die Objekthierarchie dieses Objekts muss ein Dictionary oder Array als Wurzel haben und darf nur aus Objekten der Klassen NSString, NSNumber, NSArray, NSDictionary oder NSNull bestehen; andernfalls löst die Methode eine Ausnahme aus. Ob sich ein Objektbaum an diese Struktur hält, können Sie über die Klassenmethode isValidJSONObject: prüfen. Auf die explizite Prüfung sollten Sie jedoch möglichst verzichten, da sie natürlich Rechenzeit kostet. Stattdessen sollten Sie lieber beim Aufbau der Hierarchie darauf achten, nur Objekte mit den erlaubten Klassen zu verwenden.

Die Methode erzeugt ein Datenobjekt, das das JSON-Dokument enthält. Wenn Sie es in eine Datei schreiben wollen, können Sie das über die Methode writeToFile:atomically: machen:

NSError *theError = nil;
NSData *theData = [NSJSONSerialization
dataWithJSONObject:theObject options:0 error:&theError];

if(theError) {
// Fehler auswerten
}
else if(![theData writeToFile:thePath atomically:YES]) {
// Fehler beim Schreiben der Datei
}

Listing 8.11 Schreiben eines JSON-Dokuments in eine Datei

Für den options-Parameter im Serialisierungsaufruf können Sie auch die Konstante NSJSONWritingPrettyPrinted verwenden. Dann fügt die Methode Leerzeichen und Zeilenumbrüche für eine bessere Lesbarkeit ein.

Tipp

Das JSON-Format eignet sich nicht nur für den Datenaustausch zwischen Programmen und Web-Anwendungen, sondern auch für die Speicherung von Programmdaten im Dateisystem.


Rheinwerk Computing - Zum Seitenanfang

8.2.5Verwendung des JSONKits als ParserZur vorigen Überschrift

Wenn Sie JSON in einer Applikation unter iOS 4 verwenden möchten, steht Ihnen die Klasse NSJSONSerialization nicht zur Verfügung. Es gibt jedoch eine Reihe von Open-Source-Projekten, die Sie ersatzweise einsetzen können, beispielsweise das JSONKit von John Engelhart. Sie können es über die URL https://github.com/johnezang/JSONKit/zipball/master laden. Das eigentliche Kit besteht aus einer Header- und einer Implementierungsdatei, die Sie zu Ihren Projekten hinzufügen können.

Leider unterstützt JSONKit noch kein automatisches Referenzzählen (ARC). Deswegen müssen Sie allerdings nicht das komplette Projekt auf manuelles Referenzzählen (MRC) umstellen. Sie können stattdessen ARC nur für die Datei JSONKit.m ausschalten. Übersetzter ARC-Code arbeitet ja problemlos mit übersetztem MRC-Code zusammen. Dieser Umstand ist ein weiterer Grund, warum wir die Einbindung des JSONKits hier zeigen.

Zum Ausschalten öffnen Sie das Target des Projekts, indem Sie im Projektnavigator das Projekt auswählen und in der Spalte daneben den Eintrag YouTube in der Rubrik TARGETS wählen. Unter dem Reiter Build Phases klappen Sie danach die Rubrik Compile Sources auf, in der Sie die Datei JSONKit.m selektieren. Wenn Sie nun die Taste für den Zeilenvorschub drücken, öffnet sich ein Popup mit einer Texteingabe (siehe Abbildung 8.6). Dort können Sie Kommandozeilenargumente für die Übersetzung dieser Datei eingeben. Um ARC auszuschalten, geben Sie den Text -fno-objc-arc ein.

Abbildung

Abbildung 8.6 ARC für eine einzelne Datei ausschalten

Das JSONKit stellt über die Kategorie NSData(JSONKitDeserializing) Methoden bereit, um JSON-Daten in Objektbäume umzuwandeln. Sie bildet die JSON-Datentypen auf die gleichen Objective-C-Klassen wie die Apple-Implementierung ab (siehe Tabelle 8.2). In der Datei YouTubeViewController.m brauchen Sie nur die Headerdatei JSONKit.h zu importieren und die Zuweisung zu der Variablen theResults zu ändern:

NSDictionary *theResult = [theData 
objectFromJSONDataWithParseOptions:JKParseOptionStrict error:&theError];

Listing 8.12 JSON-Deserialisierung mit dem JSONKit

Im Beispielprojekt können Sie für die Aktivierung des JSONKits das Makro USE_JSON_KIT am Anfang der Datei YouTubeViewController.m von 0 auf 1 setzen:

#define USE_JSON_KIT 1

Mit dem JSONKit können Sie natürlich auch JSON-Dokumente schreiben; die Kategorien stellen auch dafür jeweils mehrere Methoden bereit. Am einfachsten verwenden Sie die Methode JSONData. In Listing 8.11 brauchen Sie also für die Verwendung des JSONKits nur die zweite Zeile durch die folgende auszutauschen:

NSData *theData = [theObject JSONData];

POITROAE: Premature optimization is the root of all evil

Es gibt noch einen weiteren Vorteil, der dafür spricht, das JSONKit zu verwenden: Es ist wesentlich schneller als das Apple-Framework. [Anm.: http://www.cocoanetics.com/2011/03/json-versus-plist-the-ultimate-showdown] Bei großen Datenmengen kann der Einsatz des Kits also auch unter iOS 5 oder neueren Versionen Vorteile bringen. Allerdings sollten Sie es erst dann einsetzen, wenn sich die Standardimplementierung als unzureichend für Ihre Zwecke erweist. Die Umstellung ist ja nicht sehr aufwendig.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.

>> Zum Feedback-Formular
<< 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.


[Rheinwerk Computing]

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


  Zum Katalog
Zum Katalog: Apps programmieren für iPhone und iPad






Apps programmieren für iPhone und iPad
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Apps programmieren für iPhone und iPad






Apps programmieren für iPhone und iPad


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


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo