Vereinfachte RESTful-API-Nutzung und Datenpersistenz auf iOS mit Mantle und Realm

Veröffentlicht: 2022-03-11

Jeder iOS-Entwickler ist mit Core Data, einem Object-Graph- und Persistenz-Framework von Apple, vertraut. Abgesehen von der lokalen Beibehaltung von Daten verfügt das Framework über eine Vielzahl erweiterter Funktionen, wie z. B. die Nachverfolgung von Objektänderungen und das Rückgängigmachen. Diese Funktionen sind zwar in vielen Fällen nützlich, aber nicht kostenlos. Es erfordert viel Boilerplate-Code, und das Framework als Ganzes hat eine steile Lernkurve.

2014 wurde Realm, eine mobile Datenbank, veröffentlicht und eroberte die Entwicklungswelt im Sturm. Wenn wir nur Daten lokal speichern müssen, ist Realm eine gute Alternative. Schließlich erfordern nicht alle Anwendungsfälle die erweiterten Funktionen von Core Data. Realm ist extrem einfach zu bedienen und erfordert im Gegensatz zu Core Data nur sehr wenig Boilerplate-Code. Es ist außerdem Thread-sicher und soll schneller sein als das Persistenz-Framework von Apple.

In den meisten modernen mobilen Anwendungen lösen persistente Daten die Hälfte des Problems. Wir müssen oft Daten von einem Remote-Dienst abrufen, normalerweise über eine RESTful-API. Hier kommt Mantel ins Spiel. Es ist ein Open-Source-Modell-Framework für Cocoa und Cocoa Touch. Mantle vereinfacht das Schreiben von Datenmodellen für die Interaktion mit APIs, die JSON als Datenaustauschformat verwenden, erheblich.

Reich und Mantel für iOS

In diesem Artikel werden wir eine iOS-Anwendung erstellen, die eine Liste von Artikeln zusammen mit Links zu ihnen von der New York Times Article Search API v2 abruft. Die Liste wird mit einer standardmäßigen HTTP-GET-Anforderung abgerufen, wobei Anforderungs- und Antwortmodelle mit Mantle erstellt werden. Wir werden sehen, wie einfach es mit Mantle ist, Werttransformationen (zB von NSDate zu String) zu handhaben. Sobald die Daten abgerufen wurden, werden wir sie lokal mit Realm speichern. All dies mit minimalem Boilerplate-Code.

RESTful-API – Erste Schritte

Beginnen wir mit der Erstellung eines neuen Xcode-Projekts „Master-Detail-Anwendung“ für iOS mit dem Namen „RealmMantleTutorial“. Wir werden Frameworks mit CocoaPods hinzufügen. Die Pod-Datei sollte wie folgt aussehen:

 pod 'Mantle' pod 'Realm' pod 'AFNetworking'

Sobald die Pods installiert sind, können wir den neu erstellten MantleRealmTutorial- Arbeitsbereich öffnen. Wie Sie bemerkt haben, wurde auch das berühmte AFNetworking-Framework installiert. Wir werden es verwenden, um Anfragen an die API auszuführen.

Wie in der Einleitung erwähnt, bietet die New York Times eine hervorragende API für die Artikelsuche. Um es zu verwenden, muss man sich anmelden, um einen Zugangsschlüssel für die API zu erhalten. Dies kann unter http://developer.nytimes.com erfolgen. Mit dem API-Schlüssel in der Hand können wir mit dem Programmieren beginnen.

Bevor wir uns mit der Erstellung von Mantle-Datenmodellen befassen, müssen wir unsere Netzwerkschicht zum Laufen bringen. Lassen Sie uns eine neue Gruppe in Xcode erstellen und sie Network nennen. In dieser Gruppe erstellen wir zwei Klassen. Nennen wir den ersten SessionManager und stellen sicher, dass er von AFHTTPSessionManager abgeleitet ist, einer Session-Manager-Klasse von AFNetworking , dem wunderbaren Netzwerk-Framework. Unsere SessionManager -Klasse ist ein Singleton-Objekt, das wir verwenden, um Get-Anforderungen an die API auszuführen. Sobald die Klasse erstellt wurde, kopieren Sie bitte den folgenden Code in die Schnittstellen- bzw. Implementierungsdateien.

 #import "AFHTTPSessionManager.h" @interface SessionManager : AFHTTPSessionManager + (id)sharedManager; @end
 #import "SessionManager.h" static NSString *const kBaseURL = @"http://api.nytimes.com"; @implementation SessionManager - (id)init { self = [super initWithBaseURL:[NSURL URLWithString:kBaseURL]]; if(!self) return nil; self.responseSerializer = [AFJSONResponseSerializer serializer]; self.requestSerializer = [AFJSONRequestSerializer serializer]; return self; } + (id)sharedManager { static SessionManager *_sessionManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _sessionManager = [[self alloc] init]; }); return _sessionManager; } @end

Der Sitzungsmanager wird mit der Basis-URL initialisiert, die in der statischen kBaseURL- Variablen definiert ist. Es werden auch JSON-Anforderungs- und -Antwortserialisierer verwendet.

Nun heißt die zweite Klasse, die wir in der Netzwerkgruppe erstellen, APIManager . Sie soll von unserer neu erstellten SessionManager -Klasse abgeleitet werden. Sobald die erforderlichen Datenmodelle erstellt sind, fügen wir ApiManager eine Methode hinzu, die verwendet wird, um eine Liste von Artikeln von der API anzufordern.

API-Übersicht zur Artikelsuche der New York Times

Die offizielle Dokumentation für diese hervorragende API ist unter http://developer.nytimes.com/…/article_search_api_v2 verfügbar. Was wir tun werden, ist den folgenden Endpunkt zu verwenden:

 http://api.nytimes.com/svc/search/v2/articlesearch

… um Artikel abzurufen, die mit einem Suchbegriff unserer Wahl gefunden wurden, der durch einen Datumsbereich begrenzt ist. Zum Beispiel könnten wir die API bitten, eine Liste aller Artikel zurückzugeben, die in den ersten sieben Tagen des Juli 2015 in der New York Times erschienen sind und etwas mit Basketball zu tun hatten. Laut der API-Dokumentation, um das zu tun Wir müssen die folgenden Parameter in der Get-Anfrage an diesen Endpunkt setzen:

Parameter Wert
Q "Basketball"
Startdatum „20150701“
Endtermin „20150707“

Die Antwort der API ist ziemlich komplex. Nachfolgend finden Sie die Antwort für eine Anfrage mit den oben genannten Parametern, die auf nur einen Artikel (ein Element im Docs-Array) beschränkt sind, wobei zahlreiche Felder aus Gründen der Übersichtlichkeit weggelassen wurden.

 { "response": { "docs": [ { "web_url": "http://www.nytimes.com/2015/07/04/sports/basketball/robin-lopez-and-knicks-are-close-to-a-deal.html", "lead_paragraph": "Lopez, a 7-foot center, joined Arron Afflalo, a 6-foot-5 guard, as the Knicks' key acquisitions in free agency. He is expected to solidify the Knicks' interior defense.", "abstract": null, "print_page": "1", "source": "The New York Times", "pub_date": "2015-07-04T00:00:00Z", "document_type": "article", "news_desk": "Sports", "section_name": "Sports", "subsection_name": "Pro Basketball", "type_of_material": "News", "_id": "5596e7ac38f0d84c0655cb28", "word_count": "879" } ] }, "status": "OK", "copyright": "Copyright (c) 2013 The New York Times Company. All Rights Reserved." }

Was wir im Grunde als Antwort erhalten, sind drei Felder. Die erste namens response enthält das Array docs , das wiederum Elemente enthält, die Artikel darstellen. Die beiden anderen Felder sind status und copyright . Nachdem wir nun wissen, wie die API funktioniert, ist es an der Zeit, Datenmodelle mit Mantle zu erstellen.

Einführung in Mantel

Wie bereits erwähnt, ist Mantle ein Open-Source-Framework, das das Schreiben von Datenmodellen erheblich vereinfacht. Beginnen wir mit der Erstellung eines Artikellisten-Anforderungsmodells. Nennen wir diese Klasse ArticleListRequestModel und stellen Sie sicher, dass sie von MTLModel abgeleitet ist, einer Klasse, von der alle Mantle-Modelle abgeleitet werden sollten. Lassen Sie uns es außerdem dem MTLJSONSerializing- Protokoll anpassen. Unser Anforderungsmodell sollte drei Eigenschaften geeigneter Typen haben: query, articleFromDate und articleToDate . Nur um sicherzustellen, dass unser Projekt gut organisiert ist, schlage ich vor, dass diese Klasse in der Gruppe Models platziert wird.

Mantle vereinfacht das Schreiben von Datenmodellen und reduziert Boilerplate-Code.
Twittern

So sollte die Schnittstellendatei von ArticleListRequestModel aussehen:

 #import "MTLModel.h" #import "Mantle.h" @interface ArticleListRequestModel : MTLModel <MTLJSONSerializing> @property (nonatomic, copy) NSString *query; @property (nonatomic, copy) NSDate *articlesFromDate; @property (nonatomic, copy) NSDate *articlesToDate; @end

Wenn wir nun die Dokumente für unseren Endpunkt für die Artikelsuche nachschlagen oder einen Blick auf die Tabelle mit den Anforderungsparametern oben werfen, werden wir feststellen, dass sich die Namen der Variablen in der API-Anforderung von denen in unserem Anforderungsmodell unterscheiden. Mantle handhabt dies effizient mit der Methode:

 + (NSDictionary *)JSONKeyPathsByPropertyKey.

So sollte diese Methode in der Implementierung unseres Anfragemodells implementiert werden:

 #import "ArticleListRequestModel.h" @implementation ArticleListRequestModel #pragma mark - Mantle JSONKeyPathsByPropertyKey + (NSDictionary *)JSONKeyPathsByPropertyKey { return @{ @"query": @"q", @"articlesFromDate": @"begin_date", @"articlesToDate": @"end_date" }; } @end

Die Implementierung dieser Methode gibt an, wie die Eigenschaften des Modells seinen JSON-Darstellungen zugeordnet werden. Sobald die Methode JSONKeyPathsByPropertyKey implementiert wurde, können wir mit der Klassenmethode +[MTLJSONAdapter JSONArrayForModels:] eine JSON-Wörterbuchdarstellung des Modells erhalten.

Eine Sache, die noch übrig bleibt, wie wir aus der Liste der Parameter wissen, ist, dass die beiden Datumsparameter das Format „JJJJMMTT“ haben müssen. Hier wird Mantle sehr praktisch. Wir können eine benutzerdefinierte Werttransformation für jede Eigenschaft hinzufügen, indem wir die optionale Methode +<propertyName>JSONTransformer . Durch die Implementierung teilen wir Mantle mit, wie der Wert eines bestimmten JSON-Felds während der JSON-Deserialisierung transformiert werden soll. Wir können auch einen reversiblen Transformator implementieren, der beim Erstellen eines JSON aus dem Modell verwendet wird. Da wir ein NSDate -Objekt in einen String umwandeln müssen, verwenden wir auch die Klasse NSDataFormatter . Hier ist die vollständige Implementierung der ArticleListRequestModel -Klasse:

 #import "ArticleListRequestModel.h" @implementation ArticleListRequestModel + (NSDateFormatter *)dateFormatter { NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateFormat = @"yyyyMMdd"; return dateFormatter; } #pragma mark - Mantle JSONKeyPathsByPropertyKey + (NSDictionary *)JSONKeyPathsByPropertyKey { return @{ @"query": @"q", @"articlesFromDate": @"begin_date", @"articlesToDate": @"end_date" }; } #pragma mark - JSON Transformers + (NSValueTransformer *)articlesToDateJSONTransformer { return [MTLValueTransformer transformerUsingForwardBlock:^id(NSString *dateString, BOOL *success, NSError *__autoreleasing *error) { return [self.dateFormatter dateFromString:dateString]; } reverseBlock:^id(NSDate *date, BOOL *success, NSError *__autoreleasing *error) { return [self.dateFormatter stringFromDate:date]; }]; } + (NSValueTransformer *)articlesFromDateJSONTransformer { return [MTLValueTransformer transformerUsingForwardBlock:^id(NSString *dateString, BOOL *success, NSError *__autoreleasing *error) { return [self.dateFormatter dateFromString:dateString]; } reverseBlock:^id(NSDate *date, BOOL *success, NSError *__autoreleasing *error) { return [self.dateFormatter stringFromDate:date]; }]; } @end

Ein weiteres großartiges Feature von Mantle ist, dass alle diese Modelle dem NSCoding- Protokoll entsprechen und isEqual- und Hash -Methoden implementieren.

Wie wir bereits gesehen haben, enthält das resultierende JSON aus dem API-Aufruf ein Array von Objekten, die Artikel darstellen. Wenn wir diese Reaktion mit Mantle modellieren wollen, müssen wir zwei separate Datenmodelle erstellen. Einer würde Objekte modellieren, die Artikel darstellen ( docs -Array-Elemente), und der andere würde die gesamte JSON-Antwort mit Ausnahme der Elemente des docs-Arrays modellieren. Jetzt müssen wir nicht jede einzelne Eigenschaft aus dem eingehenden JSON in unsere Datenmodelle abbilden. Nehmen wir an, wir interessieren uns nur für zwei Felder von Artikelobjekten, und das wären lead_paragraph und web_url . Die ArticleModel -Klasse ist ziemlich einfach zu implementieren, wie wir unten sehen können.

 #import "MTLModel.h" #import <Mantle/Mantle.h> @interface ArticleModel : MTLModel <MTLJSONSerializing> @property (nonatomic, copy) NSString *leadParagraph; @property (nonatomic, copy) NSString *url; @end
 #import "ArticleModel.h" @implementation ArticleModel #pragma mark - Mantle JSONKeyPathsByPropertyKey + (NSDictionary *)JSONKeyPathsByPropertyKey { return @{ @"leadParagraph": @"lead_paragraph", @"url": @"web_url" }; } @end

Nachdem das Artikelmodell definiert wurde, können wir die Definition des Antwortmodells abschließen, indem wir ein Modell für die Artikelliste erstellen. So wird das Antwortmodell der Klasse ArticleList aussehen.

 #import "MTLModel.h" #import <Mantle/Mantle.h> #import "ArticleModel.h" @interface ArticleListResponseModel : MTLModel <MTLJSONSerializing> @property (nonatomic, copy) NSArray *articles; @property (nonatomic, copy) NSString *status; @end
 #import "ArticleListResponseModel.h" @class ArticleModel; @implementation ArticleListResponseModel #pragma mark - Mantle JSONKeyPathsByPropertyKey + (NSDictionary *)JSONKeyPathsByPropertyKey { return @{ @"articles" : @"response.docs", @"status" : @"status" }; } #pragma mark - JSON Transformer + (NSValueTransformer *)articlesJSONTransformer { return [MTLJSONAdapter arrayTransformerWithModelClass:ArticleModel.class]; } @end

Diese Klasse hat nur zwei Eigenschaften: Status und Artikel . Wenn wir es mit der Antwort vom Endpunkt vergleichen, sehen wir, dass das dritte JSON-Attribut copyright nicht im Antwortmodell abgebildet wird. Wenn wir uns die Methode ArticlesJSONTransformer ansehen, sehen wir, dass sie einen Werttransformator für ein Array zurückgibt, das Objekte der Klasse ArticleModel enthält.

Es ist auch erwähnenswert, dass in der Methode JSONKeyPathsByPropertyKey die Modelleigenschaftsartikel den Array-Dokumenten entsprechen , die in der JSON-Attributantwort verschachtelt sind.

Inzwischen sollten wir drei Modellklassen implementiert haben: ArticleListRequestModel, ArticleModel und ArticleListResponseModel.

Erste API-Anfrage

Restful-API

Nachdem wir nun alle Datenmodelle implementiert haben, ist es an der Zeit, zur Klasse APIManager zurückzukehren , um die Methode zu implementieren, die wir verwenden werden, um GET-Anforderungen an die API auszuführen. Die Methode:

 - (NSURLSessionDataTask *) getArticlesWithRequestModel:(ArticleListRequestModel *)requestModel success:(void (^)(ArticleListResponseModel *responseModel))success failure:(void (^)(NSError *error))failure

nimmt ein ArticleListRequestModel -Anforderungsmodell als Parameter und gibt im Erfolgsfall ein ArticleListResponseModel oder andernfalls einen NSError zurück. Die Implementierung dieser Methode verwendet AFNetworking , um eine GET-Anforderung an die API auszuführen. Bitte beachten Sie, dass wir für eine erfolgreiche API-Anfrage einen Schlüssel bereitstellen müssen, den Sie, wie bereits erwähnt, erhalten können, indem Sie sich unter http://developer.nytimes.com registrieren.

 #import "SessionManager.h" #import "ArticleListRequestModel.h" #import "ArticleListResponseModel.h" @interface APIManager : SessionManager - (NSURLSessionDataTask *)getArticlesWithRequestModel:(ArticleListRequestModel *)requestModel success:(void (^)(ArticleListResponseModel *responseModel))success failure:(void (^)(NSError *error))failure; @end
 #import "APIManager.h" #import "Mantle.h" static NSString *const kArticlesListPath = @"/svc/search/v2/articlesearch.json"; static NSString *const kApiKey = @"replace this with your own key"; @implementation APIManager - (NSURLSessionDataTask *)getArticlesWithRequestModel:(ArticleListRequestModel *)requestModel success:(void (^)(ArticleListResponseModel *responseModel))success failure:(void (^)(NSError *error))failure{ NSDictionary *parameters = [MTLJSONAdapter JSONDictionaryFromModel:requestModel error:nil]; NSMutableDictionary *parametersWithKey = [[NSMutableDictionary alloc] initWithDictionary:parameters]; [parametersWithKey setObject:kApiKey forKey:@"api-key"]; return [self GET:kArticlesListPath parameters:parametersWithKey success:^(NSURLSessionDataTask *task, id responseObject) { NSDictionary *responseDictionary = (NSDictionary *)responseObject; NSError *error; ArticleListResponseModel *list = [MTLJSONAdapter modelOfClass:ArticleListResponseModel.class fromJSONDictionary:responseDictionary error:&error]; success(list); } failure:^(NSURLSessionDataTask *task, NSError *error) { failure(error); }]; }

Bei der Implementierung dieser Methode geschehen zwei sehr wichtige Dinge. Schauen wir uns zunächst diese Zeile an:

 NSDictionary *parameters = [MTLJSONAdapter JSONDictionaryFromModel:requestModel error:nil];

Was hier passiert, ist, dass wir mit der von der MTLJSONAdapter -Klasse bereitgestellten Methode eine NSDictionary -Darstellung unseres Datenmodells erhalten. Diese Darstellung spiegelt den JSON wider, der an die API gesendet wird. Darin liegt die Schönheit von Mantle. Nachdem wir die JSONKeyPathsByPropertyKey- und +<propertyName>JSONTransformer Methoden in der ArticleListRequestModel-Klasse implementiert haben, können wir in kürzester Zeit mit nur einer einzigen Codezeile die korrekte JSON-Darstellung unseres Datenmodells erhalten.

Mantle erlaubt uns auch, Transformationen in die andere Richtung durchzuführen. Und genau das passiert mit den von der API empfangenen Daten. Das NSDictionary, das wir erhalten, wird mithilfe der folgenden Klassenmethode einem Objekt der Klasse ArticleListResponseModel zugeordnet:

 ArticleListResponseModel *list = [MTLJSONAdapter modelOfClass:ArticleListResponseModel.class fromJSONDictionary:responseDictionary error:&error];

Persistente Daten mit Realm

Jetzt, da wir Daten von einer Remote-API abrufen können, ist es an der Zeit, sie zu speichern. Wie in der Einleitung erwähnt, werden wir dies mit Realm tun. Realm ist eine mobile Datenbank und ein Ersatz für Core Data und SQLite. Wie wir weiter unten sehen werden, ist es extrem einfach zu bedienen.

Realm, die ultimative mobile Datenbank, ist ein perfekter Ersatz für Core Data und SQLite.
Twittern

Um ein Datenelement in Realm zu speichern, müssen wir zuerst ein Objekt kapseln, das von der RLMObject-Klasse abgeleitet ist. Was wir jetzt tun müssen, ist, eine Modellklasse zu erstellen, die Daten für einzelne Artikel speichert. So einfach ist es, eine solche Klasse zu erstellen.

 #import "RLMObject.h" @interface ArticleRealm : RLMObject @property NSString *leadParagraph; @property NSString *url; @end

Und das könnte es im Grunde sein, die Implementierung dieser Klasse könnte leer bleiben. Bitte beachten Sie, dass die Eigenschaften in der Modellklasse keine Attribute wie nonatomic, strong oder copy haben. Realm kümmert sich um diese und wir brauchen uns darum keine Sorgen zu machen.

Da die Artikel, die wir erhalten können, mit dem Mante-Modell Article modelliert sind, wäre es praktisch, ArticleRealm- Objekte mit Objekten der Klasse Article zu initialisieren. Dazu fügen wir unserem Realm-Modell die Methode initWithMantleModel hinzu. Hier ist die vollständige Implementierung der ArticleRealm -Klasse.

 #import "RLMObject.h" #import "ArticleModel.h" @interface ArticleRealm : RLMObject @property NSString *leadParagraph; @property NSString *url; - (id)initWithMantleModel:(ArticleModel *)articleModel; @end
 #import "ArticleRealm.h" @implementation ArticleRealm - (id)initWithMantleModel:(ArticleModel *)articleModel{ self = [super init]; if(!self) return nil; self.leadParagraph = articleModel.leadParagraph; self.url = articleModel.url; return self; } @end

Wir interagieren mit der Datenbank unter Verwendung von Objekten der Klasse RLMRealm . Wir können ein RLMRealm- Objekt leicht erhalten, indem wir die Methode „[RLMRealm defaultRealm]“ aufrufen. Es ist wichtig, sich daran zu erinnern, dass ein solches Objekt nur innerhalb des Threads gültig ist, in dem es erstellt wurde, und nicht über Threads hinweg geteilt werden kann. Das Schreiben von Daten in Realm ist ziemlich einfach. Innerhalb einer Schreibtransaktion muss ein einzelner Schreibvorgang oder eine Reihe davon ausgeführt werden. Hier ist ein Beispiel für das Schreiben in die Datenbank:

 RLMRealm *realm = [RLMRealm defaultRealm]; ArticleRealm *articleRealm = [ArticleRealm new]; articleRealm.leadParagraph = @"abc"; articleRealm.url = @"sampleUrl"; [realm beginWriteTransaction]; [realm addObject:articleRealm]; [realm commitWriteTransaction];

Was hier passiert ist folgendes. Zuerst erstellen wir ein RLMRealm- Objekt, um mit der Datenbank zu interagieren. Dann wird ein ArticleRealm -Modellobjekt erstellt (bitte beachten Sie, dass es von der RLMRealm -Klasse abgeleitet ist). Um es schließlich zu speichern, beginnt eine Schreibtransaktion, das Objekt wird der Datenbank hinzugefügt, und sobald es gespeichert ist, wird die Schreibtransaktion festgeschrieben. Wie wir sehen können, blockieren Schreibtransaktionen den Thread, in dem sie aufgerufen werden. Obwohl Realm als sehr schnell bezeichnet wird, könnte das Hinzufügen mehrerer Objekte zur Datenbank innerhalb einer einzigen Transaktion im Hauptthread dazu führen, dass die Benutzeroberfläche nicht mehr reagiert, bis die Transaktion abgeschlossen ist. Eine natürliche Lösung dafür besteht darin, eine solche Schreibtransaktion in einem Hintergrund-Thread durchzuführen.

API-Anforderung und dauerhafte Antwort im Bereich

Dies sind alle Informationen, die wir benötigen, um Artikel mit Realm zu speichern. Lassen Sie uns versuchen, eine API-Anforderung mit der Methode auszuführen

 - (NSURLSessionDataTask *) getArticlesWithRequestModel:(ArticleListRequestModel *)requestModel success:(void (^)(ArticleListResponseModel *responseModel))success failure:(void (^)(NSError *error))failure

und Mantle Anforderungs- und Antwortmodelle, um Artikel der New York Times zu erhalten, die (wie im vorherigen Beispiel) irgendetwas mit Basketball zu tun hatten und in den ersten sieben Tagen des Juni 2015 veröffentlicht wurden. Sobald die Liste solcher Artikel verfügbar ist, werden wir wird es in Realm beibehalten. Unten ist der Code, der das tut. Es wird in der viewDidLoad- Methode des Table View-Controllers in unserer App platziert.

 ArticleListRequestModel *requestModel = [ArticleListRequestModel new]; // (1) requestModel.query = @"Basketball"; requestModel.articlesToDate = [[ArticleListRequestModel dateFormatter] dateFromString:@"20150706"]; requestModel.articlesFromDate = [[ArticleListRequestModel dateFormatter] dateFromString:@"20150701"]; [[APIManager sharedManager] getArticlesWithRequestModel:requestModel // (2) success:^(ArticleListResponseModel *responseModel){ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // (3) @autoreleasepool { RLMRealm *realm = [RLMRealm defaultRealm]; [realm beginWriteTransaction]; [realm deleteAllObjects]; [realm commitWriteTransaction]; [realm beginWriteTransaction]; for(ArticleModel *article in responseModel.articles){ ArticleRealm *articleRealm = [[ArticleRealm alloc] initWithMantleModel:article]; // (4) [realm addObject:articleRealm]; } [realm commitWriteTransaction]; dispatch_async(dispatch_get_main_queue(), ^{ // (5) RLMRealm *realmMainThread = [RLMRealm defaultRealm]; // (6) RLMResults *articles = [ArticleRealm allObjectsInRealm:realmMainThread]; self.articles = articles; // (7) [self.tableView reloadData]; }); } }); } failure:^(NSError *error) { self.articles = [ArticleRealm allObjects]; [self.tableView reloadData]; }];

Zuerst erfolgt ein API-Aufruf (2) mit einem Anforderungsmodell (1), das ein Antwortmodell zurückgibt, das eine Liste von Artikeln enthält. Um diese Artikel mit Realm beizubehalten, müssen wir Realm-Modellobjekte erstellen, was in der for-Schleife (4) stattfindet. Es ist auch wichtig zu beachten, dass, da mehrere Objekte in einer einzigen Schreibtransaktion beibehalten werden, diese Schreibtransaktion in einem Hintergrund-Thread ausgeführt wird (3). Nachdem nun alle Artikel in Realm gespeichert sind, weisen wir sie der Klasseneigenschaft self.articles (7) zu. Da später im Haupt-Thread in TableView-Datenquellenmethoden auf sie zugegriffen wird, ist es sicher, sie auch im Haupt-Thread aus der Realm-Datenbank abzurufen (5). Auch hier muss für den Zugriff auf die Datenbank von einem neuen Thread ein neues RLMRealm-Objekt in diesem Thread erstellt (6) werden.

Wenn das Abrufen neuer Artikel von der API aus irgendeinem Grund fehlschlägt, werden die vorhandenen aus dem lokalen Speicher im Fehlerblock abgerufen.

Einpacken

In diesem Tutorial haben wir gelernt, wie man Mantle, ein Modell-Framework für Cocoa und Cocoa Touch, konfiguriert, um mit einer Remote-API zu interagieren. Wir haben auch gelernt, wie man Daten, die in Form von Mantle-Modellobjekten abgerufen wurden, mithilfe der mobilen Realm-Datenbank lokal speichern kann.

Falls Sie diese Anwendung ausprobieren möchten, können Sie den Quellcode aus dem GitHub-Repository abrufen. Sie müssen Ihren eigenen API-Schlüssel generieren und bereitstellen, bevor Sie die Anwendung ausführen.