Programmering

Byg offline-første mobile apps uden smerte

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

Det er en sandhed, der er almindeligt anerkendt, at en bruger i besiddelse af en smartphone skal være i mangel af en bedre forbindelse. På trods af milliarder dollars af infrastrukturinvesteringer og ubarmhjertig teknologisk innovation tager det ikke meget mere end en kort køretur at lægge mærke til en væsentlig realitet i den tilsluttede æra: Du kan ikke antage, at en netværksforbindelse vil være tilgængelig, hver gang du ønsker det. Som mobile udviklere er det en sandhed, der er praktisk at ignorere.

Offline-tilstande i apps kan være forvirrende at håndtere, men problemet begynder med en grundlæggende og forkert antagelse - at offline som standard er en fejltilstand. Det var fornuftigt, da vi byggede apps til stationære computere med dedikerede Ethernet-uplinks. Det giver ikke mening, når lukningen af ​​en elevators døre gør en app helt ubrugelig, eller når det er rimeligt at forvente, at din applikation vil blive brugt på steder, der mangler en pålidelig mobilinfrastruktur.

Vi kan ikke dække verden i dækning, så vi er nødt til at tilbyde et alternativ. Vi er nødt til at tænke offline-først. Vi er nødt til at designe apps, så de er nyttige offline. Vi er nødt til at oprette apps, der udnytter internettet fuldt ud, når det er tilgængeligt, men forstår, at internetadgang altid er midlertidig. Vi er nødt til at tage smarte designbeslutninger, der involverer offline-stater, og gøre disse offline-stater forståelige for brugerne.

Der arbejdes meget med at definere den offline-første fremtid. Realm, det firma, hvor jeg arbejder, har i nogen tid opbygget en platform i realtid til offline-første mobile apps. Vores mobile database og Realm Mobile Platform gør det nemt at oprette intelligente, offline-første apps på næsten enhver mobil enhed. Folkene på A List Apart har bidraget enormt til offline-første litteratur, især til webapps. Og udviklerfællesskaberne i de store mobile økosystemer har brugt mange timer på at tilbyde imponerende open source-løsninger.

Det følgende er en kort introduktion til, hvordan du kan oprette en offline-første mobilapp. Jeg trækker på nogle enkle Swift-prøvekoder mod slutningen for at vise, hvordan en minimal offline-første app ser ud, men de principper og problemer, der tilbydes her, er relevante for alle, der arbejder med udvikling af mobilapps.

Design til offline-først

Inden du opretter den offline-første app, du altid har ønsket, skal vi se på designløsningerne, der giver mening til desktops med meget stor sandsynlighed for at være online. Hvis din app kan håndtere offline- og online-tilstande, har vi spørgsmål at svare på, hvad den kan gøre, og hvordan vi viser brugeren, hvad der er muligt.

Definer hvad der er muligt offline

Lad os tage Twitter som et eksempel. Hvis du er offline, og du sender en tweet, kan en offline-første Twitter-klient tage to veje. Det kunne sætte tweeten i kø, indtil den genopretter forbindelse. Eller det kan nægte at lade dig tweet - selvom det lader dig sætte andre handlinger i kø som faves i kø, som Tweetbot gør.

Hvorfor ville Tweetbot forhindre dig i at tweete offline? Måske fordi når du kommer tilbage online, er dine tweets muligvis ikke mere relevante. At løse dette problem vil indebære at oprette et nyt brugergrænseflade til en liste over tweets, som du endnu ikke har sendt, men som du muligvis skal redigere eller slette, før de går online. Hvis du på den anden side hjerter en tweet, er det usandsynligt, at du vil fortryde det, hvis du konfronteres med mere information - og meget mindre problematisk at blot angive, at det er i kø til udstationering.

Du kan ikke få en offline-app til at gøre alt, hvad en online-app kan, men du kan gøre det nyttigt.

Design væk konflikter

Uanset hvilken strategi du bruger i bagenden for at forene ændringer, vil din app stå over for et punkt, hvor du har to modstridende data. Måske er det fordi serveren styrtede ned, eller fordi du og en anden person foretog offlineændringer og nu vil synkronisere dem. Alt kan ske!

Forvent således konflikter og stræb efter at løse dem på en forudsigelig måde. Tilbyd valg. Og prøv at undgå konflikter i første omgang.

At være forudsigelig betyder, at dine brugere ved, hvad der kan ske. Hvis der kan opstå en konflikt, når brugere redigerer to steder på én gang, når de er offline, skal de advares om det, når de er offline.

At tilbyde valg betyder ikke blot at acceptere den sidste skrivning eller sammenkæde ændringer eller slette den ældste kopi. Det betyder at lade brugeren beslutte, hvad der er passende.

Endelig er den bedste løsning aldrig at lade konflikter udvikle sig i første omgang. Måske betyder det at opbygge din app på en måde, så nye og underlige data fra mange kilder ikke fører til en konflikt og i stedet vises nøjagtigt, som du vil have det til. Det kan være svært at gøre i en skriveapp, der går online og offline, men en delt tegningsapp kan arkiveres for at tilføje nye stier til tegningen, når de bliver synkroniseret.

Vær eksplicit

Det er en ting at definere, hvad brugeren kan gøre offline. Et helt andet problem indebærer at gøre disse beslutninger forståelige for dine brugere. Manglende succes med at kommunikere tilstanden for dine data og tilslutningsmuligheder eller tilgængeligheden af ​​givne funktioner svarer til fejl i at have bygget en offline-første app i første omgang.

En delt app til notering illustrerer problemet. Hvis du går offline, men forventer, at samarbejdspartnere fortsætter med at redigere i appen i dit fravær, er det ikke nok bare at lade en bruger fortsætte med at skrive, indtil de er tilfredse. Når de opretter forbindelse igen, vil de blive overrasket over konflikter, der har udviklet sig.

Hjælp i stedet din bruger med at træffe den rigtige beslutning. Hvis du ser, at din serverforbindelse er blevet afbrudt, fordi din apps øverste bjælke skifter farve, ved du, hvad der kunne komme: flet konflikter! Det kan være fint det meste af tiden, og din apps brugergrænseflade kan hjælpe med at afhjælpe uventede konflikter, når du kommer tilbage online. Men hvis du mister forbindelse, når flere personer redigerer din app, ville det ikke være nyttigt at vide, at risikoen for konflikter er meget større? ”Du mistede forbindelsen, men andre redigerede. Fortsat redigering kan forårsage konflikter. ” Brugeren kan fortsætte, men kender risikoen.

Det er let at skrive uendeligt om designproblemer og løsninger, men før vi kommer for langt fra de værktøjer, vi bliver nødt til at bruge, kan det være nyttigt at se, hvordan det er at oprette en offline-første mobilapp.

Byg en offline-første app med Realm

Arkitekturen i en grundlæggende offline-første app er ikke fancy. Du har brug for en måde at fastholde data i appen (ved hjælp af en enhedsdatabase), en protokol til at kommunikere med en server (inklusive serialisering og deserialiseringskode, hvis det er nødvendigt), og den server, hvor de synkroniserede data vil leve, så det kan være distribueres til den, der har tilladelse.

Først vil jeg gå igennem, hvordan du kommer i gang med Realm Mobile Database i en iOS-app (selvom koden ikke ville se meget anderledes ud i en Android-app). Derefter præsenterer jeg en strategi til serialisering og deserialisering af kode, som du får fra en server og gemmer i din lokale Realm-database. Endelig viser jeg dig, hvordan du får det hele til at fungere sammen i en samarbejdsopgaveliste-app, der synkroniseres i realtid.

Realm Mobile Database

Det er let at komme i gang med Realm. Du installerer Realm Mobile Database og definerer derefter dit skema ved at lave klasser. Da Realm er en objektdatabase, er det virkelig så simpelt som at lave klasser, instantere nogle objekter og videregive disse objekter til en skrive blokere for at fortsætte dem til disk. Ingen serialisering eller ORM er påkrævet, plus det er hurtigere end Apples Core Data.

Her er kernen i vores model og den mest basale mulige opgaveliste-app (som du bliver nødt til at kompilere hver gang du vil lave en ny opgave):

importer RealmSwift

klasse Opgave: Objekt {

dynamisk var navn

}

class TaskList: Object {

lad opgaver = Liste ()

}

lad myTask = Opgave ()

myTask.task

lad myTaskList = TaskList ()

myTaskList.tasks.append (myTask)

lad rige = Realm ()

prøve! realm.write {

realm.add ([myTask, myTaskList])

}

Derfra tager det ikke meget at opbygge en mere fuldt funktionel app omkring en TableViewController:

importer UIKit

importer RealmSwift

klasse TaskListTableViewController: UITableViewController {

var realm = prøv! Rige()

var taskList = TaskList ()

tilsidesætte func viewDidLoad () {

super.viewDidLoad ()

udskrive (Realm.Configuration.defaultConfiguration.fileURL!)

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

prøve! realm.write {

realm.add (self.taskList)

       }

// tilføj navbar +

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

   }

func displayTaskAlert () {

// lav og vis en advarsel, der tager et navn og laver en opgave.

let alarm = UIAlertController (titel: "Lav en opgave", besked: "Hvad vil du kalde det?", foretrukket Style: UIAlertControllerStyle.alert)

alert.addTextField (configurationHandler: nul)

alert.addAction (UIAlertAction (titel: "Annuller", stil: UIAlertActionStyle.cancel, handler: nul))

alert.addAction (UIAlertAction (titel: "Opret opgave", stil: UIAlertActionStyle.default, handler: {(handling) i

lad opgave = Opgave ()

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

prøve! self.realm.write {

self.realm.add (opgave)

self.taskList.tasks.append (opgave)

           }

self.tableView.reloadData ()

       }))

self.present (alarm, animeret: sand, afslutning: nul)

   }

tilsidesætte func didReceiveMemoryWarning () {

super.didReceiveMemoryWarning ()

   }

tilsidesætte func numberOfSections (i tableView: UITableView) -> Int {

retur 1

   }

tilsidesætte func tableView (_ tableView: UITableView, numberOfRowsInSection sektion: Int) -> Int {

returner self.taskList.tasks.count

   }

tilsidesætte func tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

lad celle = tabelView.dequeueReusableCell (medIdentifier: "genbrugsidentifikator", til: indexPath)

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

returcelle

   }

}

Det er alt, hvad der kræves for at komme i gang! Du kan blive meget klogere med Realms samling og objektnotifikationer, så du kan genindlæse intelligent tabelvisning når et objekt tilføjes eller slettes, men indtil videre har vi vedholdenhed - grundlaget for en offline-første app.

Serialisering og deserialisering

En offline-første app er ikke meget af en offline-første app, medmindre den også kan gå online, og det kan være lidt vanskeligt at få data til og fra Realm.

Først og fremmest er det vigtigt at matche dit klientskema så tæt på din servers skema. I betragtning af hvordan de fleste backend-databaser fungerer, vil det sandsynligvis indebære at tilføje et primært nøglefelt til din Realm-klasse, da Realm-objekter som standard ikke har en primær nøgle.

Når du først har matchet dit skema, har du brug for en måde at deserialisere data, der kommer fra serveren til Realm, og at serialisere data til JSON for at sende tilbage til serveren. Den nemmeste metode til at gøre det er at vælge dit foretrukne modelkortbibliotek og lade det gøre det tunge løft. Swift har Argo, Decodable, ObjectMapper og Mapper. Når du nu får svar fra din server, lader du simpelthen modelmapperen afkode det til et oprindeligt RealmObject.

Alligevel er det ikke så god en løsning. Du er stadig nødt til at skrive masser af netværkskoder for at få JSON til og fra din server sikkert i første omgang, og din modelkortkode skal omskrivning og fejlretning når som helst dit skema ændres. Der burde være en bedre måde, og vi tror, ​​at Realm Mobile Platform er netop det.

Arbejde med Realm Mobile Platform

Realm Mobile Platform (RMP) giver dig synkronisering i realtid, så du kan fokusere på at opbygge en mobilapp og ikke kæmpe for at få serveren og appen til at tale. Du tager simpelthen din Realm-model ovenfor, tilføjer RMP's brugergodkendelse og lader RMP sørge for at synkronisere data mellem serveren og din apps rige. Derefter fortsætter du simpelthen med at arbejde med native Swift-objekter.

For at komme i gang skal du downloade og installere Realm Mobile Platform MacOS-pakken, som giver dig mulighed for at få en Realm Object Server-forekomst på din Mac virkelig hurtigt. Derefter tilføjer vi et par ting til vores opgaveliste-app for at få den til at oprette forbindelse til Realm Object Server.

Når du er færdig med at følge installationsinstruktionerne ovenfor, skal du have serveren kørende og en administratorbruger på //127.0.0.1:9080. Husk disse legitimationsoplysninger, og vi vender tilbage til vores Swift-kode.

Før vi skriver mere kode, skal vi foretage to små ændringer i projektet. Først skal vi gå til vores apps måleditor i Xcode, og i fanen Kapaciteter skal du aktivere kontakten Deling af nøglering.

Derefter skal vi tillade ikke-TLS-netværksanmodninger. Gå til projektets Info.plist-fil, og tilføj følgende inden i tags:

NSAppTransportSecurity

NSAllowsArbitraryLoads

   

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