Як синхронізувати CoreData та веб-службу REST асинхронно та одночасно правильно розповсюджувати будь-які помилки REST в інтерфейсі користувача


85

Гей, я працюю над шаром моделі для нашого додатка тут.

Деякі вимоги такі:

  1. Це повинно працювати на iPhone OS 3.0+.
  2. Джерелом наших даних є програма RESTful Rails.
  3. Ми повинні кешувати дані локально, використовуючи Core Data.
  4. Клієнтський код (наші контролери інтерфейсу користувача) повинен мати якомога менше знань про будь-які мережеві речі та запитувати / оновлювати модель за допомогою API основних даних.

Я перевірив сесію 117 WWDC10 щодо створення серверного користувацького досвіду, витратив деякий час, перевіряючи фреймворки Objective Resource , Core Resource та RestfulCoreData .

Фреймворк Objective Resource не розмовляє з Core Data самостійно, а є лише реалізацією клієнта REST. Усі основні ресурси та RestfulCoreData припускають, що ви розмовляєте з основними даними у своєму коді, і вони вирішують усі гайки та болти у фоновому режимі на рівні моделі.

Поки що все виглядає нормально, і спочатку я хоч або Core Resource, або RestfulCoreData покриватиме всі вищезазначені вимоги, але ... Є кілька речей, які, здавалося б, жодна з них не вирішує правильно:

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

Основний ресурс видає всі свої запити на сервер, коли ви викликаєте - (BOOL)save:(NSError **)errorваш контекст керованого об’єкта, і тому може забезпечити правильний екземпляр NSError базових запитів на сервер якимось чином не вдається. Але він блокує викличний потік, поки операція збереження не закінчиться. ПОМИЛКА.

RestfulCoreData зберігає ваші -save:дзвінки недоторканими і не вводить жодного додаткового часу очікування для потоку клієнта. Він просто стежить за, NSManagedObjectContextDidSaveNotificationа потім видає відповідні запити на сервер в обробнику сповіщень. Але цей спосіб , -save:виклик завжди завершується успішно (добре, враховуючи основних даних в порядку зі збереженими змінами) та клієнтський код , який на самому справі називається це не має ніякого способу , щоб знати , крім , можливо, не вдалося поширити на сервер з - за який - то 404або 421або що - то сталася помилка на стороні сервера. І навіть більше, локальне сховище стає для оновлення даних, але сервер ніколи не знає про зміни. ПОМИЛКА.

Отже, я шукаю можливе рішення / загальну практику вирішення всіх цих проблем:

  1. Я не хочу, щоб потік виклику блокувався під час кожного -save:дзвінка, поки трапляються мережеві запити.
  2. Я хочу якось отримувати сповіщення в інтерфейсі про те, що якась операція синхронізації пішла не так.
  3. Я хочу, щоб фактичне збереження основних даних також не вдалося, якщо запити сервера не вдаються.

Будь-які ідеї?


1
Ого, ти навіть не уявляєш, скільки клопоту ти мені врятував, задавши це запитання. На даний момент я впровадив свій додаток, щоб змусити користувача чекати даних кожного разу, коли я телефоную (хоча і до веб-служби .NET). Я думав про спосіб зробити його асинхронним, але не міг зрозуміти, як це зробити. Дякуємо за всі ресурси, які ви надали!
— Tejaswi Yerukalapudi

Відмінне запитання, дякую.
— Джастін

Посилання на основний ресурс не працює, чи хтось знає, де він розміщений зараз?

Основний ресурс все ще розміщений на GitHub тут: github.com/mikelaurence/CoreResource
— eploko

А оригінальний сайт також можна знайти на gitHub: github.com/mikelaurence/coreresource.org
— eploko

Відповіді:


26

Вам справді варто поглянути на RestKit ( http://restkit.org ) для цього випадку використання. Він призначений для вирішення проблем моделювання та синхронізації віддалених ресурсів JSON з локальним кешованим кешем. Він підтримує автономний режим для роботи повністю з кеш-пам'яті, коли немає доступної мережі. Вся синхронізація відбувається у фоновому потоці (доступ до мережі, аналіз корисного навантаження та злиття контексту керованого об’єкта), і існує багатий набір методів делегатів, щоб ви могли визначити, що відбувається.


18

Є три основні компоненти:

  1. Дія інтерфейсу користувача та наполеглива зміна CoreData
  2. Зберігаються, що змінюються до сервера
  3. Оновлення інтерфейсу користувача з відповіддю сервера

NSOperation + NSOperationQueue допоможе впорядкувати мережеві запити. Протокол делегатів допоможе вашим класам інтерфейсу зрозуміти, в якому стані перебувають мережеві запити, приблизно так:

@protocol NetworkOperationDelegate
  - (void)operation:(NSOperation *)op willSendRequest:(NSURLRequest *)request forChangedEntityWithId:(NSManagedObjectID *)entity;
  - (void)operation:(NSOperation *)op didSuccessfullySendRequest:(NSURLRequest *)request forChangedEntityWithId:(NSManagedObjectID *)entity;
  - (void)operation:(NSOperation *)op encounteredAnError:(NSError *)error afterSendingRequest:(NSURLRequest *)request forChangedEntityWithId:(NSManagedObjectID *)entity;
@end

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

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

- (void)managedObjectContextDidSave:(NSNotification *)saveNotification {
  NSArray *inserted = [[saveNotification userInfo] valueForKey:NSInsertedObjects];
  for (NSManagedObject *obj in inserted) {
    //create a new NSOperation for this entity which will invoke the appropraite rest api
    //add to operation queue
  }

  //do the same thing for deleted and updated objects
}

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

Якщо ваш REST API підтримує пакетний режим, ви можете навіть надіслати весь масив одночасно, а потім повідомити ваш інтерфейс, що синхронізовано кілька сутностей.

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


2

Це стає проблемою синхронізації, і її нелегко вирішити. Ось, що я б зробив: у вашому інтерфейсі iPhone використовуйте один контекст, а потім за допомогою іншого контексту (та іншого потоку) завантажуйте дані з вашої веб-служби. Після того, як все закінчиться, пройдіть рекомендовані нижче процеси синхронізації / імпорту, а потім оновіть свій користувальницький інтерфейс після того, як все імпортується належним чином. Якщо під час доступу до мережі все піде погано, просто відкочуйте зміни в контексті, що не стосується UI. Це купа роботи, але я думаю, що це найкращий спосіб підійти до неї.

Основні дані: Ефективний імпорт даних

Основні дані: управління змінами

Основні дані: багатопотоковість із основними даними


0

Вам потрібна функція зворотного виклику, яка буде виконуватися в іншому потоці (тому, де відбувається фактична взаємодія сервера), а потім помістити код результату / інформацію про помилку в напівглобальні дані, які періодично перевірятимуться в потоці інтерфейсу користувача. Переконайтеся, що з'єднання числа, яке служить прапором, є атомним, або у вас буде умова перегонів - скажімо, якщо ваша відповідь на помилку становить 32 байти, вам потрібен int (який повинен мати атомні звернення), а потім ви зберігаєте цей int у вимкненому / хибному / не готовому стані, поки ваш більший блок даних не буде записаний, і лише тоді напишіть "істина", щоб так би мовити переключити перемикач.

Для корельованого збереження на стороні клієнта потрібно просто зберегти ці дані, а не зберігати їх, поки ви не отримаєте ОК із сервера, переконайтесь, що у вас є параметр kinnf відкату - скажімо, спосіб видалення - це помилка сервера.

Пам'ятайте, що це ніколи не буде 100% безпечним, якщо ви не виконаєте повну 2-фазну процедуру коміту (збереження або видалення клієнта може провалитися після сигналу з серверного сервера), але це буде коштувати вам як мінімум 2 поїздок на сервер може коштувати вам 4, якщо єдиним варіантом відкату є видалення).

В ідеалі ви б зробили всю блокуючу версію операції в окремому потоці, але для цього вам знадобиться 4.0.

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