Programmering

Bygg offline-første mobilapper uten smerte

Alexander Stigsen er medstifter og administrerende direktør i Realm.

Det er en sannhet som er allment anerkjent at en bruker i besittelse av en smarttelefon må være i mangel av en bedre forbindelse. Til tross for milliarder dollar av infrastrukturinvesteringer og ubarmhjertig teknologisk innovasjon, tar det ikke mye mer enn en kort kjøretur å legge merke til en essensiell realitet i den tilkoblede tiden: Du kan ikke anta at en nettverkstilkobling vil være tilgjengelig hver gang du vil ha den. Som mobilutviklere er det en sannhet som er praktisk å ignorere.

Frakoblede tilstander i apper kan være forvirrende å håndtere, men problemet begynner med en grunnleggende og feil antagelse - at offline er som standard en feiltilstand. Det var fornuftig da vi bygde apper for stasjonære datamaskiner med dedikerte Ethernet-opplinker. Det gir ikke mening når lukking av heisens dører gjør en app helt ubrukelig, eller når det er rimelig å forvente at applikasjonen din vil bli brukt på steder som mangler en pålitelig mobilinfrastruktur.

Vi kan ikke dekke verden i dekning, så vi må tilby et alternativ. Vi må tenke offline-først. Vi må designe apper for å være nyttige offline. Vi må bygge apper som drar full nytte av internett når det er tilgjengelig, men forstår at internettilgang alltid er midlertidig. Vi må ta smarte designbeslutninger som involverer frakoblede stater og gjøre de frakoblede statene forståelige for brukerne.

Det gjøres mye arbeid for å definere den offline-første fremtiden. Realm, selskapet hvor jeg jobber, har bygget en sanntidsplattform for offline-første mobilapper i noen tid. Vår mobile database og Realm Mobile Platform gjør det enkelt å lage intelligente, offline-første apper på nesten hvilken som helst mobil enhet. Folkene på A List Apart har bidratt enormt til offline-første litteraturen, spesielt for webapper. Og utviklermiljøene til de store mobile økosystemene har brukt mange timer på å tilby imponerende åpen kildekode-løsninger.

Det som følger er en kort introduksjon til hvordan du kan bygge en offline-første mobilapp. Jeg vil trekke på noen enkle Swift-eksempelkoder mot slutten for å vise hvordan en minimal offline-første app ser ut, men prinsippene og problemene som tilbys her er relevante for alle som jobber med utvikling av mobilapper.

Design for offline-først

Før du bygger den offline-første appen du alltid har ønsket deg, må vi se på designløsningene som var fornuftige for stasjonære datamaskiner med svært stor sannsynlighet for å være online. Hvis appen din kan håndtere tilstander offline og online, har vi spørsmål å svare på hva den kan gjøre, og hvordan vi viser brukeren hva som er mulig.

Definer hva som er mulig offline

La oss ta Twitter som et eksempel. Hvis du ikke er frakoblet og legger ut en tweet, kan en første frakoblet Twitter-klient ta to veier. Det kan stå i køen til tweeten til den får tilkobling. Eller det kan nekte å la deg kvitre - selv om den lar deg stille andre handlinger som faves i kø, slik Tweetbot gjør.

Hvorfor ville Tweetbot hindre deg i å tweete offline? Kanskje fordi når du kommer tilbake på nettet, er tweets kanskje ikke relevante lenger. Å løse problemet vil innebære å lage et nytt brukergrensesnitt for en liste over tweets du ikke har lagt ut ennå, men som du kanskje trenger å redigere eller slette før de går online. Hvis du likte en tweet, derimot, er det lite sannsynlig at du vil angre den hvis du blir konfrontert med mer informasjon - og mye mindre problematisk å bare indikere at den er i kø for innlegg.

Du kan ikke få en offline app til å gjøre alt en online app kan, men du kan gjøre den nyttig.

Design bort konflikter

Uansett hvilken strategi du bruker på baksiden for å forene endringer, vil appen din møte et punkt der du har to motstridende data. Kanskje det er fordi serveren krasjet eller fordi du og en annen person har gjort endringer uten nett og nå vil synkronisere dem. Alt kan skje!

Så forvent konflikter og forsøk å løse dem på en forutsigbar måte. Tilby valg. Og prøv å unngå konflikter i utgangspunktet.

Å være forutsigbar betyr at brukerne vet hva som kan skje. Hvis en konflikt kan oppstå når brukere redigerer to steder samtidig når de er frakoblet, bør de bli varslet om det når de er frakoblet.

Å tilby valg betyr ikke bare å godta den siste skrivingen eller sammenkoble endringene eller slette den eldste kopien. Det betyr å la brukeren bestemme hva som er passende.

Endelig er den beste løsningen å la konflikter utvikle seg i utgangspunktet. Kanskje betyr det å bygge appen din på en måte slik at nye og rare data fra mange kilder ikke fører til en konflikt, og i stedet vises nøyaktig slik du ønsker det. Det kan være vanskelig å gjøre i en skriveapp som går online og offline, men en delt tegne-app kan arkiveres for å legge til nye baner til tegningen når de blir synkronisert.

Vær eksplisitt

Det er en ting å definere hva brukeren kan gjøre offline. Et helt annet problem innebærer å gjøre disse beslutningene forståelige for brukerne dine. Unnlatelse av å lykkes med å kommunisere tilstanden til dataene dine og tilkoblingen, eller tilgjengeligheten av gitte funksjoner, tilsvarer feil i å ha bygget en offline-første app i utgangspunktet.

En delt notatapp illustrerer problemet. Hvis du går frakoblet, men forventer at samarbeidspartnere fortsetter å redigere i appen i ditt fravær, er det ikke nok å bare la en bruker fortsette å skrive til de er fornøyde. Når de kobler til igjen, vil de bli overrasket over konflikter som har utviklet seg.

Hjelp i stedet brukeren din med å ta den riktige avgjørelsen. Hvis du ser at serverforbindelsen din er brutt fordi appens øverste linje endrer farge, vet du hva som kan komme: slå sammen konflikter! Det kan være bra det meste av tiden, og appens brukergrensesnitt kan hjelpe deg med å avhjelpe uventede konflikter når du kommer tilbake på nettet. Men hvis du mister tilkoblingen når flere personer redigerer appen din, ville det ikke være nyttig å vite at risikoen for konflikter er mye større? “Du mistet forbindelsen, men andre redigerte. Hvis du fortsetter å redigere, kan det føre til konflikter. ” Brukeren kan fortsette, men kjenner risikoen.

Det er enkelt å skrive uendelig om designproblemer og løsninger, men før vi kommer for langt fra verktøyene vi må bruke, kan det være nyttig å se hvordan det er å bygge en offline-første mobilapp.

Bygg en offline-første app med Realm

Arkitekturen til en grunnleggende offline-første app er ikke fancy. Du trenger en måte å vedvare data i appen (ved hjelp av en enhetsdatabase), en protokoll for å kommunisere med en server (inkludert serialisering og deserialiseringskode om nødvendig), og serveren der de synkroniserte dataene vil leve slik at det kan være distribueres til den som har tillatelse.

Først vil jeg gå gjennom hvordan du kommer i gang med Realm Mobile Database i en iOS-app (selv om koden ikke ville se mye annerledes ut i en Android-app). Deretter presenterer jeg en strategi for serialisering og deserialisering av kode som du får fra en server og lagrer i din lokale Realm-database. Til slutt vil jeg vise deg hvordan du kan få det til å fungere sammen i en samarbeidende oppgaveliste-app som synkroniseres i sanntid.

Realm Mobile Database

Det er lett å komme i gang med Realm. Du installerer Realm Mobile Database, og definer deretter skjemaet ditt ved å lage klasser. Fordi Realm er en objektdatabase, er det virkelig så enkelt som å lage klasser, instantiere noen objekter og overføre disse objektene til en skrive blokker for å fortsette dem til disk. Ingen serialisering eller ORM er nødvendig, pluss at den er raskere enn Apples kjernedata.

Her er kjernen i modellen vår og den mest grunnleggende mulige listen over gjøremålslister (som du må kompilere hver gang du vil lage en ny oppgave):

importer RealmSwift

klasse Oppgave: Objekt {

dynamisk var navn

}

class TaskList: Object {

la oppgaver = Liste ()

}

la myTask = Oppgave ()

myTask.task

la myTaskList = TaskList ()

myTaskList.tasks.append (myTask)

la riket = Realm ()

prøve! realm.write {

realm.add ([myTask, myTaskList])

}

Derfra tar det ikke mye å bygge ut en mer fullt funksjonell app rundt en TableViewController:

importer UIKit

importer RealmSwift

klasse TaskListTableViewController: UITableViewController {

var realm = prøv! Rike ()

var taskList = TaskList ()

overstyre func viewDidLoad () {

super.viewDidLoad ()

skriv ut (Realm.Configuration.defaultConfiguration.fileURL!)

// Her kan du erstatte self.taskList med et tidligere lagret TaskList-objekt

prøve! realm.write {

realm.add (self.taskList)

       }

// legg til navbar +

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

   }

func displayTaskAlert () {

// lage og vise et varsel som tar navn og gjør en oppgave.

let alert = UIAlertController (tittel: "Lag en oppgave", melding: "Hva vil du kalle det?", foretrukket Style: UIAlertControllerStyle.alert)

alert.addTextField (configurationHandler: nil)

alert.addAction (UIAlertAction (tittel: “Avbryt”, stil: UIAlertActionStyle.cancel, handler: null))

alert.addAction (UIAlertAction (tittel: "Opprett oppgave", stil: UIAlertActionStyle.default, handler: {(handling) i

la oppgave = oppgave ()

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

prøve! self.realm.write {

self.realm.add (oppgave)

self.taskList.tasks.append (oppgave)

           }

self.tableView.reloadData ()

       }))

self.present (alert, animated: true, fullføring: null)

   }

overstyre func didReceiveMemoryWarning () {

super.didReceiveMemoryWarning ()

   }

overstyre func numberOfSections (i tableView: UITableView) -> Int {

retur 1

   }

overstyre func tableView (_ tableView: UITableView, numberOfRowsInSection seksjon: Int) -> Int {

returner self.taskList.tasks.count

   }

overstyre 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

returcelle

   }

}

Det er alt som trengs for å komme i gang! Du kan bli mye smartere med Realms samling og objektvarsler, slik at du kan laste inn intelligent på nytt tableView når et objekt blir lagt til eller slettet, men foreløpig har vi utholdenhet - grunnlaget for en offline-første app.

Serialisering og deserialisering

En offline-første app er ikke mye av en offline-første app med mindre den også kan gå online, og det kan være litt vanskelig å få data til og fra Realm.

Først og fremst er det viktig å matche klientskjemaet ditt så nært som serverens skjema. Gitt hvordan de fleste back-end-databaser fungerer, vil det sannsynligvis innebære å legge til et primærnøkkelfelt i Realm-klassen din, da Realm-objekter som standard ikke har en primærnøkkel.

Når du har matchet skjemaet ditt godt, trenger du en måte å deserialisere data som kommer fra serveren til Realm, og å serialisere data til JSON for å sende tilbake til serveren. Den enkleste metoden for å gjøre det er å velge ditt favorittmodellkartbibliotek og la det løfte tungt. Swift har Argo, Decodable, ObjectMapper og Mapper. Nå når du får svar fra serveren din, lar du bare modellmapperen dekode den til et innfødt RealmObject.

Likevel er det ikke så bra en løsning. Du må fremdeles skrive massevis av nettverkskode for å få JSON til og fra serveren din trygt i utgangspunktet, og modellmapperkoden din vil trenge omskriving og feilsøking når skjemaet endres. Det burde være en bedre måte, og vi tror Realm Mobile Platform er akkurat det.

Jobber med Realm Mobile Platform

Realm Mobile Platform (RMP) gir deg sanntids synkronisering slik at du kan fokusere på å bygge en mobilapp, ikke kjempe for å få serveren og appen til å snakke. Du tar ganske enkelt Realm-modellen din ovenfor, legger til RMPs brukerautentisering, og lar RMP ta seg av synkronisering av data mellom serveren og appens riker. Så fortsetter du ganske enkelt å jobbe med innfødte Swift-objekter.

For å komme i gang, last ned og installer Realm Mobile Platform MacOS-pakken, som lar deg få en Realm Object Server-forekomst som går på din Mac veldig raskt. Deretter legger vi til noen få ting i vår oppgaveliste-app for å få den til å koble seg til Realm Object Server.

Når du er ferdig med å følge installasjonsinstruksjonene ovenfor, bør du ha serveren i gang og en administratorbruker på //127.0.0.1:9080. Husk legitimasjonen, og vi kommer tilbake til Swift-koden.

Før vi skriver mer kode, må vi gjøre to små endringer i prosjektet. Først må vi gå til appens målredigerer i Xcode, og i kategorien Evner aktiverer du nøkkelringdelingsbryteren.

Deretter må vi tillate forespørsler om ikke-TLS-nettverk. Gå til prosjektets Info.plist-fil og legg til følgende inne i tagger:

NSAppTransportSecurity

NSAllowsArbitraryLoads

   

$config[zx-auto] not found$config[zx-overlay] not found