Як синхронізувати 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.