Crea app per dispositivi mobili offline senza problemi

Alexander Stigsen è co-fondatore e CEO di Realm.

È una verità universalmente riconosciuta che un utente in possesso di uno smartphone debba essere alla ricerca di una migliore connessione. Nonostante miliardi di dollari di investimenti infrastrutturali e l'inarrestabile innovazione tecnologica, non ci vuole molto di più di un breve viaggio per notare una realtà essenziale dell'era connessa: non puoi presumere che una connessione di rete sarà disponibile ogni volta che lo desideri. In quanto sviluppatori di dispositivi mobili, è una verità che è conveniente ignorare.

Gli stati offline nelle app possono essere difficili da gestire, ma il problema inizia con un presupposto di base e errato: che offline è, per impostazione predefinita, uno stato di errore. Ciò aveva senso quando abbiamo creato app per computer desktop con uplink ethernet dedicati. Non ha senso quando la chiusura delle porte di un ascensore rende un'app completamente inutile o quando è ragionevole aspettarsi che la tua applicazione venga utilizzata in luoghi privi di un'infrastruttura cellulare affidabile.

Non possiamo coprire il mondo in copertura, quindi dobbiamo offrire un'alternativa. Dobbiamo prima pensare offline. Dobbiamo progettare app che siano utili offline. Dobbiamo creare app che sfruttino appieno Internet quando è disponibile, ma comprendiamo che l'accesso a Internet è sempre temporaneo. Dobbiamo prendere decisioni di progettazione intelligenti che coinvolgono gli stati offline e renderli comprensibili agli utenti.

È stato fatto molto lavoro per definire il primo futuro offline. Realm, la società in cui lavoro, costruisce da tempo una piattaforma in tempo reale per le app mobili offline first. Il nostro database mobile e la Realm Mobile Platform semplificano la creazione di app intelligenti e offline su quasi tutti i dispositivi mobili. I ragazzi di A List Apart hanno contribuito enormemente alla prima letteratura offline, specialmente per le app web. E le comunità di sviluppatori dei principali ecosistemi mobili hanno trascorso molte ore a offrire straordinarie soluzioni open source proprie.

Quella che segue è una breve introduzione su come creare un'app mobile per la prima volta offline. Attingerò ad un semplice codice di esempio Swift verso la fine per mostrare l'aspetto di un'app minimale offline, ma i principi e i problemi offerti qui sono rilevanti per chiunque lavori nello sviluppo di app mobili.

Progettare prima offline

Prima di creare l'app offline che hai sempre desiderato, dobbiamo rivisitare le soluzioni di design che avevano senso per i desktop con un'elevata probabilità di essere online. Se la tua app è in grado di gestire gli stati offline e online, abbiamo domande a cui rispondere su cosa può fare e su come mostrare all'utente cosa è possibile fare.

Definisci cosa è possibile fare offline

Prendiamo Twitter come esempio. Se sei offline e pubblichi un tweet, un primo client Twitter offline potrebbe prendere due strade. Potrebbe mettere in coda il tweet finché non riacquista la connettività. Oppure potrebbe rifiutarsi di farti twittare, anche se ti permette di mettere in coda altre azioni come i preferiti, come fa Tweetbot.

Perché Tweetbot ti impedisce di twittare offline? Forse perché quando torni online, i tuoi tweet potrebbero non essere più rilevanti. La risoluzione di questo problema comporterebbe la creazione di una nuova interfaccia utente per un elenco di tweet che non hai ancora pubblicato, ma che potresti dover modificare o eliminare prima che siano online. Se hai incoraggiato un tweet, d'altra parte, è improbabile che lo annulleresti se ti confrontassi con più informazioni e molto meno problematico indicare semplicemente che è in coda per la pubblicazione.

Non puoi fare in modo che un'app offline faccia tutto ciò che può fare un'app online, ma puoi renderla utile.

Disegna via i conflitti

Indipendentemente dalla strategia che utilizzi nel back-end per riconciliare le modifiche, la tua app si troverà ad affrontare un punto in cui hai due dati in conflitto. Forse è perché il server si è bloccato o perché tu e un'altra persona avete apportato modifiche offline e ora desiderate sincronizzarle. Potrebbe succedere di tutto!

Pertanto, anticipa i conflitti e sforzati di risolverli in modo prevedibile. Offrire scelte. E cerca di evitare i conflitti in primo luogo.

Essere prevedibili significa che i tuoi utenti sanno cosa potrebbe accadere. Se può sorgere un conflitto quando gli utenti modificano in due posizioni contemporaneamente quando sono offline, dovrebbero essere avvisati quando sono offline.

Offrire scelte significa non semplicemente accettare l'ultima scrittura o concatenare modifiche o eliminare la copia più vecchia. Significa lasciare che l'utente decida cosa è appropriato.

Infine, la soluzione migliore è non lasciare mai che i conflitti si sviluppino in primo luogo. Forse questo significa costruire la tua app in modo tale che dati nuovi e strani da molte fonti non portino a un conflitto e invece vengano visualizzati esattamente come vorresti. Potrebbe essere difficile da fare in un'app di scrittura che va online e offline, ma un'app di disegno condivisa può essere progettata per aggiungere nuovi percorsi al disegno ogni volta che vengono sincronizzati.

Sii esplicito

Una cosa è definire ciò che l'utente può fare offline. Un altro problema consiste nel rendere queste decisioni intelligibili ai tuoi utenti. La mancata comunicazione dello stato dei dati e della connettività, o la disponibilità di determinate funzionalità, equivale al fallimento nell'aver creato innanzitutto un'app offline.

Un'app per prendere appunti condivisa illustra il problema. Se vai offline ma ti aspetti che i collaboratori continuino a modificare nell'app in tua assenza, non è sufficiente consentire semplicemente a un utente di continuare a digitare finché non sono soddisfatti. Quando si ricollegheranno, saranno sorpresi dai conflitti che si sono sviluppati.

Invece, aiuta il tuo utente a prendere la decisione giusta. Se vedi che la tua connessione al server è stata interrotta perché la barra superiore della tua app cambia colore, sai cosa potrebbe arrivare: unisci i conflitti! Potrebbe andare bene per la maggior parte del tempo e l'interfaccia utente della tua app può aiutarti a risolvere i conflitti imprevisti quando torni online. Ma se perdi la connettività quando più persone stanno modificando la tua app, non sarebbe utile sapere che il rischio di conflitti è molto maggiore? “Hai perso la connessione, ma altri stavano modificando. Continuare a modificare potrebbe causare conflitti ". L'utente può continuare ma conosce il rischio.

È facile scrivere all'infinito su problemi e soluzioni di progettazione, ma prima di allontanarci troppo dagli strumenti che dovremo utilizzare, potrebbe essere utile vedere com'è creare un'app mobile offline.

Crea un'app offline per la prima volta con Realm

L'architettura di un'app di base offline prima non è fantasiosa. È necessario un modo per rendere persistenti i dati nell'app (utilizzando un database sul dispositivo), un protocollo per comunicare con un server (inclusi codice di serializzazione e deserializzazione se necessario) e il server in cui risiederanno i dati sincronizzati in modo che possano essere distribuito a chi ne ha il permesso.

Innanzitutto, ti guiderò attraverso come iniziare con Realm Mobile Database all'interno di un'app iOS (anche se il codice non apparirebbe molto diverso in un'app Android). Quindi presenterò una strategia per serializzare e deserializzare il codice ottenuto da un server e archiviato nel database Realm locale. Infine, ti mostrerò come far funzionare tutto insieme in un'app collaborativa per l'elenco delle cose da fare che si sincronizza in tempo reale.

Database mobile di Realm

È facile iniziare con Realm. Installa il Realm Mobile Database, quindi definisci il tuo schema creando classi. Poiché Realm è un database di oggetti, è davvero semplice come creare classi, istanziare alcuni oggetti e passare quegli oggetti in un writeblocco per mantenerli su disco. Non è richiesta alcuna serializzazione o ORM, inoltre è più veloce dei Core Data di Apple.

Ecco il nucleo del nostro modello e l'app per l'elenco delle cose da fare più semplice possibile (che dovresti ricompilare ogni volta che desideri eseguire una nuova attività):

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

}

Da lì, non ci vuole molto per creare un'app più completamente funzionale attorno a 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

   }

}

È tutto ciò che serve per iniziare! Puoi diventare molto più intelligente con la raccolta e le notifiche degli oggetti di Realm, in modo da poter ricaricare in modo intelligente tableViewquando un oggetto viene aggiunto o eliminato, ma per ora abbiamo la persistenza, la base di un'app offline per la prima volta.

Serializzazione e deserializzazione

Un'app offline non è granché come app offline a meno che non possa anche andare online, e ottenere dati da e verso Realm può essere un po 'complicato.

Prima di tutto, è fondamentale far corrispondere lo schema del client il più vicino allo schema del server. Dato il funzionamento della maggior parte dei database back-end, ciò comporterà probabilmente l'aggiunta di un campo chiave primaria alla classe Realm, poiché gli oggetti Realm non hanno per impostazione predefinita una chiave primaria.

Una volta che lo schema è stato abbinato correttamente, è necessario un modo per deserializzare i dati provenienti dal server in Realm e per serializzare i dati in JSON per inviarli al server. Il metodo più semplice per farlo è scegliere la tua libreria di mappatura del modello preferita e lasciare che faccia il lavoro pesante. Swift ha Argo, Decodable, ObjectMapper e Mapper. Ora, quando ricevi una risposta dal tuo server, lasci semplicemente che il model mapper la decodifichi in un RealmObject nativo.

Tuttavia, non è un'ottima soluzione. Devi ancora scrivere un sacco di codice di rete per ottenere JSON da e verso il tuo server in modo sicuro in primo luogo e il codice del mappatore di modelli dovrà essere riscritto e debug ogni volta che lo schema cambia. Dovrebbe esserci un modo migliore e pensiamo che la Realm Mobile Platform sia esattamente questo.

Lavorare con la piattaforma mobile Realm

Realm Mobile Platform (RMP) ti offre la sincronizzazione in tempo reale in modo che tu possa concentrarti sulla creazione di un'app mobile, non lottare per far parlare il server e l'app. Devi semplicemente prendere il tuo modello Realm sopra, aggiungere l'autenticazione utente di RMP e lasciare che RMP si occupi della sincronizzazione dei dati tra il server e gli ambiti della tua app. Quindi continui semplicemente a lavorare con gli oggetti Swift nativi.

Per iniziare, scarica e installa il bundle MacOS di Realm Mobile Platform, che ti consente di ottenere un'istanza di Realm Object Server sul tuo Mac molto rapidamente. Quindi aggiungeremo alcuni elementi alla nostra app per l'elenco delle cose da fare per collegarla al server degli oggetti di Realm.

Una volta che hai finito di seguire le istruzioni di installazione sopra, dovresti avere il server in esecuzione e un utente amministratore su //127.0.0.1:9080. Ricorda quelle credenziali e torneremo al nostro codice Swift.

Prima di scrivere altro codice, dobbiamo apportare due piccole modifiche al progetto. Innanzitutto, dobbiamo andare all'editor di destinazione della nostra app in Xcode e nella scheda Capacità, abilitare l'opzione Condivisione portachiavi.

Quindi, dovremo consentire le richieste di rete non TLS. Vai al file Info.plist del progetto e aggiungi quanto segue all'interno dei tag:

NSAppTransportSecurity

   NSAllowsArbitraryLoads