Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Core Data

Kerndaten und Swift: Asynchrones Abrufen

by
Read Time:11 minsLanguages:
This post is part of a series called Core Data and Swift.
Core Data and Swift: Batch Deletes

German (Deutsch) translation by Katharina Grigorovich-Nevolina (you can also view the original English article)

In den vorherigen Abschnitten haben wir über Stapelaktualisierungen und Stapellöschungen gesprochen. In diesem Tutorial werden wir uns genauer ansehen, wie asynchrones Abrufen implementiert wird und in welchen Situationen Ihre Anwendung von dieser neuen API profitieren kann.

1. Das Problem

Wie Batch-Updates steht auch das asynchrone Abrufen seit geraumer Zeit auf der Wunschliste vieler Entwickler. Abrufanforderungen können komplex sein und eine nicht triviale Zeit in Anspruch nehmen. Während dieser Zeit blockiert die Abrufanforderung den Thread, auf dem sie ausgeführt wird, und blockiert daher den Zugriff auf den verwalteten Objektkontext, in dem die Abrufanforderung ausgeführt wird. Das Problem ist einfach zu verstehen, aber wie sieht die Lösung von Apple aus?

2. Die Lösung

Apples Antwort auf dieses Problem ist das asynchrone Abrufen. Eine asynchrone Abrufanforderung wird im Hintergrund ausgeführt. Dies bedeutet, dass andere Aufgaben während der Ausführung nicht blockiert werden, z. B. das Aktualisieren der Benutzeroberfläche im Hauptthread.

Das asynchrone Abrufen bietet außerdem zwei weitere praktische Funktionen: Fortschrittsberichterstattung und Stornierung. Eine asynchrone Abrufanforderung kann jederzeit abgebrochen werden, beispielsweise wenn der Benutzer entscheidet, dass der Abschluss der Abrufanforderung zu lange dauert. Die Fortschrittsberichterstattung ist eine nützliche Ergänzung, um dem Benutzer den aktuellen Status der Abrufanforderung anzuzeigen.

Asynchrones Abrufen ist eine flexible API. Es ist nicht nur möglich, eine asynchrone Abrufanforderung abzubrechen, sondern auch Änderungen am Kontext des verwalteten Objekts vorzunehmen, während die asynchrone Abrufanforderung ausgeführt wird. Mit anderen Worten, der Benutzer kann Ihre Anwendung weiterhin verwenden, während die Anwendung im Hintergrund eine asynchrone Abrufanforderung ausführt.

3. Wie funktioniert es?

Wie bei Stapelaktualisierungen werden asynchrone Abrufanforderungen als NSPersistentStoreRequest-Objekt an den Kontext des verwalteten Objekts übergeben, genauer gesagt als Instanz der NSAsynchronousFetchRequest-Klasse.

Eine NSAsynchronousFetchRequest-Instanz wird mit einem NSFetchRequest-Objekt und einem Abschlussblock initialisiert. Der Abschlussblock wird ausgeführt, wenn die asynchrone Abrufanforderung ihre Abrufanforderung abgeschlossen hat.

Lassen Sie uns die Aufgabenanwendung, die wir zuvor in dieser Reihe erstellt haben, erneut betrachten und die aktuelle Implementierung der NSFetchedResultsController-Klasse durch eine asynchrone Abrufanforderung ersetzen.

Schritt 1: Projekteinrichtung

Laden Sie das Projekt von GitHub herunter oder klonen Sie es und öffnen Sie es in Xcode 7. Bevor wir mit der NSAsynchronousFetchRequest-Klasse arbeiten können, müssen wir einige Änderungen vornehmen. Wir können die NSFetchedResultsController-Klasse nicht zum Verwalten der Daten der Tabellenansicht verwenden, da die NSFetchedResultsController-Klasse für die Ausführung auf dem Hauptthread konzipiert wurde.

Schritt 2: Ersetzen des Controllers für abgerufene Ergebnisse

Aktualisieren Sie zunächst die ViewController-Klasse wie unten gezeigt. Wir entfernen die Eigenschaft fetchedResultsController und erstellen eine neue Eigenschaft, items, vom Typ [Item] zum Speichern der zu erledigenden Elemente. Dies bedeutet auch, dass die ViewController-Klasse nicht mehr dem NSFetchedResultsControllerDelegate-Protokoll entsprechen muss.

Bevor wir die viewDidLoad()-Methode umgestalten, möchte ich zunächst die Implementierung des UITableViewDataSource-Protokolls aktualisieren. Schauen Sie sich die Änderungen an, die ich vorgenommen habe.

Wir müssen auch eine Codezeile in der Methode prepareForSegue(_:sender:) ändern, wie unten gezeigt.

Löschen Sie zu guter Letzt die Implementierung des NSFetchedResultsControllerDelegate-Protokolls, da wir es nicht mehr benötigen.

Schritt 3: Erstellen der asynchronen Abrufanforderung

Wie Sie unten sehen können, erstellen wir die asynchrone Abrufanforderung in der viewDidLoad()-Methode des Ansichts-Controllers. Nehmen wir uns einen Moment Zeit, um zu sehen, was los ist.

Zunächst erstellen und konfigurieren wir eine NSFetchRequest-Instanz, um die asynchrone Abrufanforderung zu initialisieren. Es ist diese Abrufanforderung, die die asynchrone Abrufanforderung im Hintergrund ausführen wird.

Um eine NSAsynchronousFetchRequest-Instanz zu initialisieren, rufen wir init(request:CompletionBlock:) auf und übergeben fetchRequest und einen Completion-Block.

Der Abschlussblock wird aufgerufen, wenn die asynchrone Abrufanforderung die Ausführung ihrer Abrufanforderung abgeschlossen hat. Der Abschlussblock verwendet ein Argument vom Typ NSAsynchronousFetchResult, das das Ergebnis der Abfrage sowie einen Verweis auf die ursprüngliche asynchrone Abrufanforderung enthält.

Im Abschlussblock rufen wir processAsynchronousFetchResult (_:) auf und übergeben das NSAsynchronousFetchResult-Objekt. Wir werden uns in wenigen Augenblicken diese Hilfsmethode ansehen.

Das Ausführen der asynchronen Abrufanforderung ist fast identisch mit dem Ausführen einer NSBatchUpdateRequest. Wir rufen executeRequest(_:) im Kontext des verwalteten Objekts auf und übergeben die asynchrone Abrufanforderung.

Obwohl die asynchrone Abrufanforderung im Hintergrund ausgeführt wird, beachten Sie, dass die Methode executeRequest(_:) sofort zurückgegeben wird und uns ein NSAsynchronousFetchResult-Objekt übergibt. Sobald die asynchrone Abrufanforderung abgeschlossen ist, wird dasselbe NSAsynchronousFetchResult-Objekt mit dem Ergebnis der Abrufanforderung gefüllt.

Denken Sie aus dem vorherigen Tutorial daran, dass executeRequest(_:) eine Wurfmethode ist. Wir fangen alle Fehler in der catch-Klausel der do-catch-Anweisung ab und drucken sie zum Debuggen auf die Konsole.

Schritt 4: Verarbeiten des asynchronen Abrufergebnisses

Die processAsynchronousFetchResult(_:)-Methode ist nichts anderes als eine Hilfsmethode, in der wir das Ergebnis der asynchronen Abrufanforderung verarbeiten. Wir setzen die items-Eigenschaft des View-Controllers mit dem Inhalt der finalResult-Eigenschaft des Ergebnisses und laden die Tabellenansicht neu.

Schritt 5: Erstellen und Ausführen

Erstellen Sie das Projekt und führen Sie die Anwendung im iOS-Simulator aus. Wenn Ihre Anwendung abstürzt, wenn sie versucht, die asynchrone Abrufanforderung auszuführen, verwenden Sie möglicherweise eine API, die ab iOS 9 (und OS X El Capitan) veraltet ist.

In Core Data und Swift: Concurrency habe ich die verschiedenen Parallelitätstypen erläutert, die ein verwalteter Objektkontext haben kann. Ab iOS 9 (und OS X El Capitan) ist der ConfinementConcurrencyType veraltet. Gleiches gilt für die init()-Methode der NSManagedObjectContext-Klasse, da eine Instanz mit dem Parallelitätstyp ConfinementConcurrencyType erstellt wird.

Wenn Ihre Anwendung abstürzt, verwenden Sie höchstwahrscheinlich einen verwalteten Objektkontext mit einem ConfinementConcurrencyType-Parallelitätstyp, der das asynchrone Abrufen nicht unterstützt. Zum Glück ist die Lösung einfach. Erstellen Sie einen verwalteten Objektkontext mit dem angegebenen Initialisierer init(concurrencyType:) und übergeben Sie MainQueueConcurrencyType oder PrivateQueueConcurrencyType als Parallelitätstyp.

4. Fortschritt anzeigen

Die NSAsynchronousFetchRequest-Klasse bietet Unterstützung für die Überwachung des Fortschritts der Abrufanforderung und es ist sogar möglich, eine asynchrone Abrufanforderung abzubrechen, wenn der Benutzer beispielsweise feststellt, dass der Abschluss zu lange dauert.

Die NSAsynchronousFetchRequest-Klasse nutzt die NSProgress-Klasse für die Fortschrittsberichterstattung sowie für das Abbrechen einer asynchronen Abrufanforderung. Die NSProgress-Klasse, die seit iOS 7 und OS X Mavericks verfügbar ist, ist eine clevere Möglichkeit, den Fortschritt einer Aufgabe zu überwachen, ohne die Aufgabe eng an die Benutzeroberfläche koppeln zu müssen.

Die NSProgress-Klasse unterstützt auch das Abbrechen. Auf diese Weise kann eine asynchrone Abrufanforderung abgebrochen werden. Lassen Sie uns herausfinden, was wir tun müssen, um die Fortschrittsberichterstattung für die asynchrone Abrufanforderung zu implementieren.

Schritt 1: Hinzufügen von SVProgressHUD

Wir zeigen dem Benutzer den Fortschritt der asynchronen Abrufanforderung mithilfe der SVProgressHUD-Bibliothek von Sam Vermette. Der einfachste Weg, dies zu erreichen, sind CocoaPods. So sieht das Podfile des Projekts aus.

Führen Sie pod install über die Befehlszeile aus und vergessen Sie nicht, den von CocoaPods für Sie erstellten Arbeitsbereich anstelle des Xcode-Projekts zu öffnen.

Schritt 2: Einrichten von NSProgress

In diesem Artikel werden wir die NSProgress-Klasse nicht im Detail untersuchen, aber Sie können gerne mehr darüber in der Apple-Dokumentation lesen. Wir erstellen eine NSProgress-Instanz in der viewDidLoad()-Methode des View Controllers, bevor wir die asynchrone Abrufanforderung ausführen.

Sie werden überrascht sein, dass wir die Gesamtzahl der Einheiten auf 1 setzen. Der Grund ist einfach. Wenn Core Data die asynchrone Abrufanforderung ausführt, weiß es nicht, wie viele Datensätze im persistenten Speicher gefunden werden. Dies bedeutet auch, dass wir dem Benutzer den relativen Fortschritt nicht anzeigen können - einen Prozentsatz. Stattdessen zeigen wir dem Benutzer den absoluten Fortschritt - die Anzahl der gefundenen Datensätze.

Sie können dieses Problem beheben, indem Sie eine Abrufanforderung ausführen, um die Anzahl der Datensätze abzurufen, bevor Sie die asynchrone Abrufanforderung ausführen. Ich ziehe es jedoch vor, dies nicht zu tun, da dies auch bedeutet, dass das Abrufen der Datensätze aus dem persistenten Speicher aufgrund der zusätzlichen Abrufanforderung zu Beginn länger dauert.

Schritt 3: Hinzufügen eines Beobachters

Wenn wir die asynchrone Abrufanforderung ausführen, wird uns sofort ein NSAsynchronousFetchResult-Objekt übergeben. Dieses Objekt verfügt über eine progress-Eigenschaft vom Typ NSProgress. Diese progress-Eigenschaft müssen wir beobachten, wenn wir Fortschrittsaktualisierungen erhalten möchten.

Beachten Sie, dass wir resignCurrent für das progress-Objekt aufrufen, um den früheren Aufruf von becomeCurrentWithPendingUnitCount: auszugleichen. Beachten Sie, dass beide Methoden im selben Thread aufgerufen werden müssen.

Schritt 4: Entfernen des Beobachters

Im Abschlussblock der asynchronen Abrufanforderung entfernen wir den Beobachter und schließen das Fortschritts-HUD ab.

Bevor wir observeValueForKeyPath (_:ofObject:change:context:) implementieren, müssen wir den Fortschritts-HUD anzeigen, bevor wir die asynchrone Abrufanforderung erstellen.

Schritt 5: Fortschrittsberichterstattung

Wir müssen nur noch die Methode observeValueForKeyPath (_:ofObject:change:context:) implementieren. Wir prüfen, ob context ProgressContext entspricht, erstellen ein status-Objekt, indem wir die Anzahl der abgeschlossenen Datensätze aus dem change-Wörterbuch extrahieren und das Fortschritts-HUD aktualisieren. Beachten Sie, dass wir die Benutzeroberfläche im Hauptthread aktualisieren.

5. Dummy-Daten

Wenn wir unsere Anwendung ordnungsgemäß testen möchten, benötigen wir mehr Daten. Ich empfehle zwar nicht, den folgenden Ansatz in einer Produktionsanwendung zu verwenden, aber es ist eine schnelle und einfache Möglichkeit, die Datenbank mit Daten zu füllen.

Öffnen Sie AppDelegate.swift und aktualisieren Sie die Methode application (_:didFinishLaunchingWithOptions:) wie unten gezeigt. Die populateDatabase()-Methode ist eine einfache Hilfsmethode, bei der wir der Datenbank Dummy-Daten hinzufügen.

Die Implementierung ist unkompliziert. Da wir Dummy-Daten nur einmal einfügen möchten, überprüfen wir die Standarddatenbank des Benutzers auf den Schlüssel "didPopulateDatabase". Wenn der Schlüssel nicht gesetzt ist, fügen wir Dummy-Daten ein.

Die Anzahl der Datensätze ist wichtig. Wenn Sie die Anwendung auf dem iOS-Simulator ausführen möchten, können Sie 100.000 oder 1.000.000 Datensätze einfügen. Dies funktioniert auf einem physischen Gerät nicht so gut und dauert zu lange.

In der for-Schleife erstellen wir ein verwaltetes Objekt und füllen es mit Daten. Beachten Sie, dass wir die Änderungen des verwalteten Objektkontexts nicht bei jeder Iteration der for-Schleife speichern.

Schließlich aktualisieren wir die Standarddatenbank des Benutzers, um sicherzustellen, dass die Datenbank beim nächsten Start der Anwendung nicht gefüllt wird.

Toll. Führen Sie die Anwendung im iOS-Simulator aus, um das Ergebnis anzuzeigen. Sie werden feststellen, dass es einige Momente dauert, bis die asynchrone Abrufanforderung mit dem Abrufen von Datensätzen beginnt und das Fortschritts-HUD aktualisiert.

Showing The Progress of The Asynchronous Fetch RequestShowing The Progress of The Asynchronous Fetch RequestShowing The Progress of The Asynchronous Fetch Request

6. Änderungen brechen

Durch Ersetzen der Controller-Klasse für abgerufene Ergebnisse durch eine asynchrone Abrufanforderung haben wir einige Teile der Anwendung beschädigt. Das Tippen auf das Häkchen eines Aufgabenelements scheint beispielsweise nicht mehr zu funktionieren. Während die Datenbank aktualisiert wird, spiegelt die Benutzeroberfläche die Änderung nicht wider. Die Lösung ist ziemlich einfach zu reparieren und ich überlasse es Ihnen, eine Lösung zu implementieren. Sie sollten jetzt über genügend Wissen verfügen, um das Problem zu verstehen und eine geeignete Lösung zu finden.

Schlussfolgerung

Ich bin sicher, Sie stimmen zu, dass das asynchrone Abrufen überraschend einfach zu verwenden ist. Das schwere Heben wird von Core Data durchgeführt, was bedeutet, dass die Ergebnisse der asynchronen Abrufanforderung nicht manuell mit dem verwalteten Objektkontext zusammengeführt werden müssen. Ihre einzige Aufgabe besteht darin, die Benutzeroberfläche zu aktualisieren, wenn die asynchrone Abrufanforderung Ihnen die Ergebnisse übermittelt.

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.