Créez des applications mobiles hors ligne sans peine

Alexander Stigsen est co-fondateur et PDG de Realm.

C'est une vérité universellement reconnue qu'un utilisateur en possession d'un smartphone doit avoir besoin d'une meilleure connexion. Malgré des milliards de dollars d'investissement dans les infrastructures et une innovation technologique incessante, il ne faut pas beaucoup plus qu'un court trajet en voiture pour remarquer une réalité essentielle de l'ère connectée: vous ne pouvez pas supposer qu'une connexion réseau sera disponible chaque fois que vous le souhaitez. En tant que développeurs mobiles, c'est une vérité qu'il est pratique d'ignorer.

Les états hors ligne dans les applications peuvent être déroutants à gérer, mais le problème commence par une hypothèse de base et incorrecte: que hors ligne est, par défaut, un état d'erreur. Cela avait du sens lorsque nous avons créé des applications pour les ordinateurs de bureau avec des liaisons montantes Ethernet dédiées. Cela n'a aucun sens lorsque la fermeture des portes d'un ascenseur rend une application complètement inutile ou lorsqu'il est raisonnable de s'attendre à ce que votre application soit utilisée dans des endroits dépourvus d'une infrastructure cellulaire fiable.

Nous ne pouvons pas couvrir le monde entier, nous devons donc proposer une alternative. Nous devons d'abord penser hors ligne. Nous devons concevoir des applications pour être utiles hors ligne. Nous devons créer des applications qui tirent pleinement parti d'Internet lorsqu'il est disponible, mais comprenons que l'accès à Internet est toujours temporaire. Nous devons prendre des décisions de conception intelligentes impliquant des états hors ligne et rendre ces états hors ligne intelligibles aux utilisateurs.

Beaucoup de travail est en cours pour définir le futur hors ligne. Realm, l'entreprise dans laquelle je travaille, construit depuis un certain temps une plate-forme en temps réel pour les applications mobiles hors ligne. Notre base de données mobile et la plateforme mobile Realm facilitent la création d'applications intelligentes, d'abord hors ligne, sur presque tous les appareils mobiles. Les gens de A List Apart ont énormément contribué à la littérature hors ligne, en particulier pour les applications Web. Et les communautés de développeurs des principaux écosystèmes mobiles ont passé de nombreuses heures à proposer leurs propres solutions open source impressionnantes.

Ce qui suit est une brève introduction à la façon dont vous pouvez créer une première application mobile hors ligne. Je vais m'appuyer sur un exemple de code Swift simple vers la fin pour montrer à quoi ressemble une application minimale hors ligne, mais les principes et les problèmes proposés ici sont pertinents pour toute personne travaillant dans le développement d'applications mobiles.

Concevoir pour la première fois hors ligne

Avant de créer l'application hors ligne dont vous avez toujours rêvé, nous devons revoir les solutions de conception qui ont du sens pour les ordinateurs de bureau avec une très forte probabilité d'être en ligne. Si votre application peut gérer les états hors ligne et en ligne, nous avons des questions à répondre sur ce qu'elle peut faire et comment nous montrons à l'utilisateur ce qui est possible.

Définissez ce qui est possible hors ligne

Prenons comme exemple Twitter. Si vous êtes hors ligne et que vous publiez un tweet, un premier client Twitter hors ligne peut emprunter deux chemins. Il pourrait mettre le tweet en file d'attente jusqu'à ce qu'il retrouve la connectivité. Ou il peut refuser de vous laisser tweeter, même s'il vous permet de mettre en file d'attente d'autres actions telles que les favoris, comme le fait Tweetbot.

Pourquoi Tweetbot vous empêcherait-il de tweeter hors ligne? Peut-être parce que lorsque vous serez de retour en ligne, vos tweets pourraient ne plus être pertinents. Résoudre ce problème impliquerait de créer une nouvelle interface utilisateur pour une liste de tweets que vous n'avez pas encore publiés, mais que vous devrez peut-être modifier ou supprimer avant de les mettre en ligne. Si vous avez un tweet, en revanche, il est peu probable que vous l'annuliez si vous êtes confronté à plus d'informations - et il est beaucoup moins problématique d'indiquer simplement qu'il est en attente de publication.

Vous ne pouvez pas faire en sorte qu'une application hors ligne fasse tout ce qu'une application en ligne peut, mais vous pouvez la rendre utile.

Éliminez les conflits

Quelle que soit la stratégie que vous utilisez sur le back-end pour réconcilier les modifications, votre application sera confrontée à un point où vous avez deux éléments de données en conflit. C'est peut-être parce que le serveur est tombé en panne ou parce que vous et une autre personne avez effectué des modifications hors connexion et que vous souhaitez maintenant les synchroniser. Tout peut arriver!

Ainsi, anticipez les conflits et efforcez-vous de les résoudre de manière prévisible. Offrez des choix. Et essayez d'éviter les conflits en premier lieu.

Être prévisible signifie que vos utilisateurs savent ce qui pourrait arriver. Si un conflit peut survenir lorsque les utilisateurs modifient à deux endroits à la fois lorsqu'ils sont hors ligne, ils doivent en être avertis lorsqu'ils sont hors ligne.

Offrir des choix ne signifie pas simplement accepter la dernière écriture ou concaténer les modifications ou supprimer la copie la plus ancienne. Cela signifie laisser l'utilisateur décider de ce qui est approprié.

Enfin, la meilleure solution est de ne jamais laisser les conflits se développer en premier lieu. Peut-être que cela signifie créer votre application de manière à ce que les données nouvelles et étranges provenant de nombreuses sources ne conduisent pas à un conflit et s'affichent exactement comme vous le souhaitez. Cela peut être difficile à faire dans une application d'écriture qui se met en ligne et hors ligne, mais une application de dessin partagée peut être conçue pour ajouter de nouveaux chemins au dessin chaque fois qu'ils sont synchronisés.

Soyez explicite

C'est une chose de définir ce que l'utilisateur peut faire hors ligne. Un tout autre problème consiste à rendre ces décisions intelligibles pour vos utilisateurs. Ne pas communiquer avec succès l'état de vos données et de votre connectivité, ou la disponibilité de fonctionnalités données, équivaut à un échec dans la création d'une application hors ligne en premier lieu.

Une application de prise de notes partagée illustre le problème. Si vous vous déconnectez mais que vous vous attendez à ce que les collaborateurs continuent de modifier dans l'application en votre absence, il ne suffit pas de simplement permettre à un utilisateur de continuer à taper jusqu'à ce qu'il soit satisfait. Lorsqu'ils se reconnecteront, ils seront surpris par les conflits qui se sont développés.

Aidez plutôt votre utilisateur à prendre la bonne décision. Si vous voyez que votre connexion au serveur a été interrompue parce que la barre supérieure de votre application change de couleur, vous savez ce qui pourrait arriver: les conflits de fusion! Cela peut convenir la plupart du temps, et l'interface utilisateur de votre application peut vous aider à résoudre les conflits inattendus lorsque vous revenez en ligne. Mais si vous perdez la connectivité lorsque plusieurs personnes modifient votre application, ne serait-il pas utile de savoir que le risque de conflits est beaucoup plus grand? «Vous avez perdu la connexion, mais d'autres étaient en train de modifier. Continuer les modifications peut entraîner des conflits. » L'utilisateur peut continuer mais connaît le risque.

Il est facile d'écrire sans cesse sur les problèmes de conception et les solutions, mais avant de nous éloigner trop des outils que nous devrons utiliser, il peut être utile de voir à quoi ressemble la création d'une première application mobile hors ligne.

Créez une application hors ligne avec Realm

L'architecture d'une application de base hors ligne n'est pas sophistiquée. Vous avez besoin d'un moyen de conserver les données dans l'application (à l'aide d'une base de données sur l'appareil), d'un protocole pour communiquer avec un serveur (y compris le code de sérialisation et de désérialisation si nécessaire) et du serveur sur lequel les données synchronisées vivront afin qu'elles puissent être distribué à quiconque en a la permission.

Tout d'abord, je vais vous expliquer comment démarrer avec la base de données mobile Realm dans une application iOS (bien que le code ne soit pas très différent dans une application Android). Ensuite, je présenterai une stratégie pour sérialiser et désérialiser le code que vous obtenez d'un serveur et stockez dans votre base de données Realm locale. Enfin, je vais vous montrer comment faire fonctionner le tout dans une application de liste de tâches collaborative qui se synchronise en temps réel.

Base de données mobile du royaume

Il est facile de démarrer avec Realm. Vous installez la base de données mobile Realm, puis définissez votre schéma en créant des classes. Comme Realm est une base de données d'objets, c'est vraiment aussi simple que de créer des classes, d'instancier certains objets et de passer ces objets dans un writebloc pour les conserver sur le disque. Aucune sérialisation ou ORM n'est requise, et c'est plus rapide que les données de base d'Apple.

Voici le cœur de notre modèle et l'application de liste de tâches la plus basique possible (que vous devrez recompiler à chaque fois que vous souhaitez créer une nouvelle tâche):

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])

}

À partir de là, il ne faut pas grand-chose pour créer une application plus fonctionnelle autour d'un 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

   }

}

C'est tout ce qu'il faut pour commencer! Vous pouvez devenir beaucoup plus intelligent avec les notifications de collection et d'objet de Realm, de sorte que vous pouvez recharger intelligemment le tableViewquand un objet est ajouté ou supprimé, mais pour l'instant, nous avons la persistance - la base d'une application hors ligne d'abord.

Sérialisation et désérialisation

Une application hors ligne n'est pas vraiment une application hors ligne à moins qu'elle ne puisse également être connectée, et obtenir des données vers et depuis le royaume peut être un peu délicat.

Tout d'abord, il est crucial de faire correspondre votre schéma client au plus près du schéma de votre serveur. Étant donné le fonctionnement de la plupart des bases de données principales, cela impliquera probablement l'ajout d'un champ de clé primaire à votre classe Realm, car les objets Realm n'ont pas par défaut de clé primaire.

Une fois que votre schéma correspond bien, vous avez besoin d'un moyen de désérialiser les données provenant du serveur dans Realm et de sérialiser les données en JSON pour les renvoyer au serveur. La méthode la plus simple pour le faire est de choisir votre bibliothèque de mappage de modèles préférée et de la laisser faire le gros du travail. Swift a Argo, Decodable, ObjectMapper et Mapper. Désormais, lorsque vous recevez une réponse de votre serveur, vous laissez simplement le mappeur de modèle le décoder en un RealmObject natif.

Pourtant, ce n'est pas une excellente solution. Vous devez toujours écrire une tonne de code de mise en réseau pour obtenir JSON vers et depuis votre serveur en toute sécurité en premier lieu, et votre code de mappeur de modèle devra être réécrit et débogué à chaque fois que votre schéma change. Il devrait y avoir un meilleur moyen, et nous pensons que la plate-forme mobile Realm est exactement cela.

Travailler avec la plate-forme mobile Realm

La plate-forme mobile Realm (RMP) vous offre une synchronisation en temps réel afin que vous puissiez vous concentrer sur la création d'une application mobile, sans vous battre pour faire parler le serveur et l'application. Il vous suffit de prendre votre modèle de domaine ci-dessus, d'ajouter l'authentification utilisateur de RMP et de laisser RMP se charger de la synchronisation des données entre le serveur et les domaines de votre application. Ensuite, vous continuez simplement à travailler avec des objets Swift natifs.

Pour commencer, téléchargez et installez le bundle Realm Mobile Platform MacOS, qui vous permet d'obtenir une instance de Realm Object Server sur votre Mac très rapidement. Ensuite, nous ajouterons quelques éléments à notre application de liste de tâches pour la connecter au serveur d'objets de royaume.

Une fois que vous avez terminé de suivre les instructions d'installation ci-dessus, vous devriez avoir le serveur en cours d'exécution et un utilisateur administrateur à //127.0.0.1:9080. Souvenez-vous de ces informations d'identification et nous reviendrons à notre code Swift.

Avant d'écrire plus de code, nous devons apporter deux petites modifications au projet. Tout d'abord, nous devons accéder à l'éditeur cible de notre application dans Xcode, et dans l'onglet Capabilities, activer le commutateur de partage de trousseau.

Ensuite, nous devrons autoriser les requêtes réseau non TLS. Accédez au fichier Info.plist du projet et ajoutez ce qui suit dans les balises:

NSAppTransportSecurity

   NSAllowsArbitraryLoads