Erstellen Sie mobile Offline-Apps ohne Schmerzen

Alexander Stigsen ist Mitbegründer und CEO von Realm.

Es ist eine allgemein anerkannte Wahrheit, dass ein Benutzer, der ein Smartphone besitzt, eine bessere Verbindung benötigen muss. Trotz Milliarden von Dollar an Infrastrukturinvestitionen und unermüdlicher technologischer Innovation ist es nur eine kurze Fahrt, um eine wesentliche Realität der vernetzten Ära zu erkennen: Sie können nicht davon ausgehen, dass eine Netzwerkverbindung jedes Mal verfügbar sein wird, wenn Sie dies wünschen. Als Entwickler von Mobilgeräten ist es eine Wahrheit, die man bequem ignorieren kann.

Offline-Zustände in Apps können verwirrend sein, aber das Problem beginnt mit einer grundlegenden und falschen Annahme: Offline ist standardmäßig ein Fehlerzustand. Das war sinnvoll, als wir Apps für Desktop-Computer mit dedizierten Ethernet-Uplinks erstellt haben. Es ist nicht sinnvoll, wenn das Schließen der Türen eines Aufzugs eine App völlig unbrauchbar macht oder wenn zu erwarten ist, dass Ihre Anwendung an Orten verwendet wird, an denen keine zuverlässige Mobilfunkinfrastruktur vorhanden ist.

Wir können die Welt nicht in Deckung bringen, deshalb müssen wir eine Alternative anbieten. Wir müssen zuerst offline denken. Wir müssen Apps entwerfen, um offline nützlich zu sein. Wir müssen Apps erstellen, die das Internet voll ausnutzen, wenn es verfügbar ist, aber verstehen, dass der Internetzugang immer nur vorübergehend ist. Wir müssen intelligente Entwurfsentscheidungen treffen, die Offline-Zustände beinhalten, und diese Offline-Zustände für Benutzer verständlich machen.

Es wird viel Arbeit geleistet, um die Offline-First-Zukunft zu definieren. Realm, das Unternehmen, in dem ich arbeite, baut seit einiger Zeit eine Echtzeitplattform für mobile Offline-Apps auf. Unsere mobile Datenbank und die Realm Mobile Platform machen es einfach, intelligente Offline-Apps auf nahezu jedem mobilen Gerät zu erstellen. Die Leute von A List Apart haben enorm zur Offline-First-Literatur beigetragen, insbesondere für Web-Apps. Und die Entwicklergemeinschaften der großen mobilen Ökosysteme haben viele Stunden damit verbracht, beeindruckende eigene Open Source-Lösungen anzubieten.

Im Folgenden finden Sie eine kurze Einführung, wie Sie eine Offline-First-Mobile-App erstellen können. Ich werde gegen Ende auf einen einfachen Swift-Beispielcode zurückgreifen, um zu zeigen, wie eine minimale Offline-First-App aussieht, aber die hier angebotenen Prinzipien und Probleme sind für jeden relevant, der in der Entwicklung mobiler Apps arbeitet.

Design für Offline-First

Bevor Sie die Offline-First-App erstellen, die Sie sich immer gewünscht haben, müssen wir die Designlösungen überdenken, die für Desktops mit einer sehr hohen Wahrscheinlichkeit, online zu sein, sinnvoll sind. Wenn Ihre App Offline- und Online-Zustände verarbeiten kann, müssen wir Fragen dazu beantworten, was sie kann und wie wir dem Benutzer zeigen, was möglich ist.

Definieren Sie, was offline möglich ist

Nehmen wir als Beispiel Twitter. Wenn Sie offline sind und einen Tweet veröffentlichen, kann ein Twitter-Client, der zuerst offline ist, zwei Wege einschlagen. Es könnte den Tweet in die Warteschlange stellen, bis die Konnektivität wieder hergestellt ist. Oder es könnte sich weigern, Sie twittern zu lassen - selbst wenn Sie damit andere Aktionen wie Favoriten in die Warteschlange stellen können, wie dies bei Tweetbot der Fall ist.

Warum sollte Tweetbot Sie daran hindern, offline zu twittern? Vielleicht, weil Ihre Tweets zu dem Zeitpunkt, an dem Sie wieder online sind, möglicherweise nicht mehr relevant sind. Um dieses Problem zu lösen, müssen Sie eine neue Benutzeroberfläche für eine Liste von Tweets erstellen, die Sie noch nicht veröffentlicht haben, die Sie jedoch möglicherweise bearbeiten oder löschen müssen, bevor sie online gehen. Wenn Sie dagegen einen Tweet geschrieben haben, ist es unwahrscheinlich, dass Sie ihn rückgängig machen, wenn Sie mit mehr Informationen konfrontiert werden - und viel weniger problematisch, wenn Sie einfach angeben, dass er für die Veröffentlichung in der Warteschlange steht.

Sie können eine Offline-App nicht dazu bringen, alles zu tun, was eine Online-App kann, aber Sie können sie nützlich machen.

Konflikte weg gestalten

Unabhängig von der Strategie, die Sie im Back-End verwenden, um Änderungen abzustimmen, wird Ihre App an einem Punkt stehen, an dem zwei widersprüchliche Daten vorliegen. Vielleicht liegt es daran, dass der Server abgestürzt ist oder dass Sie und eine andere Person Offline-Änderungen vorgenommen haben und diese jetzt synchronisieren möchten. Alles könnte passieren!

Nehmen Sie Konflikte vorweg und bemühen Sie sich, sie auf vorhersehbare Weise zu lösen. Angebotsauswahl. Und versuchen Sie zunächst, Konflikte zu vermeiden.

Vorhersehbar zu sein bedeutet, dass Ihre Benutzer wissen, was passieren kann. Wenn ein Konflikt auftreten kann, wenn Benutzer offline an zwei Stellen gleichzeitig bearbeiten, sollten sie darauf hingewiesen werden, wenn sie offline sind.

Das Anbieten von Auswahlmöglichkeiten bedeutet, nicht einfach das letzte Schreiben zu akzeptieren oder Änderungen zu verketten oder die älteste Kopie zu löschen. Dies bedeutet, dass der Benutzer entscheiden muss, was angemessen ist.

Schließlich ist die beste Lösung, Konflikte niemals entstehen zu lassen. Vielleicht bedeutet dies, dass Sie Ihre App so erstellen, dass neue und seltsame Daten aus vielen Quellen nicht zu Konflikten führen und stattdessen genau so angezeigt werden, wie Sie es möchten. In einer Online- und Offline-Schreib-App ist dies möglicherweise schwierig. Eine gemeinsam genutzte Zeichen-App kann jedoch so aufgebaut werden, dass der Zeichnung bei jeder Synchronisierung neue Pfade hinzugefügt werden.

Sei explizit

Es ist eine Sache zu definieren, was der Benutzer offline tun kann. Ein ganz anderes Problem besteht darin, diese Entscheidungen für Ihre Benutzer verständlich zu machen. Wenn Sie den Status Ihrer Daten und der Konnektivität oder die Verfügbarkeit bestimmter Funktionen nicht erfolgreich kommunizieren, ist dies gleichbedeutend mit einem Fehler bei der Erstellung einer Offline-First-App.

Eine gemeinsame Notizen-App veranschaulicht das Problem. Wenn Sie offline gehen, aber erwarten, dass Mitarbeiter in Ihrer Abwesenheit die Bearbeitung in der App fortsetzen, reicht es nicht aus, einem Benutzer einfach zu erlauben, weiter zu tippen, bis er zufrieden ist. Wenn sie sich wieder verbinden, werden sie von Konflikten überrascht sein, die sich entwickelt haben.

Helfen Sie stattdessen Ihrem Benutzer, die richtige Entscheidung zu treffen. Wenn Sie feststellen, dass Ihre Serververbindung unterbrochen wurde, weil die obere Leiste Ihrer App ihre Farbe ändert, wissen Sie, was kommen könnte: Konflikte zusammenführen! Das kann die meiste Zeit in Ordnung sein, und die Benutzeroberfläche Ihrer App kann dazu beitragen, unerwartete Konflikte zu beheben, wenn Sie wieder online gehen. Wenn Sie jedoch die Konnektivität verlieren, wenn mehrere Personen Ihre App bearbeiten, wäre es nicht hilfreich zu wissen, dass das Risiko von Konflikten viel größer ist? „Sie haben die Verbindung verloren, aber andere haben gerade bearbeitet. Die weitere Bearbeitung kann zu Konflikten führen. “ Der Benutzer kann fortfahren, kennt aber das Risiko.

Es ist einfach, endlos über Designprobleme und -lösungen zu schreiben, aber bevor wir zu weit von den Tools entfernt sind, die wir verwenden müssen, kann es hilfreich sein zu sehen, wie es ist, eine mobile Offline-App zu erstellen.

Erstellen Sie mit Realm eine Offline-First-App

Die Architektur einer einfachen Offline-First-App ist nichts Besonderes. Sie benötigen eine Möglichkeit, Daten in der App beizubehalten (mithilfe einer Datenbank auf dem Gerät), ein Protokoll für die Kommunikation mit einem Server (einschließlich Serialisierungs- und Deserialisierungscode, falls erforderlich) und den Server, auf dem die synchronisierten Daten gespeichert werden, damit sie gespeichert werden können verteilt an jeden, der die Erlaubnis hat.

Zunächst werde ich Ihnen zeigen, wie Sie mit der Realm Mobile-Datenbank in einer iOS-App beginnen (obwohl der Code in einer Android-App nicht viel anders aussehen würde). Anschließend werde ich eine Strategie zum Serialisieren und Deserialisieren von Code vorstellen, den Sie von einem Server erhalten und in Ihrer lokalen Realm-Datenbank speichern. Abschließend zeige ich Ihnen, wie Sie alles in einer kollaborativen Aufgabenlisten-App zusammenarbeiten lassen, die in Echtzeit synchronisiert wird.

Realm Mobile-Datenbank

Es ist einfach, mit Realm zu beginnen. Sie installieren die Realm Mobile-Datenbank und definieren dann Ihr Schema, indem Sie Klassen erstellen. Da Realm eine Objektdatenbank ist, ist es wirklich so einfach, Klassen zu erstellen, einige Objekte zu instanziieren und diese Objekte an einen writeBlock zu übergeben, um sie auf der Festplatte zu speichern. Es ist keine Serialisierung oder ORM erforderlich und außerdem schneller als die Kerndaten von Apple.

Hier ist der Kern unseres Modells und die grundlegendste Aufgabenlisten-App (die Sie jedes Mal neu kompilieren müssen, wenn Sie eine neue Aufgabe erstellen möchten):

import RealmSwift

class Task: Object {

   dynamic var name

}

class TaskList: Object {

   let tasks = List()

}

let myTask = Task()

myTask.task

let myTaskList = TaskList()

myTaskList.tasks.append(myTask)

let realm = Realm()

try! realm.write{

            realm.add([myTask, myTaskList])

}

Von da an braucht es nicht viel, um eine voll funktionsfähige App um Folgendes zu erstellen TableViewController:

import UIKit

import RealmSwift

class TaskListTableViewController: UITableViewController {

   var realm = try! Realm()

   var taskList = TaskList()

   override func viewDidLoad() {

       super.viewDidLoad()

       print(Realm.Configuration.defaultConfiguration.fileURL!)

       // Here, you could replace self.taskList with a previously saved TaskList object

       try! realm.write {

           realm.add(self.taskList)

       }

       // add navbar +

       navigationItem.setRightBarButton(UIBarButtonItem.init(barButtonSystemItem: UIBarButtonSystemItem.add, target: self, action: #selector(displayTaskAlert)), animated: false)

   }

   func displayTaskAlert() {

       // make and display an alert that’ll take a name and make a task.

       let alert = UIAlertController(title: “Make a task”, message: “What do you want to call it?”, preferredStyle: UIAlertControllerStyle.alert)

       alert.addTextField(configurationHandler: nil)

       alert.addAction(UIAlertAction(title: “Cancel”, style: UIAlertActionStyle.cancel, handler: nil))

       alert.addAction(UIAlertAction(title: “Create Task”, style: UIAlertActionStyle.default, handler: { (action) in

           let task = Task()

           task.name = (alert.textFields?[0].text)!

           try! self.realm.write {

               self.realm.add(task)

               self.taskList.tasks.append(task)

           }

           self.tableView.reloadData()

       }))

       self.present(alert, animated: true, completion: nil)

   }

   override func didReceiveMemoryWarning() {

       super.didReceiveMemoryWarning()

   }

   override func numberOfSections(in tableView: UITableView) -> Int {

       return 1

   }

   override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

       return self.taskList.tasks.count

   }

   override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

       let cell = tableView.dequeueReusableCell(withIdentifier: “reuseIdentifier”, for: indexPath)

       cell.textLabel?.text = self.taskList.tasks[indexPath.row].name

       return cell

   }

}

Das ist alles, was Sie brauchen, um loszulegen! Mit den Sammlungs- und Objektbenachrichtigungen von Realm können Sie viel schlauer werden, sodass Sie das tableViewHinzufügen oder Löschen eines Objekts auf intelligente Weise neu laden können. Derzeit haben wir jedoch die Persistenz - die Grundlage für eine Offline-First-App.

Serialisierung und Deserialisierung

Eine Offline-First-App ist keine Offline-First-App, es sei denn, sie kann auch online geschaltet werden, und das Abrufen von Daten zu und von Realm kann etwas schwierig sein.

Zunächst ist es entscheidend, Ihr Client-Schema so genau wie möglich an das Schema Ihres Servers anzupassen. Angesichts der Funktionsweise der meisten Back-End-Datenbanken wird dies wahrscheinlich das Hinzufügen eines Primärschlüsselfelds zu Ihrer Realm-Klasse umfassen, da Realm-Objekte standardmäßig keinen Primärschlüssel haben.

Sobald Ihr Schema gut übereinstimmt, müssen Sie die vom Server kommenden Daten in Realm deserialisieren und die Daten in JSON serialisieren, um sie an den Server zurückzusenden. Die einfachste Methode, dies zu tun, besteht darin, Ihre bevorzugte Modellzuordnungsbibliothek auszuwählen und sie das schwere Heben ausführen zu lassen. Swift hat Argo, Decodable, ObjectMapper und Mapper. Wenn Sie jetzt eine Antwort von Ihrem Server erhalten, lassen Sie den Model Mapper sie einfach in ein natives RealmObject dekodieren.

Trotzdem ist es keine so gute Lösung. Sie müssen immer noch eine Menge Netzwerkcode schreiben, um JSON sicher von und zu Ihrem Server zu bringen, und Ihr Model-Mapper-Code muss jedes Mal neu geschrieben und debuggt werden, wenn sich Ihr Schema ändert. Es sollte einen besseren Weg geben, und wir denken, dass die Realm Mobile Platform genau das ist.

Arbeiten mit der Realm Mobile Platform

Mit der Realm Mobile Platform (RMP) können Sie in Echtzeit synchronisieren, sodass Sie sich auf die Erstellung einer mobilen App konzentrieren können, ohne den Server und die App zum Sprechen zu bringen. Nehmen Sie einfach Ihr Realm-Modell oben, fügen Sie die Benutzerauthentifizierung von RMP hinzu und lassen Sie RMP die Synchronisierung der Daten zwischen dem Server und den Realms Ihrer App übernehmen. Dann arbeiten Sie einfach weiter mit nativen Swift-Objekten.

Laden Sie zunächst das Realm Mobile Platform MacOS-Bundle herunter und installieren Sie es, mit dem Sie eine Realm Object Server-Instanz sehr schnell auf Ihrem Mac installieren können. Anschließend fügen wir unserer Aufgabenlisten-App einige Elemente hinzu, um eine Verbindung zum Realm Object Server herzustellen.

Wenn Sie die obigen Installationsanweisungen befolgt haben, sollte der Server ausgeführt werden und ein Administrator unter //127.0.0.1:9080. Erinnern Sie sich an diese Anmeldeinformationen, und wir kehren zu unserem Swift-Code zurück.

Bevor wir weiteren Code schreiben, müssen wir zwei kleine Änderungen am Projekt vornehmen. Zuerst müssen wir in Xcode zum Zieleditor unserer App gehen und auf der Registerkarte Funktionen den Schalter Schlüsselbundfreigabe aktivieren.

Dann müssen wir Nicht-TLS-Netzwerkanforderungen zulassen. Gehen Sie zur Info.plist-Datei des Projekts und fügen Sie Folgendes in die Tags ein:

NSAppTransportSecurity

   NSAllowsArbitraryLoads