Изграждайте офлайн първи мобилни приложения без болка

Александър Стигсен е съосновател и главен изпълнителен директор на Realm.

Универсално призната истина е, че потребителят, притежаващ смартфон, трябва да иска по-добра връзка. Въпреки милиарди долари инвестиции в инфраструктура и безмилостни технологични иновации, не е нужно много повече от кратко пътуване, за да забележите съществена реалност на свързаната ера: Не можете да предположите, че мрежовата връзка ще бъде достъпна всеки път, когато искате. Като мобилни разработчици е истина, която е удобна за игнориране.

Офлайн състоянията в приложенията може да са объркващи, но проблемът започва с основно и неправилно предположение - че офлайн по подразбиране е състояние на грешка. Това имаше смисъл, когато създадохме приложения за настолни компютри със специални връзки към Ethernet. Няма смисъл, когато затварянето на вратите на асансьора прави приложението напълно безполезно или когато е разумно да се очаква, че приложението ви ще се използва на места, които нямат надеждна клетъчна инфраструктура.

Не можем да покрием света с покритие, затова трябва да предложим алтернатива. Първо трябва да мислим офлайн. Трябва да проектираме приложения, за да бъдем полезни офлайн. Трябва да създадем приложения, които да се възползват напълно от интернет, когато той е достъпен, но разбираме, че достъпът до интернет винаги е временен. Трябва да вземем решения за интелигентен дизайн, включващи офлайн състояния и да направим тези офлайн състояния разбираеми за потребителите.

Работи се много, за да се определи бъдещето офлайн първо. Realm, компанията, в която работя, от известно време изгражда платформа в реално време за първи офлайн мобилни приложения. Нашата мобилна база данни и мобилната платформа Realm улесняват създаването на интелигентни, първоначално приложени офлайн приложения на почти всяко мобилно устройство. Хората от A List Apart са допринесли изключително много за литературата за офлайн, особено за уеб приложения. А общностите на разработчиците на основните мобилни екосистеми са прекарали много часове, предлагайки впечатляващи собствени решения с отворен код.

Следва кратко въведение в това как можете да създадете офлайн първо мобилно приложение. Ще изтегля няколко прости примерни кода на Swift към края, за да покажа как изглежда едно минимално първо приложение за офлайн, но принципите и проблемите, предлагани тук, са от значение за всеки, който работи в разработването на мобилни приложения.

Дизайн за офлайн първо

Преди да създадете първоначално офлайн приложението, което винаги сте искали, трябва да преразгледаме дизайнерските решения, които са имали смисъл за настолни компютри с много голяма вероятност да бъдат онлайн. Ако вашето приложение може да се справя офлайн и онлайн, имаме въпроси, на които да отговорим какво може да направи и как показваме на потребителя какво е възможно.

Определете какво е възможно офлайн

Да вземем Twitter за пример. Ако сте офлайн и публикувате туит, клиентът за първи път в Twitter, който е офлайн, може да поеме по два пътя. Той може да постави на опашката туита, докато възстанови връзката. Или може да откаже да ви позволи да туитвате - дори ако ви позволява да поставяте в опашка други действия, като например любими, както прави Tweetbot.

Защо Tweetbot би ви попречил да пишете офлайн? Може би защото, докато се върнете онлайн, вашите туитове вече не са актуални. Решаването на този проблем би включвало създаването на нов потребителски интерфейс за списък с туитове, които все още не сте публикували, но които може да се наложи да редактирате или изтриете, преди да влязат в мрежа. Ако сте чули туит, от друга страна, е малко вероятно да го отмените, ако се сблъскате с повече информация - и много по-малко проблематично просто да посочите, че е поставен в опашката за публикуване.

Не можете да накарате офлайн приложението да прави всичко, което онлайн приложението може, но можете да го направите полезно.

Проектирайте конфликти

Независимо от стратегията, която използвате на задната страна, за да съгласувате промените, приложението ви ще се изправи пред момент, в който имате две противоречащи си данни. Може би защото сървърът се е сринал или защото вие и друг човек сте направили офлайн промени и сега искате да ги синхронизирате. Всичко може да се случи!

По този начин изпреварвайте конфликтите и се стремете да ги разрешите по предсказуем начин. Предлагайте избор. И първо се опитайте да избегнете конфликти.

Предвидимостта означава, че вашите потребители знаят какво може да се случи. Ако може да възникне конфликт, когато потребителите редактират наведнъж на две места, когато са офлайн, тогава те трябва да бъдат предупредени за това, когато са офлайн.

Предлагането на избор означава не просто приемане на последното записване или обединяване на промените или изтриване на най-старото копие. Това означава да оставите потребителя да реши какво е подходящо.

И накрая, най-доброто решение е никога да не позволявате на конфликтите да се развиват на първо място. Може би това означава да изградите приложението си по начин, така че новите и странни данни от много източници да не водят до конфликт и вместо това да се показват точно както бихте искали. Това може да е трудно да се направи в приложение за писане, което преминава онлайн и офлайн, но споделено приложение за рисуване може да бъде създадено, за да добавя нови пътища към чертежа, когато се синхронизират.

Бъдете изрични

Едно е да дефинирате какво може да прави потребителят офлайн. Съвсем друг проблем включва вземането на тези решения разбираеми за вашите потребители. Неуспехът да комуникирате успешно състоянието на вашите данни и свързаност или наличността на дадени функции е равносилно на провал при създаването на първоначално офлайн първо приложение.

Споделеното приложение за водене на бележки илюстрира проблема. Ако излезете офлайн, но очаквате сътрудниците да продължат да редактират в приложението във ваше отсъствие, не е достатъчно просто да позволите на потребителя да продължи да пише, докато не се зарадва. Когато се свържат отново, ще бъдат изненадани от възникналите конфликти.

Вместо това помогнете на вашия потребител да вземе правилното решение. Ако видите, че връзката ви със сървъра е прекъсната, защото горната лента на приложението ви променя цвета си, знаете какво може да предстои: конфликти на сливане! През повечето време това може да е добре и потребителският интерфейс на приложението ви може да помогне за отстраняване на неочаквани конфликти, когато се върнете онлайн. Но ако загубите свързаност, когато множество хора редактират приложението ви, не би ли било полезно да знаете, че рискът от конфликти е много по-голям? „Изгубихте връзка, но други редактираха. Продължаването на редактирането може да доведе до конфликти. " Потребителят може да продължи, но знае риска.

Лесно е да пишете безкрайно за дизайнерски проблеми и решения, но преди да се отдалечим твърде много от инструментите, които ще трябва да използваме, може да е полезно да разберем какво е да създадем офлайн първо мобилно приложение.

Създайте първо офлайн приложение с Realm

Архитектурата на основно офлайн първо приложение не е изискана. Нуждаете се от начин за запазване на данни в приложението (с помощта на база данни на устройството), протокол за комуникация със сървър (включително код за сериализация и десериализация, ако е необходимо) и сървър, на който ще живеят синхронизираните данни, така че да могат да бъдат разпределя се на този, който има разрешение.

Първо ще ви преведа как да започнете с Realm Mobile Database в приложение за iOS (макар че кодът няма да изглежда много по-различно в приложение за Android). След това ще представя стратегия за сериализиране и десериализиране на код, който получавате от сървър и съхранявате във вашата локална база данни на Realm. И накрая, ще ви покажа как да накарате всичко да работи заедно в съвместно приложение със списък със задачи, което се синхронизира в реално време.

Realm Mobile база данни

Лесно е да започнете с Realm. Инсталирате базата данни Realm Mobile, след което дефинирате схемата си, като правите класове. Тъй като Realm е обектна база данни, това е наистина толкова просто, колкото създаването на класове, създаването на инстанции на някои обекти и предаването на тези обекти в writeблок, за да ги запази на диск. Не е необходима сериализация или ORM, освен това е по-бърза от основните данни на Apple.

Ето ядрото на нашия модел и най-основното възможно приложение за списък със задачи (което ще трябва да прекомпилирате всеки път, когато искате да направите нова задача):

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

}

Оттам нататък не е нужно много, за да се изгради по-напълно функционално приложение около 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

   }

}

Това е всичко, за да започнете! Можете да получите много по-акъл с известията за събиране и обекти на Realm, така че можете да презаредите интелигентно времето, tableViewкогато даден обект е добавен или изтрит, но засега имаме постоянство - основата на офлайн първо приложение.

Сериализация и десериализация

Приложението офлайн първо не е много приложение офлайн първо, освен ако не може да се включи и онлайн, а получаването на данни до и от Realm може да е малко сложно.

На първо място, съвпадението на вашата клиентска схема толкова близо до схемата на вашия сървър е от решаващо значение. Като се има предвид как работят повечето бази данни от back-end, това вероятно ще включва добавяне на поле за първичен ключ към вашия клас Realm, тъй като Realm обектите по подразбиране нямат първичен ключ.

След като схемата Ви съвпадне добре, имате нужда от начин за десериализиране на данните, идващи от сървъра, в Realm и за сериализиране на данни в JSON, за да ги изпратите обратно на сървъра. Най-лесният метод да го направите е да изберете любимата си библиотека за картографиране на модели и да я оставите да прави тежкото повдигане. Swift има Argo, Decodable, ObjectMapper и Mapper. Сега, когато получите отговор от вашия сървър, вие просто оставяте модела на mapper да го декодира в естествен RealmObject.

И все пак не е толкова страхотно решение. Все още трябва да напишете тон мрежов код, за да получите JSON до и от вашия сървър безопасно на първо място и кодът на вашия модел за картографиране ще се нуждае от пренаписване и отстраняване на грешки по всяко време, когато вашата схема се промени. Трябва да има по-добър начин и ние смятаме, че Realm Mobile Platform е точно това.

Работа с мобилната платформа Realm

Мобилната платформа Realm (RMP) ви дава синхронизация в реално време, така че да можете да се съсредоточите върху изграждането на мобилно приложение, а не да се борите да накарате сървъра и приложението да говорят. Просто вземате вашия модел Realm по-горе, добавяте удостоверяване на потребителя на RMP и оставяте RMP да се грижи за синхронизирането на данни между сървъра и областите на вашето приложение. След това просто продължавате да работите с местни Swift обекти.

За да започнете, изтеглете и инсталирайте пакета Realm Mobile Platform MacOS, който ви позволява да стартирате екземпляр на Realm Object Server на вашия Mac наистина бързо. След това ще добавим няколко елемента към нашето приложение за списък със задачи, за да се свържем със сървъра на Realm Object.

След като приключите с инструкциите за инсталиране по-горе, трябва да стартирате сървъра и потребител на администратор на //127.0.0.1:9080. Запомнете тези идентификационни данни и ние ще се върнем към нашия Swift код.

Преди да напишем повече код, трябва да направим две малки промени в проекта. Първо, трябва да отидем в целевия редактор на приложението ни в Xcode и в раздела Capabilities да активираме превключвателя Keychain Sharing.

След това ще трябва да разрешим мрежови заявки, които не са TLS. Отидете на файла Info.plist на проекта и добавете следното в етикетите:

NSAppTransportSecurity

   NSAllowsArbitraryLoads