Як синхронізувати основні дані iPhone з веб-сервером, а потім перейти на інші пристрої? [зачинено]


293

Я працюю над методом синхронізації основних даних, що зберігаються в додатку iPhone, між декількома пристроями, такими як iPad або Mac. Існує не так багато (якщо вони взагалі є) фреймворків синхронізації для використання з Core Data на iOS. Однак я думав про таку концепцію:

  1. Зміни вносяться до локального сховища основних даних, і зміна зберігається. (a) Якщо пристрій в Інтернеті, він намагається надіслати набір змін на сервер, включаючи ідентифікатор пристрою пристрою, який надіслав набір змін. (b) Якщо набір змін не доходить до сервера або якщо пристрій не в мережі, додаток додасть зміни, встановлені до черги, яку потрібно надіслати, коли він з’явиться в Інтернеті.
  2. Сервер, сидячи в хмарі, об'єднує конкретні набори змін, які він отримує, з його основною базою даних.
  3. Після того як набір змін (або черга наборів змін) об'єднується на хмарний сервер, сервер пересилає всі ці набори змін на інші пристрої, зареєстровані на сервері, використовуючи якусь систему опитування. (Я думав використовувати сервіси Push Apple, але, мабуть, за коментарями, це не є працездатною системою.)

Чи є щось фантазійне, про що мені потрібно думати? Я розглянув рамки REST, такі як ObjectiveResource , Core Resource та RestfulCoreData . Звичайно, це все, що працюють з Ruby on Rails, до яких я не прив’язаний, але це місце для початку. Основні вимоги, що пред'являються до мого рішення:

  1. Будь-які зміни слід надсилати у фоновому режимі, не призупиняючи основний потік.
  2. Він повинен використовувати якомога меншу пропускну здатність.

Я продумав низку проблем:

  1. Переконайтесь, що ідентифікатори об’єктів для різних сховищ даних на різних пристроях приєднані на сервері. Тобто у мене буде таблиця ідентифікаторів об’єктів та ідентифікаторів пристроїв, які прив’язані через посилання на об'єкт, що зберігається в базі даних. У мене буде запис (DatabaseId [унікальний для цієї таблиці], ObjectId [унікальний для елемента у всій базі даних], Datafield1, Datafield2), поле ObjectId буде посилатися на іншу таблицю, AllObjects: (ObjectId, DeviceId, DeviceObjectId). Потім, коли пристрій підштовхує набір змін, він передаватиметься по ідентифікатору пристрою та по об'єктуId від об'єкта основних даних у локальному сховищі даних. Тоді мій хмарний сервер перевірить, чи не відповідає objectId та Id пристрою в таблиці AllObjects, і знайде запис, який слід змінити в початковій таблиці.
  2. Усі зміни мають бути відмічені часом, щоб їх можна було об'єднати.
  3. Пристрій повинен опитувати сервер, не використовуючи занадто багато акумулятора.
  4. Місцевим пристроям також потрібно буде оновити все, що зберігається в пам'яті, якщо / коли зміни надійдуть від сервера.

Чи ще чогось мені тут не вистачає? На які типи рамок слід звернути увагу, щоб зробити це можливим?


5
Ви не можете розраховувати на отримання Push-сповіщень. Користувач може просто натиснути їх, і коли надійде друге повідомлення, ОС відкидає перше. Поштові повідомлення IMO - це поганий спосіб отримувати оновлення синхронізації, так чи інакше, оскільки вони переривають користувача. Додаток повинен ініціювати синхронізацію кожного разу, коли він запускається.
Оле Бегеманн

ДОБРЕ. Дякую за інформацію - за винятком постійного опитування сервера та перевірки оновлень при запуску, чи існує спосіб, щоб пристрій отримував оновлення? Мені цікаво змусити його працювати, якщо програма відкрита на декількох пристроях одночасно.
Джейсон

1
(Я знаю трохи пізно, але, якщо хтось стикається з цим, а також дивується), щоб синхронізувати кілька пристроїв одночасно, ви могли б підтримувати відкрите з'єднання або з іншим пристроєм, або з сервером, і надсилати повідомлення, щоб повідомити іншому пристрою ), коли відбувається оновлення. (наприклад, спосіб роботи IRC / обміну миттєвими повідомленнями)
Dan2552

1
@ Dan2552: те, що ви описуєте, відоме як [довгий опитування] [ en.wikipedia.org/wiki/… і є чудовою ідеєю, проте відкриті з'єднання вимагають досить багато акумулятора та пропускної здатності на мобільному пристрої.
johndodo

1
Ось хороший підручник від Рей Вендерліха про те, як синхронізувати дані між вашим додатком та веб-службою: raywenderlich.com/15916/…
JRG-Developer

Відповіді:


144

Я пропоную уважно прочитати та реалізувати стратегію синхронізації, обговорену Деном Гровером на конференції iPhone 2009, доступну тут як документ у форматі PDF.

Це життєздатне рішення і його не так складно реалізувати (Ден реалізував це у кількох своїх програмах), перекриваючи рішення, описане Крісом. Для поглибленого, теоретичного обговорення синхронізації дивіться статтю від Russ Cox (MIT) та Вільяма Джозефсона (Princeton):

Синхронізація файлів з парами векторного часу

що однаково добре застосовується до основних даних із деякими очевидними модифікаціями. Це забезпечує загалом набагато більш надійну та надійну стратегію синхронізації, але вимагає більше зусиль для правильної реалізації.

Редагувати:

Здається, що pdf-файл Grover більше недоступний (ламане посилання, березень 2015 року). ОНОВЛЕННЯ: посилання доступне через Machine Back Machine тут

Рамка Objective-C під назвою ZSync та розроблена Маркусом Заррою була застарілою, враховуючи, що iCloud нарешті підтримує правильну синхронізацію основних даних.


Хтось має оновлене посилання на відео ZSync? Також зберігається ZSync? Я бачу, що він востаннє оновлювався у 2010 році.
Джеремі Велдін

Останнє зобов'язання ZSync щодо github відбулося у вересні 2010 року, що призводить мене до того, що Маркус перестав його підтримувати.
швидкопсувний Дейв

1
Алгоритм, описаний Деном Гровером, досить хороший. Однак він не працюватиме з багатопотоковим кодом сервера (таким чином: це зовсім не буде масштабуватися), оскільки немає способу переконатися, що клієнт не пропустить оновлення, коли буде використаний час для перевірки нових оновлень. . Будь ласка, виправте мене, якщо я помиляюся - я вбив би, щоб побачити, як це працює.
масі

1
@Patt, я щойно надіслав вам файл pdf, як вимагали. Ура, Массімо Кафаро.
Массімо Кафаро

3
Відсутній Cross-Platform Синхронізація даних PDF слайди Ден Гровер доступні через Wayback Machine.
Меттью Каїріс

272

Я зробив щось подібне до того, що ти намагаєшся зробити. Дозвольте розповісти, що я дізнався і як це зробив.

Я припускаю, що між вашим об'єктом Core Data та моделлю (або db-схемою) на сервері у вас є взаємозв'язок. Ви просто хочете, щоб вміст сервера синхронізувався з клієнтами, але клієнти також можуть змінювати та додавати дані. Якщо я мав таке право, тоді читайте.

Я доповнив чотири поля для сприяння синхронізації:

  1. sync_status - додайте це поле лише до вашої основної моделі даних. Додаток використовується для визначення того, чи є у вас зміни в очікуванні. Я використовую наступні коди: 0 означає відсутність змін, 1 означає, що в черзі для синхронізації з сервером, а 2 означає, що це тимчасовий об'єкт і його можна очистити.
  2. is_deleted - додайте це до серверної та основної моделі даних. Подія "Видалення" насправді не повинна видаляти рядок із бази даних або з вашої клієнтської моделі, оскільки це не дає нічого синхронізувати назад. Маючи цей простий булевий прапор, ви можете встановити is_deleted на 1, синхронізувати його, і всі будуть раді. Ви також повинні змінити код на сервері та клієнті, щоб запитувати не видалені елементи з "is_deleted = 0".
  3. last_modified - додайте це до серверної та основної моделі даних. Це поле повинно автоматично оновлюватися сервером поточної дати та часу, коли що-небудь змінюється на цьому записі. Він ніколи не повинен змінюватися клієнтом.
  4. guide - Додайте поле глобального унікального ідентифікатора (див. http://en.wikipedia.org/wiki/Globally_unique_identifier ) до сервера та основної моделі даних. Це поле стає первинним ключем і стає важливим при створенні нових записів про клієнта. Зазвичай ваш основний ключ - це наростаюче ціле число на сервері, але ми маємо пам’ятати, що вміст можна створити в автономному режимі та синхронізувати пізніше. GUID дозволяє нам створити ключ, перебуваючи в автономному режимі.

На клієнті додайте код, щоб встановити sync_status на 1 на об'єкті моделі, коли щось змінюється і потрібно синхронізуватись із сервером. Нові об'єкти моделі повинні генерувати GUID.

Синхронізація - це один запит. Запит містить:

  • MAX остання зміна часової позначки об'єктів вашої моделі. Це повідомляє серверу, що ви хочете зміни лише після цього часу.
  • Масив JSON, що містить усі елементи з sync_status = 1.

Сервер отримує запит і робить це:

  • Він бере вміст з масиву JSON і змінює або додає записи, які він містить. Поле last_modified автоматично оновлюється.
  • Сервер повертає масив JSON, що містить усі об'єкти з часовою позначкою last_modified, що перевищує часову марку, надіслану в запиті. Сюди включатимуться щойно отримані об'єкти, які служать підтвердженням того, що запис був успішно синхронізований із сервером.

Додаток отримує відповідь і робить це:

  • Він бере вміст з масиву JSON і змінює або додає записи, які він містить. Кожен запис отримує встановити sync_status 0.

Я сподіваюся, що це допомагає. Я використав слово запис і модель взаємозамінно, але я думаю, ви зрозуміли, що ви зрозуміли. Удачі.


2
Поле last_modified також існує у локальній базі даних, але воно не оновлюється годинником iPhone. Він встановлюється сервером і синхронізується назад. MAX (last_modified) дата - це те, що додаток надсилає на сервер, щоб сказати, щоб він повернув все, що було змінено після цієї дати.
chris

3
Глобальне значення для клієнта може замінити MAX(last_modified), але це буде зайвим, оскільки MAX(last_modified)достатньо. sync_statusМає іншу роль. Як я писав раніше, MAX(last_modified)визначає, що потрібно синхронізувати з сервером, а sync_statusвизначає, що потрібно синхронізувати з сервером.
chris

2
@Flex_Addicted Дякую Так, вам потрібно буде повторити поля для кожної сутності, яку ви хочете синхронізувати. Однак потрібно більше уважно ставитись під час синхронізації моделі з відносинами (наприклад, «1 до багатьох»).
chris

2
@BenPackard - Ви маєте рацію. Підхід не вирішує конфлікт, тому останній клієнт виграє. Мені не доводилося стикатися з цим у своїх додатках, оскільки записи редагує один користувач. Мені буде цікаво дізнатись, як ви вирішуєте це.
chris

2
Привіт @noilly, врахуйте такий випадок: Ви вносите зміни в локальний об'єкт і потрібно синхронізувати його назад на сервер. Синхронізація може відбутися лише через години чи дні пізніше (скажімо, якщо ви деякий час не в режимі офлайн), і в цей час додаток, можливо, було вимкнене та перезапущено кілька разів. У цьому випадку методи на NSManagedObjectContext не дуже допоможуть.
chris

11

Якщо ви все ще шукаєте спосіб пройти, загляньте на мобільний телефон Couchbase. Це в основному все, що ви хочете. ( http://www.couchbase.com/nosql-databases/couchbase-mobile )


3
Це робиться лише тим, що ви хочете, якщо ви можете висловити свої дані як документи, а не реляційні дані. Існують роботи навколо, але вони не завжди гарні або варті того.
Джеремі Велдін

Документів вистачає для невеликих додатків
Хай Фенг Као

@radiospiel Ваше посилання порушено
Мік

Це також додасть залежність, яку потрібно створити в резервній програмі Couchbase. Навіть я почав з ідеї NOSQL для синхронізації, але я не можу обмежити, щоб мій сервер був NOSQL, оскільки у нас працює MS SQL в бекенде.
thesmersmersign

@Mick: це, здається, працює знову (чи хтось полагодив посилання? Дякую)
radiospiel

7

Подібно до @Cris Я реалізував клас синхронізації між клієнтом і сервером і вирішив усі відомі досі проблеми (надсилати / отримувати дані на сервер / з сервером, об'єднувати конфлікти на основі часових позначок, видаляти дублюючі записи в ненадійних мережевих умовах, синхронізувати вкладені дані та файли тощо.)

Ви просто повідомте класу, яка сутність та які стовпці повинні синхронізуватися та де ваш сервер.

M3Synchronization * syncEntity = [[M3Synchronization alloc] initForClass: @"Car"
                                                              andContext: context
                                                            andServerUrl: kWebsiteUrl
                                             andServerReceiverScriptName: kServerReceiverScript
                                              andServerFetcherScriptName: kServerFetcherScript
                                                    ansSyncedTableFields:@[@"licenceNumber", @"manufacturer", @"model"]
                                                    andUniqueTableFields:@[@"licenceNumber"]];


syncEntity.delegate = self; // delegate should implement onComplete and onError methods
syncEntity.additionalPostParamsDictionary = ... // add some POST params to authenticate current user

[syncEntity sync];

Ви можете знайти джерело, робочий приклад та інші інструкції тут: github.com/knagode/M3Synchronization .


Чи буде нормально, якщо ми змінимо час роботи пристрою на ненормальне значення?
Золотий

5

Повідомте користувача про оновлення даних за допомогою push-повідомлення. Використовуйте фонову нитку в додатку, щоб перевірити локальні дані та дані на хмарному сервері, тоді як на сервері відбувається зміна, зміни місцевих даних, навпаки.

Тому я вважаю, що найскладнішою є оцінка даних, в якій стороні є недійсними.

Сподіваюсь, це може допомогти вам


5

Щойно я опублікував першу версію мого нового API даних для синхронізації хмарних даних, відомого як SynCloud. SynCloud має багато відмінностей з iCloud, оскільки він дозволяє багатокористувацькому інтерфейсу синхронізації. Він також відрізняється від інших api-файлів синхронізації тим, що дозволяє створювати релевантні дані з кількох таблиць.

Дізнайтеся більше на веб- сайті http://www.syncloudapi.com

Створюйте з iOS 6 SDK, це дуже актуально з 27.09.2012.


5
Ласкаво просимо до переповнення стека! Дякуємо, що опублікували свою відповідь! Будь ласка, уважно прочитайте FAQ щодо самореклами .
Ендрю Барбер

5

Я думаю, що гарне рішення проблеми GUID - це "розподілена система ідентифікаторів". Я не впевнений, що таке правильний термін, але я вважаю, що саме його використовували документи MS SQL-сервера (SQL використовує / використовував цей метод для розподілених / синхронізованих баз даних). Це досить просто:

Сервер призначає всі ідентифікатори. Щоразу, коли синхронізація виконується, перше, що перевіряється, - це "скільки ідентифікаторів у мене залишилось у цього клієнта?" Якщо клієнт не вистачає, він запитує у сервера новий блок ідентифікаторів. Потім клієнт використовує ідентифікатори в цьому діапазоні для нових записів. Це чудово підходить для більшості потреб, якщо ви можете призначити блок достатньо великий, щоб він "ніколи" не закінчувався до наступної синхронізації, але не був таким великим, що з часом вичерпується сервер. Якщо клієнт коли-небудь закінчується, обробка може бути досить простою, просто скажіть користувачеві "вибачте, що ви не можете додати більше елементів, поки ви не синхронізуєте" ... якщо вони додають багато елементів, чи не повинні вони синхронізуватись, щоб уникнути застарілих даних проблеми все одно?

Я думаю, що це чудово, ніж використання випадкових GUID, тому що випадкові GUID не є 100% безпечними і зазвичай мають бути набагато довше, ніж стандартні ідентифікатори (128-біт проти 32-біт). Зазвичай у вас є індекси за ідентифікатором і часто зберігаєте ідентифікаційні номери в пам'яті, тому важливо зберегти їх невеликими.

Не дуже хотів публікувати як відповідь, але я не знаю, що хтось бачив би це як коментар, і я думаю, що це важливо для цієї теми та не включено до інших відповідей.


2

Спочатку слід переосмислити, скільки даних, таблиць та відносин у вас буде. У своєму рішенні я реалізував синхронізацію через файли Dropbox. Я спостерігаю за змінами в основному MOC і зберігаю ці дані у файлах (кожен рядок зберігається як gzipped json). Якщо працює підключення до Інтернету, я перевіряю, чи є якісь зміни на Dropbox (Dropbox дає мені зміни в дельті), завантажую їх і зливаю (останні виграші) і, нарешті, ставлю змінені файли. Перед синхронізацією я помістив файл блокування на Dropbox, щоб запобігти синхронізації інших клієнтів неповних даних. Під час завантаження змін безпечно завантажувати лише часткові дані (наприклад, втрачене з'єднання з Інтернетом). Коли завантаження закінчено (повністю або частково), воно починає завантажувати файли в основні дані. Якщо є невирішені відносини (завантажуються не всі файли), він припиняє завантаження файлів і намагається закінчити завантаження пізніше. Відносини зберігаються лише як GUID, тому я можу легко перевірити, які файли потрібно завантажити, щоб мати повну цілісність даних. Синхронізація починається після внесення змін до основних даних. Якщо змін немає, він перевіряє зміни на Dropbox кожні кілька хвилин та під час запуску програми. Крім того, коли зміни надсилаються на сервер, я надсилаю трансляцію на інші пристрої, щоб інформувати їх про зміни, щоб вони могли швидше синхронізуватися. Кожна синхронізована сутність має властивість GUID (Guide використовується також як ім'я файлів для обміну файлами). У мене також є база даних Sync, де я зберігаю перегляд Dropbox кожного файлу (я можу порівняти його, коли дельта Dropbox скидає стан). Файли також містять назву сутності, стан (видалено / не видалено), наведення (те саме, що і ім’я файлу), перегляд бази даних (для виявлення міграцій даних або уникнення синхронізації з версіями, які ніколи не використовуються) та звичайно дані (якщо рядок не видалено). тож я можу легко перевірити, які файли потрібно завантажити, щоб мати повну цілісність даних. Синхронізація починається після внесення змін до основних даних. Якщо змін немає, він перевіряє зміни на Dropbox кожні кілька хвилин та під час запуску програми. Крім того, коли зміни надсилаються на сервер, я надсилаю трансляцію на інші пристрої, щоб інформувати їх про зміни, щоб вони могли швидше синхронізуватися. Кожна синхронізована сутність має властивість GUID (Guide використовується також як ім'я файлів для обміну файлами). У мене також є база даних Sync, де я зберігаю перегляд Dropbox кожного файлу (я можу порівняти його, коли дельта Dropbox скидає стан). Файли також містять назву сутності, стан (видалено / не видалено), наведення (те саме, що і ім’я файлу), перегляд бази даних (для виявлення міграцій даних або уникнення синхронізації з версіями, які ніколи не використовуються) та звичайно дані (якщо рядок не видалено). тож я можу легко перевірити, які файли потрібно завантажити, щоб мати повну цілісність даних. Синхронізація починається після внесення змін до основних даних. Якщо змін немає, він перевіряє зміни на Dropbox кожні кілька хвилин та під час запуску програми. Крім того, коли зміни надсилаються на сервер, я надсилаю трансляцію на інші пристрої, щоб інформувати їх про зміни, щоб вони могли швидше синхронізуватися. Кожна синхронізована сутність має властивість GUID (Guide використовується також як ім'я файлів для обміну файлами). У мене також є база даних Sync, де я зберігаю перегляд Dropbox кожного файлу (я можу порівняти його, коли дельта Dropbox скидає стан). Файли також містять назву сутності, стан (видалено / не видалено), наведення (те саме, що і ім’я файлу), перегляд бази даних (для виявлення міграцій даних або уникнення синхронізації з версіями, які ніколи не використовуються) та звичайно дані (якщо рядок не видалено).

Це рішення працює для тисяч файлів і близько 30 об'єктів. Замість Dropbox я міг би використовувати магазин ключів / цінностей як веб-сервіс REST, який я хочу зробити пізніше, але не маю на це часу :) На даний момент, на мій погляд, моє рішення надійніше, ніж iCloud і, що дуже важливо, Я маю повний контроль над тим, як це працює (головним чином тому, що це мій власний код).

Іншим рішенням є збереження змін MOC як транзакцій - файлів буде обмінюватися набагато менше, ніж сервер, але важче виконати початкове завантаження в належному порядку в порожні основні дані. iCloud працює таким чином, а також інші рішення синхронізації мають подібний підхід, наприклад, TICoreDataSync .

- ОНОВЛЕННЯ

Через деякий час я перейшов до ансамблів - рекомендую це рішення через винахід колеса.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.