Restful Services

Need a REST?
Kommentare

Wer unter iOS Apps entwickelt, kommt früher oder später mit ihnen in Berührung: den Web Services. Apps, die vollständig autark arbeiten, lassen sich kaum noch entwickeln. Sie bieten meist nicht genug Mehrwert im Vergleich zu existierenden Applikationen. Teils wird nur mit einem privaten API kommuniziert, um der App ein Backend zur Verfügung zu stellen. Andere Apps nutzen öffentliche APIs und integrieren angebotene Dienste. Auf welche Weise die Schnittstellen dabei angesprochen werden, hängt stark von dem Typ der Implementierung ab. Es gibt verschiedene Ansätze. Einer davon sind RESTful Web Services. In diesem Artikel zeige ich, wie Sie unter iOS mit der Hilfe der freien AFNetworking-Bibliothek RESTful Services in Ihren Apps ansprechen können.

AFNetworking [1] ist eine freie Bibliothek, die in der Praxis häufig benötigte Netzwerklogik kapselt und innerhalb von iOS-Apps zur Verfügung stellt. Sie basiert auf dem iOS-Framework, macht den Zugriff auf Webserver allerdings deutlich einfacher und angenehmer. Dank AFNetworking müssen zum Beispiel nicht immer wieder gleiche oder sehr ähnliche Anwendungsteile implementiert werden, sobald auf Webserver zugegriffen wird. Die Bibliothek spart Entwicklern Zeit und Arbeit und macht die Entwicklung und die Fehlersuche einfacher. Doch werfen wir zunächst einen kurzen Blick auf REST selbst.

Grundlegendes zu REST

Die Abkürzung REST steht für Representational State Transfer [2]. Bei REST handelt es sich um ein Paradigma, nicht um ein eigenes Protokoll. Eine Anwendung wird als RESTful eingestuft, wenn sie die grundlegenden Prinzipien der REST-Idee befolgt. Ein wichtiges Merkmal aus der Sicht der API-Konsumenten sind die verwendeten Operationen, mit denen Entitäten abgerufen oder verändert werden. Die verwendete HTTP-Methode, im REST-Kontext auch oft als Verb bezeichnet, signalisiert beim Aufruf einer Ressource, welche Aktion ausgeführt werden soll:

  • GET: Lesen von Entitäten
  • POST: Erzeugen neuer Entitäten
  • PUT: Aktualisieren vorhandener Entitäten
  • DELETE: Löschen vorhandener Entitäten

Ebenfalls von hoher Bedeutung ist die eindeutige Adressierbarkeit von Datensätzen. Jede Ressource, also jede gespeicherte Entität, muss über einen eigenen URL erreichbar sein. Es gibt noch weitere Anforderungen, die jedoch nicht unmittelbar für den Konsumenten des API wichtig sind. Deshalb gehe ich darauf nicht weiter ein und konzentriere mich auf die Clientseite. Im Rahmen des Artikels entsteht exemplarisch eine Beispielanwendung, die auf einen REST Service zugreift. Die fertige App ist eine stark vereinfachte Software zur Verwaltung einer Büchersammlung.

REST-API für Testzwecke

Wer die Beispiele in diesem Artikel praktisch nachvollziehen möchte, benötigt dafür einen Web Service. Der Service für diese Beispielanwendung wird von einer Symfony-Applikation bereitgestellt, die über GitHub bereitsteht [3]. Sie kann, wie dort beschrieben eingerichtet werden. Das Symfony-Projekt speichert die Datensätze in einer sehr schlanken MySQL-Datenbank. Für die persistente Speicherung der Datensätze wird deshalb zwingend eine MySQL-Datenbank benötigt. Dort wird zu jedem Buch eine eindeutige ID, ein Buchtitel sowie der Autor gespeichert.

Ein neues XCode-Projekt

Der erste Schritt zu unserer Bibliotheks-App ist das Anlegen eines XCode-Projekts. Dazu wird einfach im Menü File | New der Punkt Project gewählt. Es öffnet sich ein Dialog, in dem gefragt wird, welche Vorlage für das Projekt verwendet werden soll (Abb. 1).

Abb. 1: Neues XCode-Projekt erzeugen

Wunderbar geeignet ist die Vorlage Single View Application aus der Kategorie iOS | APPLICATION. Anschließend wird im zweiten Schritt der Name des Projekts angegeben. Im dritten Schritt wird der Speicherort für das Projekt gewählt und bestätigt. Im letzten Schritt erzeugt XCode das neue Projekt und öffnet es automatisch. Nun ist das Projekt vorbereitet und AFNetworking kann installiert werden.

[ header = AFNetworking installieren ]

AFNetworking installieren

AFNetworking kann unkompliziert zu einem Projekt hinzugefügt werden. Die offizielle Dokumentation sowie der Getting Started Guide [4] beschreiben das notwendige Vorgehen perfekt. Daher hier nur noch eine Kurzfassung: Der erste Schritt besteht darin, AFNetworking herunterzuladen. Der Quellcode wird als ZIP-Archiv bereitgestellt. Dieses müssen Sie entpacken. Die AFNetworking-Dateien werden anschließend in XCode über FILE | ADD FILES TO ‚<PROJEKTNAME>‘ … in das Projekt eingebunden. Dazu einfach in dem sich öffnenden Dialog in dem soeben entpackten Verzeichnis das Unterverzeichnis AFNetworking auswählen und die Auswahl bestätigen. Schon ist AFNetworking eingebunden und kann verwendet werden.

Data-Transfer-Objekt erstellen

Es ist guter Stil, Datensätze innerhalb einer Anwendung mithilfe des Data-Transfer-Object-Entwurfsmusters zu repräsentieren, statt Arrays dafür zu verwenden. Dabei werden einfache Klassen ohne Logik als „Container“ für Datensätze verwendet. Neben Eigenschaften der Klassen sowie Methoden zur Initialisierung gibt es nur Get- und Set-Methoden. Wir verwenden die Klasse Book als Data Transfer Object, um die Bücher darstellen zu können. Dazu wird eine neue Datei vom Typ Objective-C class über den Menüpunkt FILE | NEW | FILE erzeugt. Als Klassenname wird Book angegeben und als Basisklasse NSObject gewählt.

Sobald XCode die Dateien erzeugt hat, werden im Interface der Klasse, mithilfe der @property-Anweisung, die drei Eigenschaften Id, Titel und Autor erzeugt. Die Get-/Set-Methoden müssen nicht (mehr) manuell implementiert werden. Das erledigt XCode, beziehungsweise der Präprozessor für uns. Anschließend sieht der fertige Header in der Datei Book.h wie in Listing 1 aus.

// Book.h
#import <Foundation/Foundation.h>

@interface Book : NSObject

@property NSInteger bookId;
@property (strong, nonatomic) NSString *title;
@property (strong, nonatomic) NSString *author;

- (id)initWithAttributes:(NSDictionary *)attributes;

@end

Nun folgt die Implementierung der Klasse in der Datei Book.m. Die einzige Methode ist der spezielle Konstruktur initWithAttributes. Diese Methode erzeugt auf Basis eines speziellen NSDictionary eine Instanz der Klasse, initialisiert die Eigenschaften entsprechend und gibt eine Referenz zurück (Listing 2).

// Book.m
#import "Book.h"

@implementation Book

- (id)initWithAttributes:(NSDictionary *)attributes
{ 
  self = [super init];
  if (!self) {
    return nil;
  }
  self.bookId = [[attributes valueForKeyPath:@"id"] intValue];
  self.title = [attributes valueForKeyPath:@"title"];
  self.author = [attributes valueForKeyPath:@"author"];
  return self;
}

@end 

Jedes Buch wird innerhalb der App durch diese Klasse repräsentiert, nachdem sie vom API abgerufen wurden. Um mit ihr kommunizieren zu können, werden jedoch noch ein Client sowie ein Ressourcen-Model benötigt, was uns zum nächsten Schritt der Implementierung führt.

[ header = Der API-Client ]

Der API-Client

Nun beginnt die Arbeit mit der AFNetworking-Bibliothek. Zunächst muss die Verbindung zum Webserver hergestellt werden. Mit anderen Worten: Es wird ein HTTP-Client benötigt. Der ist für die Verbindung zum Webserver zuständig. AFNetworking stellt die Klasse AFHTTPClient bereit. Von dieser wird in der Praxis meist eine Subklasse erstellt und in dieser das Singleton-Entwurfsmuster implementiert. So kann der HTTP-Client zentral konfiguriert und die einmalig erzeugte Clientinstanz immer wieder verwendet werden. Dafür erzeugen wir die Klasse API mit der Superklasse AFHTTPClient.

Bei der Kommunikation mit dem Webserver wird, speziell bei AFNetworking, das Blockkonzept von iOS sehr häufig verwendet. Wer es noch nicht kennt, sollte sich damit zuerst befassen. Ein gelungener Einstieg ist zum Beispiel der Artikel „Getting Started with Blocks“ in der iOS Developer Library [5]. Sehr grob zusammengefasst ähneln Blöcke anonymen Funktionen. Sie können zum Beispiel als Parameter an eine Methode übergeben werden und sind ganz besonders als callback-Methoden geeignet. Die definierte Programmlogik hat zum Zeitpunkt des Aufrufs Zugriff auf lokale Variablen, die an der Stelle der Blockdefinition existieren. So kann ein Block zum Beispiel im Controller definiert werden und bei der Ausführung auf die dortigen Variablen zugreifen. Dieses Verhalten wird zum Beispiel genutzt, um mit dem Benutzer zu kommunizieren und ihm Feedback zu geben.

Die Blocksyntax ist etwas eigenwillig und nicht unbedingt übersichtlich. Deshalb bietet es sich an, in der API-Klasse einige Blocksignaturen mittels typedef etwas lesbarer abzukürzen. Die fertige Klasse sieht dann wie in Listing 4 zu sehen aus. Zunächst der Header in der Datei API.h (Listing 3).

// API.h
#import "AFNetworking.h"
#import <UIKit/UIKit.h>

#define ApiURL @"http://localhost/restapi/web/app_dev.php/"

// error block typedef
typedef void (^APIFailureBlock)(NSString *errorMessage);
typedef void (^APISuccessBlock)();

@interface API : AFHTTPClient

+ (API *)sharedInstance;

@end

Neben dem oben beschriebenen Code wird zudem ApiURL definiert. Dort wird der Basis-URL des API angegeben. Bei der Initialisierung der Clientinstanz in der Methode sharedInstance wird sie ausgelesen. Listing 4 zeigt die Implementierung selbst.

// API.m
#import "API.h"

@implementation API

+ (API*)sharedInstance
{
  static API *sharedInstance = nil;
  static dispatch_once_t oncePredicate;
  dispatch_once(&oncePredicate, ^{
    sharedInstance = [[self alloc] initWithBaseURL:[NSURL URLWithString:ApiURL]];
  });
  [sharedInstance setDefaultHeader:@"Accept" value:@"application/json"];
  return sharedInstance;
}
- (id)initWithBaseURL:(NSURL *)url
{
  self = [super initWithBaseURL:url];
  if (self) {
    [self registerHTTPOperationClass:[AFJSONRequestOperation class]];
  }
  return self;
}

@end 

Die statische Methode sharedInstance implementiert das Singleton-Entwurfsmuster. In ihr wird, insofern nicht bereits geschehen, der HTTP-Client erzeugt. Über die Methode setDefaultHeader wird hier der Accept-HTTP-Header gesetzt. Dieser definiert, dass wir bei Antworten des API als Content-Type application/json erwarten. Jedes API bietet unterschiedliche Möglichkeiten. Theoretisch wäre zum Beispiel auch XML als Format denkbar. Wenn die Wahl besteht, wird der Wunsch über den Accept-Header signalisiert. Wichtig ist nur, dass dies natürlich bei der Implementierung berücksichtigt wird. Nachdem nun Verbindungen hergestellt werden können, müssen zusätzlich die CRUD-Methoden für unsere Applikation vorbereitet werden. Die werden im Ressourcen-Model gespeichert.

[ header = Ressourcen-Model für das API ]

Ressourcen-Model für das API

Das Ressourcen-Model ist das Herzstück der API-Anbindung. Es kapselt die Logik zur Kommunikation mit dem Web Service. Hier wird die Basis für die CRUD-Funktionalität der App bereitgestellt. Die Grundlage dafür bildet die Klasse API aus dem vorherigen Schritt. Theoretisch könnten die Zugriffe natürlich direkt in einem Controller implementiert werden. Das wäre aber schlechter Stil und verstößt zudem auch gegen das MVC-Entwurfsmuster. Bei allen daraus resultierenden Konsequenzen sprechen zwei Punkte ganz besonders dagegen: Die Wartbarkeit der gesamten App wird schlechter. Zudem ist es nicht mehr einfach möglich, das Ressourcen-Model einfach auszutauschen. Wenn sich das API ändert oder in Zukunft ein komplett neues Backend verwendet wird, müssten alle Anpassungen direkt im Controller gemacht werden. Das ist mehr als suboptimal.

In der Beispielanwendung gibt es zur Demonstration das Ressourcen-Model BookResource für Bücher. In einer realen App existieren meist mehr Entitäten wie zum Beispiel Benutzer, Produkte, Artikel, Bilder und Ähnliches. Dementsprechend werden dann auch mehrere Ressourcen-Model benötigt. Nur durch saubere Trennung bleibt der Quellcode strukturiert und übersichtlicher. Das Ressourcen-Model wird von der Klasse NSObject abgeleitet. Im Header sind die Signaturen der CRUD-Methoden bereits sichtbar. Zudem wird die Methode parseErrorMessage vorbereitet (Listing 5). Dieses wird für die Verarbeitung von Fehlern verwendet.

// BookResource.h
#import "API.h"
#import "Book.h"
#import <Foundation/Foundation.h>

typedef void (^BookSuccessBlock)(NSMutableArray *items);

@interface BookResource : NSObject

- (void)fetchBooks:(BookSuccessBlock)success
failure:(APIFailureBlock)failure;

- (void)deleteBook:(APISuccessBlock)success
failure:(APIFailureBlock)failure
book:(Book *)book;

- (void)createBook:(APISuccessBlock)success
failure:(APIFailureBlock)failure
book:(Book *)book;

- (void)updateBook:(APISuccessBlock)success
failure:(APIFailureBlock)failure
book:(Book *)book;

- (void)parseErrorMessage:(APIFailureBlock)failure
operation:(AFHTTPRequestOperation *)operation
error:(NSError *)error;
@end

Zu beachten ist vor allen Dingen der Typ des Parameters success. Dieser Block wird im Erfolgsfall ausgeführt. Da nur die Methode fetchBooks Daten zurückgibt, wird ihr der eigens definierte BookSuccessBlock übergeben. Er erwartet beim Aufruf als Parameter die gefundenen Buchdatensätze in Form eines Arrays. Los geht es mit der Implementierung der Methode fetchBooks (Listing 6).

// BookResource.m
#import "BookResource.h"

@implementation BookResource

- (void)fetchBooks:(BookSuccessBlock)success
failure:(APIFailureBlock)failure
{
  [[API sharedInstance] getPath:@"books"
    parameters:nil
    success:^(AFHTTPRequestOperation *operation, id responseObject) {
      NSMutableArray *books = [[NSMutableArray alloc] init];
      for (NSDictionary *attributes in [responseObject valueForKeyPath:@"books"]) {
        Book *book = [[Book alloc] initWithAttributes:attributes];
        [books addObject:book];
        book = nil;
      }
      success(books);
    }
    failure:^(AFHTTPRequestOperation *operation, NSError *error) {
      [self parseErrorMessage:failure operation:operation error:error]; 
  }];
}

// ...

@end 

Die Klasse AFHTTPClient, von der unsere Klasse API abgeleitet wird, bietet für GET-Anfragen die Methode getPath an. Dieser wird der aufzurufende URL-Pfad books übergeben. Intern wird dieser Pfad an den Basis-URL, der in der Datei API.h definiert wurde, angehangen. Über den Parameter parameters könnten zudem optional Parameter übergeben werden. Dies spielt hier keine Rolle, wird aber später noch einmal relevant.

Besonders interessant ist an der Stelle der Parameter success, über den ein Block an den HTTP-Client übergeben wird. Er wird ausgeführt, sobald die HTTP-Verbindung erfolgreich aufgebaut wurde. Dabei werden zwei Parameter übergeben: die ursprüngliche HTTP-Anfrage operation sowie die Antwort responseObject. Im success-Block selbst lesen wir alle Bücher aus der Antwort, dem responseObject, und erzeugen für jedes Buch eine Instanz der Klasse Book. Alle Bücher werden im Array books gesammelt und beim Aufruf des Blocks success(books) schließlich als Parameter übergeben. So werden die Datensätze von „innen“ nach „außen“ zum Frontend transportiert und an den Aufrufer, den BookViewController, übergeben. Der kümmert sich schließlich um die Darstellung.

Weiter geht es mit dem Löschen von Büchern. Die Logik dafür wird in Form der Methode deleteBook bereitgestellt (Listing 7).

- (void)deleteBook:(APISuccessBlock)success
failure:(APIFailureBlock)failure
book:(Book *)book
{
  NSString *apiPath = [NSString stringWithFormat:@"books/%d", book.bookId];
  [[API sharedInstance] deletePath:apiPath
    parameters:nil
    success:^(AFHTTPRequestOperation *operation, id responseObject) {
      success();
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
      [self parseErrorMessage:failure operation:operation error:error];
  }];
} 

Sie folgt dem gleichen Muster wie fetchBooks. Wesentlicher Unterschied hier ist jedoch ein abweichender Pfad im URL. Es wird eine bestimmte ID im URL übergeben. Zudem rufen wir deletePath statt getPath auf. Dadurch wird die DELETE-HTTP-Methode verwendet und dem API signalisiert, dass Daten gelöscht werden sollen. Zudem wird hier der generische APISuccessBlock ohne Parameter aufgerufen. Der Erfolg wird über den HTTP-Statuscode signalisiert. Ob die Anfrage erfolgreich war, wertet AFNetworking bereits aus. Im Fehlerfall wird stattdessen der failure-Block ausgeführt.

Die beiden letzten CRUD-Methoden verhalten sich nahezu identisch. Deshalb wird hier lediglich die Methode zum Speichern neuer Datensätze gezeigt. Der einzige Unterschied zwischen createBook und updateBook besteht in der verwendeten HTTP-Methode. Im REST-Kontext wird eine neue Entität durch einen POST-Request erzeugt. Deshalb nutzt createBook die Methode postPath. Ein Update wird mit der HTTP-Methode PUT signalisiert. Dementsprechend greift die Methode updateBook auf putPath zurück (Listing 8).

- (void)createBook:(APISuccessBlock)success
failure:(APIFailureBlock)failure
book:(Book *)book
{
  NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
  [NSString stringWithFormat:@"%ld", (long)book.bookId ], @"book[id]",
    book.title, @"book[title]",
    book.author, @"book[author]",
  nil];
  [[API sharedInstance] postPath:@"books"
    parameters:params
    success:^(AFHTTPRequestOperation *operation, id responseObject) {
      success();
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
      [self parseErrorMessage:failure operation:operation error:error];
  }];
}

Neu im Vergleich zu den bisherigen Anfragen ist, dass nun auch Parameter übergeben werden. Diese sendet AFNetworking zusammen mit der HTTP-Anfrage an den Server. Das Verhalten ist quasi wie bei einem Formular auf einer Webseite. Zuletzt noch die Methode zur Verarbeitung von Fehlern: Alle Methoden greifen auf sie zurück, wenn ein Fehler aufgetreten ist (Listing 9).

- (void)parseErrorMessage:(APIFailureBlock)failure
operation:(AFHTTPRequestOperation *)operation
error:(NSError *)error
{
  if ([operation.responseString length] > 0) {
    NSData *jsonData = [operation.responseString dataUsingEncoding:NSUTF8StringEncoding];
      NSDictionary *json = [NSJSONSerialization JSONObjectWithData:jsonData
      options:0
      error:nil];
    failure([json objectForKey:@"message"]);
  } else {
    failure([error localizedDescription]);
  }
} 

Generelle Fehler werden als NSError-Objekt übergeben und von der Methode parseErrorMessage ausgelesen. Das sind zum Beispiel Verbindungs- oder Serverfehler. Wenn ein logischer Fehler in der Webanwendung selbst auftritt und eine Fehlermeldung zurückgegeben wird, liest die Methode diese aus der Eigenschaft responseString aus. In jedem Fall wird der failure-Block aufgerufen. Diesem wird die Fehlermeldung übergeben, wodurch sie an der Stelle der Blockdefinition zur Verfügung steht. In unserem Fall ist dies später ein ViewController. Er zeigt dem Benutzer die Fehlermeldung im Frontend.

Nachdem nun die API-Anbindung selbst implementiert wurde, muss sie im Frontend integriert werden. Ich verwende dazu eine TableView, die einfach alle vorhandenen Buchdatensätze anzeigt. Aus der Tabelle können über den Bearbeitungsmodus einzelne Datensätze entfernt werden. In der Beispielanwendung wird zudem gezeigt, wie neue Datensätze eingefügt und bestehende aktualisiert werden können.

[ header = Ein TableViewController + Fazit ]

Ein TableViewController

Für die Darstellung einer Tabelle mit Büchern habe ich den TableViewController BookViewController erzeugt. Die Klasse habe ich nach dem gleichen Schema wie die oben beschriebene angelegt, allerdings als Subklasse von UITableViewController. Im Storyboard muss zudem der vorhandene ViewController entfernt und ein TableViewController hinzugefügt werden. Dieser wird im zweiten Schritt mit der Maus ausgewählt und über den Identity Inspector als Custom Class die Klasse BookViewController gewählt (Abb. 2).


Abb. 2: XCode Identity Inspector 

Die Tabelle selbst ist sehr einfach gehalten. Interessant ist speziell das Zusammenspiel mit dem API. Zu beachten ist dazu die Eigenschaft books der Klasse BookViewController. Sie ist vom Typ NSMutableArray und vorhanden, um alle Instanzen der Klasse Book vorzuhalten. Die Besonderheit dabei: Immer wenn das Array neu geschrieben wird, wird in der überschriebenen Methode setBooks die Tabelle neu geladen. Der Abruf der Bücher findet schließlich in der Methode viewDidAppear statt. Immer wenn die Tabelle erneut angezeigt wird, wird die Bücherliste neu geladen. Dazu wird die Methode loadBooks aufgerufen. In dieser wird das API angesprochen sowie die Blöcke definiert, die das Ressourcen-Model im Erfolgs- bzw. Fehlerfall ausführt (Listing 10).

- (void)viewDidAppear:(BOOL)animated
{
  [super viewDidAppear:animated];
  [self loadBooks];
}

- (void)loadBooks
{
  [SVProgressHUD show];
  BookResource *bookResource = [[BookResource alloc] init];
  [bookResource fetchBooks:^(NSMutableArray *items) {
    self.books = items;
    [SVProgressHUD dismiss];
  } failure:^(NSString *errorMessage) {
    [SVProgressHUD showErrorWithStatus:errorMessage];
  }];
} 

Die hier verwendete Klasse SVProgressHUD [6] wird zur Anzeige einer Ladeanimation genutzt. Zudem werden dem Benutzer darüber Meldungen bei Erfolg oder bei einem Fehler angezeigt. Um dem Benutzer das Löschen von Daten zu ermöglichen, benutzt die App den Edit-Modus der TableView. Sobald er aktiviert wurde, kann der Benutzer gezielt Datensätze löschen. Nach der Bestätigung wird die Methode commitEditingStyle im Controller aufgerufen. Dort wird über das Ressourcen-Model das Buch im Web Service gelöscht (Listing 11).

- (void)tableView:(UITableView *)tableView
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath
{
  [SVProgressHUD show];
  NSUInteger row = [indexPath row];
  Gallery *gallery = [self.tableData objectAtIndex:row];
  APIResource *apiResource = [[APIResource alloc] init];
  [apiResource deleteGallery:^{
    // success!
    [self.tableData removeObjectAtIndex:row];
    [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
      withRowAnimation:UITableViewRowAnimationAutomatic];
    [SVProgressHUD showSuccessWithStatus:@"Successfully removed!"];
  } failure:^(NSString *errorMessage) {
    [SVProgressHUD showErrorWithStatus:errorMessage];
  } gallery:gallery];
} 

Zudem wird auch gleichzeitig die Zeile aus der Tabelle entfernt. Was in diesem Artikel leider nicht vollständig erläutert werden kann, ist das Anlegen und Aktualisieren von vorhandenen Büchern. Dafür werden weitere ViewController und Übergänge zwischen den Views (Segues) benötigt. All dies würde den Rahmen komplett sprengen. Allerdings folgt die Integration einem ähnlichen Prinzip. Die vollständige Implementierung kann in dem Beispielprojekt auf GitHub [7] entnommen werden.

Wurde alles erfolgreich umgesetzt, sieht das Endergebnis in etwa wie in Abbildung 3 zu sehen aus.


Abb. 3: Die fertige Beispielanwendung

Fazit

Die AFNetworking-Bibliothek ist ein wunderbarer Helfer beim Zugriff und der Verarbeitung von Ressourcen aus dem Web. Die Integration ist sehr einfach und kann über eigene Models sehr sauber und strukturiert realisiert werden. Die hier vorgestellte Beispielanwendung ist grundlegend einsatzbereit und nur unwesentlich von einer App aus der Praxis entfernt. Dort wird die Netzwerklogik meist durch das Zusammenspiel mehrerer Entitäten komplexer, folgt aber der gleichen Logik. Zudem müsste beim Zugriff auf ein API in der realen Welt ein Mechanismus zur Authentifizierung implementiert werden. Hier gibt es mehrere Möglichkeiten wie bspw. OAuth, xwsse, ein API-Token oder Ähnliches. Die verschiedenen Authentifizierungsverfahren bieten jedoch fast genug Potenzial für einen ganz eigenen Artikel.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -