Статьи

iOS SDK: расширенная разработка RestKit

В этой статье мы продолжим наше исследование RestKit, платформы iOS для работы с веб-сервисами. Предполагается, что читатель прочитал часть I и имеет практические знания по RestKit. Основываясь на фундаменте, который мы создали во введении, мы рассмотрим расширенные возможности библиотеки и узнаем, как ускорить наши усилия по разработке iOS.

  • Усовершенствованная сеть: все запросы, доступность, очередь запросов и фоновая загрузка / выгрузка покрыты.
  • Расширенное сопоставление объектов: кодирование ключ-значение и сопоставление отношений.
  • Базовые данные: подробно обсуждается интеграция между картографическим объектом и средой персистентности базовых данных Apple. Это включает в себя настройку, управление отношениями, заполнение базы данных и т. Д.
  • Уровни интеграции: мы кратко коснемся точек интеграции, представленных библиотекой для работы с бэкэндами Ruby on Rails и взаимодействия с фреймворком Three20 в Facebook.

Чтобы помочь читателю в понимании представленных здесь концепций, прилагаемый пример приложения предоставляется с дистрибутивом RestKit. В каждом разделе руководства вы найдете конкретный пример в примере RKCatalog , который находится в каталоге RestKit / examples / RKCatalog.

На момент написания этой статьи RestKit в настоящее время имеет версию 0.9.2 . Исходный код библиотеки и пример кода можно загрузить со страницы загрузок RestKit .

Мы уже познакомились с ключевыми игроками на уровне сети RestKit: RKClient, RKRequest и RKResponse. Эти три класса предоставляют простой, чистый API для отправки запросов к удаленному веб-сервису. В этом разделе мы увидим, как RestKit расширяется, когда все становится сложнее.

Во введении к сетевому уровню мы научились использовать RestKit для отправки запросов с использованием методов get , post , put и delete . Эти методы абстрагируют детали создания полного URL, построения запроса, заполнения тела запроса и асинхронной отправки запроса.

Мы также узнали, как встраивать параметры в наши запросы, предоставляя NSDictionary пар ключ / значение. Под прикрытием RestKit берет этот словарь и создает тело HTTP в кодировке URL для присоединения к запросу. Заголовок Content-Type установлен на application / x-www-form-urlencoded, и запрос отправляется на обработку. Это очень удобно по сравнению с тем, как создавать тела запросов самостоятельно, и именно эта поддержка составляет основу средства отображения объектов.

Но как насчет запросов, которые не могут быть представлены словарями или загружены в память одновременно? Мы должны выйти за рамки простоты, предоставляемой NSDictionary, и взглянуть на две новые сущности: RKRequestSerializable и RKParams.

Хотя мы часто используем NSDictionary для представления параметров для многих наших запросов, RestKit специально не благословляет NSDictionary. Если вы посмотрите на сигнатуру метода для аргумента params RKRequest, вы заметите, что он вообще не определяет NSDictionary. Вместо этого вы увидите:

1
@property(nonatomic, retain) NSObject<RKRequestSerializable>* params;

Обратите внимание на протокол RKRequestSerializable здесь. RKRequestSerializable определяет очень легкий протокол, который позволяет произвольным классам сериализовать себя для использования в теле RKRequest. Когда вы импортируете RestKit, он добавляет категорию в NSDictionary, обеспечивая реализацию протокола RKRequestSerializable. RKRequestSerializable определяет только пару методов, которые необходимо реализовать, чтобы сделать сериализуемый любой тип объекта:

  • HTTPHeaderValueForContentType — этот метод возвращает значение NSString, которое будет использоваться в качестве заголовка Content-Type для запроса. Для NSDictionary мы кодируем ключи / значения, используя кодирование формы, и возвращаем ‘application / x-www-form-urlencoded’ для этого метода.
  • HTTPHeaderValueForContentLength — этот необязательный метод возвращает значение NSUInteger, которое будет использоваться в качестве заголовка Content-Length для запроса. Это полезно при длительных запросах на загрузку в целях отслеживания прогресса.
  • HTTPBody — Этот метод возвращает объект NSData, содержащий данные, которые вы хотите отправить в качестве тела запроса. Для NSDictionary мы проходим пары ключ / значение и создаем строку в кодировке URL. Затем строка приводится в NSData с использованием кодировки NSUTF8StringEncoding. Этот метод является необязательным, если вы предоставляете реализацию HTTPBodyStream (см. Ниже).
  • HTTPBodyStream — этот метод возвращает объект NSStream, который следует использовать для чтения данных для использования в теле запроса. HTTPBodyStream будет консультироваться перед HTTPBody во время построения запроса. HTTPBodyStream может использоваться для поддержки загрузки файлов, которые существуют на диске или слишком велики для размещения в основной памяти. RestKit будет эффективно передавать данные с диска и отправлять их на обработку.

Теперь, когда мы понимаем, как RestKit приводит произвольные объекты в сериализуемые представления, мы можем взглянуть на другую реализацию RKRequestSerializable, которая поставляется с библиотекой: RKParams. RKParams предоставляет простой интерфейс для построения более сложных запросов из нескольких частей. Вы можете думать о RKParams как о реализации словаря с поддержкой HTTP. Помимо предоставления простых объектов для значений в наших параметрах, RKParams также позволяет нам предоставлять NSData и пути к файлам на диске. Мы также можем установить имя файла и тип MIME для каждого параметра отдельно. Чтобы проиллюстрировать, как это работает, давайте посмотрим на некоторый код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
NSString* myFilePath = @»/some/path/to/picture.gif»;
RKParams* params = [RKParams params];
 
// Set some simple values — just like we would with NSDictionary
[params setValue:@»Blake» forParam:@»name»];
[params setValue:@»[email protected]» forParam:@»email»];
 
// Create an Attachment
RKParamsAttachment* attachment = [params setFile:myFilePath forParam:@»image1″];
attachment.MIMEType = @»image/gif»;
attachment.fileName = @»picture.gif»;
 
// Attach an Image from the App Bundle
UIImage* image = [UIImage imageNamed:@»another_image.png»];
NSData* imageData = UIImagePNGRepresentation(image);
[params setData:imageData MIMEType:@»image/png» forParam:@»image2″];
 
// Let’s examine the RKRequestSerializable info…
NSLog(@»RKParams HTTPHeaderValueForContentType = %@», [params HTTPHeaderValueForContentType]);
NSLog(@»RKParams HTTPHeaderValueForContentLength = %d», [params HTTPHeaderValueForContentLength]);
 
// Send a Request!
[[RKClient sharedClient] post:@»/uploadImages» params:params delegate:self];

По сути, здесь мы создаем стек объектов RKParamsAttachment, которые содержатся в экземпляре RKParams. При каждом вызове setValue , setFile или setData мы создаем новый экземпляр RKParamsAttachment и добавляем его в стек. Каждый из этих методов возвращает объект RKParamsAttachment, который он создал для вас, чтобы вы могли дополнительно настроить его при необходимости. Мы видим, что это используется для установки свойств MIMEType и fileName для изображения. Когда мы присваиваем объект params RKRequest, он сериализуется в документ multipart / form-data и читается как поток базовым NSURLConnection. Такое потоковое поведение позволяет использовать RKParams для чтения очень больших файлов с диска без исчерпания памяти на устройстве iOS.

Пример кода — см. RKParamsExample в RKCatalog

Пока вы счастливо отправляли запросы и обрабатывали ответы с помощью RKClient, RKRequest и RKResponse, другая часть RestKit тихо работала за кулисами, без вашего ведома: RKRequestQueue. Очередь запросов является важным игроком службы поддержки в мире RestKit и становится все более важной по мере того, как ваше приложение расширяется. RKRequestQueue выполняет три основные обязанности: управление памятью запросов, обеспечение чрезмерной нагрузки на сеть и управление жизненным циклом запросов.

Управление памятью может стать очень утомительным в приложениях Какао, так как большая часть работы происходит асинхронно. RestKit призван уменьшить сложность и церемонию, связанные с работой с веб-службами в Какао, и, таким образом, предоставляет RKRequestQueue, чтобы перенести проблемы управления памятью от разработчика приложения в среду. Вспомните, как выглядит типичный запрос / ответ RestKit:

01
02
03
04
05
06
07
08
09
10
— (void)sendARequest {
  RKRequest* request = [[RKClient sharedClient] get:@»/some/path» delegate:self];
  NSLog(@»Sent a request! %@», request);
}
 
— (void)request:(RKRequest*)request didLoadResponse:(RKResponse*)response {
  if ([response isJSON]) {
    NSLog(@»Got a JSON response back!»);
  }
}

Обратите внимание, что нигде не видно ни одного вызова для сохранения, разблокировки или автоматического выпуска. Это где очередь запросов вступает в игру. Когда мы просим отправить объект RKRequest, он не сразу отправляется. Вместо этого он сохраняется экземпляром RQRequestQueue sharedQueue и отправляется как можно скорее. RestKit отслеживает уведомления, генерируемые RKRequest & RKResponse, и освобождает запрос на удержание после завершения обработки. Это позволяет нам работать с веб-сервисами, не задумываясь об управлении памятью.

В дополнение к сохранению и освобождению экземпляров RKRequest, RKRequestQueue также служит привратником самого доступа к сети. Когда приложение впервые запускается или возвращается из фонового состояния, RestKit использует его интеграцию с API-интерфейсами доступа к конфигурации системы, чтобы определить, доступен ли какой-либо доступ к сети. При обращении к удаленному серверу по имени хоста может возникнуть задержка между запуском и определением доступности сети. В течение этого времени RestKit находится в неопределенном состоянии достижимости, и RKRequestQueue будет откладывать отправку любых запросов, пока не будет определена достижимость сети. После определения достижимости RKRequestQueue предотвращает перегрузку сети, ограничивая количество одновременных запросов до пяти.

Как только ваши пользовательские интерфейсы начинают охватывать несколько контроллеров, и пользователи быстро перемещаются по стеку контроллеров, вы можете начать генерировать несколько запросов, которые не нужно выполнять, поскольку пользователь отклонил представление. Здесь мы также обратимся к RKRequestQueue . Давайте представим, что у нас есть контроллер, который сразу начинает загружать некоторые данные при появлении представления. Но в контроллере также есть ряд кнопок, к которым пользователь может быстро получить доступ для изменения перспектив, что делает запущенный нами запрос уже неинтересным. Мы можем либо удержать экземпляры RKRequest, которые мы генерируем, либо позволить RKRequestQueue сделать всю работу за нас. Давайте посмотрим, как это будет работать:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
— (void)viewWillAppear:(BOOL)animated {
  /**
   * Ask RKClient to load us some data.
   * transparently pushed onto the RKRequestQueue sharedQueue instance
   */
  [RKClient sharedClient] get:@»/some/data.json» delegate:self];
}
 
// We have been dismissed — clean up any open requests
— (void)dealloc {
  [[RKRequestQueue sharedQueue] cancelRequestsWithDelegate:self];
  [super dealloc];
}
 
// We have been obscured — cancel any pending requests
— (void)viewWillDisappear:(BOOL)animated {
  [[RKRequestQueue sharedQueue] cancelRequestsWithDelegate:self];
}

Вместо того, чтобы самим управлять запросом и выполнять уборку, мы можем просто попросить RKRequestQueue отменить любые запросы, для которых мы являемся делегатом. Если в данный момент нет ни одной обработки, никаких действий предприниматься не будет.

Единственный экземпляр sharedQueue создается для вас во время инициализации платформы. Также возможно создание дополнительных специальных очередей для управления группами запросов.
более детально Например, специальная очередь может быть полезна для загрузки или выгрузки контента в фоновом режиме, при этом основная общая очередь остается свободной для ответа на действия пользователя. Давайте рассмотрим пример использования специальной очереди:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
— (IBAction)queueRequests {
    RKRequestQueue* queue = [[RKRequestQueue alloc] init];
    queue.delegate = self;
    queue.concurrentRequestsLimit = 1;
    queue.showsNetworkActivityIndicatorWhenBusy = YES;
 
    // Queue up 4 requests
    [queue addRequest:[[RKClient sharedClient] requestWithResourcePath:@»/RKRequestQueueExample» delegate:self]];
    [queue addRequest:[[RKClient sharedClient] requestWithResourcePath:@»/RKRequestQueueExample» delegate:self]];
    [queue addRequest:[[RKClient sharedClient] requestWithResourcePath:@»/RKRequestQueueExample» delegate:self]];
    [queue addRequest:[[RKClient sharedClient] requestWithResourcePath:@»/RKRequestQueueExample» delegate:self]];
 
    // Start processing!
    [queue start];
}

В этом примере мы создали специальную очередь, которая отправляет один запрос за раз и вращает индикатор сетевой активности системы. Для очереди запросов доступно несколько методов делегатов, чтобы упростить управление группами запросов. Посмотрите пример RKRequestQueue в RKCatalog для подробных примеров.

Пример кода — см. RKRequestQueueExample в RKCatalog

Как разработчики iOS, одной из досадных реалий, с которыми мы сталкиваемся и с которыми приходится сталкиваться, является прерывистая связь. По мере того, как наши пользователи перемещаются по всему миру с нашими приложениями, связь гарантированно приходит и уходит. Кодирование для этой реальности может усложнить нашу логику и исказить намерение в нашем коде с помощью условной логики.

Что еще хуже, API-интерфейс SCReachability, доступный нам для мониторинга состояния сети, реализован как API-интерфейсы низкого уровня C. Чтобы облегчить эту нагрузку и предоставить платформу для функциональности более высокого уровня, RestKit поставляется с оболочкой для низкоуровневых API-интерфейсов SCReachability: RKReachabilityObserver.

RKReachabilityObserver абстрагирует API-интерфейс SCReachability C и вместо этого представляет очень простой интерфейс Objective-C для определения состояния сети. Давайте посмотрим на некоторый код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
— (void)workWithReachability {
  /**
   * Initialize an observer against a hostname.
   * string and RestKit will configure the observer to watch the network address instead of the host
   */
  RKReachabilityObserver* observer = [RKReachabilityObserver reachabilityObserverWithHostName:@»restkit.org»];
 
  // Let the run-loop execute so reachability can be determined
  [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
 
  if ([observer isNetworkReachable]) {
    NSLog(@»We have network access! Huzzah!»);
 
    if ([observer isConnectionRequired]) {
      NSLog(@»Network is available if we open a connection…»);
    }
 
    if (RKReachabilityReachableViaWiFi == [observer networkStatus]) {
      NSLog(@»Online via WiFi!»);
    } else if (RKReachabilityReachableViaWWAN == [observer networkStatus]) {
      NSLog(@»Online via 3G or Edge…»);
    }
  } else {
    NSLog(@»No network access.»);
  }
}

Теперь, когда мы увидели, как инициализировать и работать с RKReachabilityObserver, стоит отметить, что большую часть времени нам не нужно! Когда вы инициализируете RKClient или RKObjectManager с базовым URL-адресом, RestKit продолжает работу и инициализирует экземпляр RKReachabilityObserver, ориентированный на имя хоста, указанное в базовом URL-адресе. Этот наблюдатель доступен вам через свойство baseURLReachabilityObserver в RKClient. RKReachabilityObserver также генерирует уведомления при каждом изменении состояния сети. Обычно эти события — это все, что вас интересует. Наблюдение за событиями достижимости легко:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@implementation ReachabilityInterestedClass
 
— (id)init {
  if ((self = [super init])) {
      [[NSNotificationCenter defaultCenter] addObserver:self
                                                selector:@selector(reachabilityChanged:)
                                                name:RKReachabilityStateChangedNotification
                                                object:nil];
  }
 
  return self;
}
 
— (void)reachabilityChanged:(NSNotification*)notification {
  RKReachabilityObserver* observer = (RKReachabilityObserver*)[notification object];
 
  if ([observer isNetworkAvailable]) {
    NSLog(@»We’re online!»);
  } else {
    NSLog(@»We’ve gone offline!»);
  }
}
 
@end

Пример кода — см. RKReachabilityExample в RKCatalog

Мы рассмотрим, как RestKit использует Reachability для обеспечения прозрачного автономного доступа в разделе кэширования объектов Core Data.

С iOS 4.0 Apple представила поддержку многозадачности для приложений. Поддержка многозадачности основывается на переводе приложений в фоновое состояние, где они могут быть быстро восстановлены в полностью интерактивном режиме, но при этом не использовать ресурсы в фоновом режиме. Это может представлять проблему для сетевых приложений, например, созданных с помощью RestKit: важные длительные запросы могут быть прерваны из-за выхода пользователя из приложения. К счастью, Apple также предоставила ограниченную поддержку для продления срока службы вашего процесса, создав фоновую задачу с beginBackgroundTaskWithExpirationHandler метода UIApplication . Этот метод принимает блок Objective C и возвращает значение UIBackgroundTaskIdentifier для фоновой задачи, которая была создана. При создании фоновой задачи необходимо соблюдать осторожность, чтобы обеспечить обратную совместимость с развертываниями iOS 3.0.

RestKit стремится облегчить эту нагрузку на разработчика, предоставляя простую и прозрачную поддержку фоновых задач во время цикла запроса. Давайте посмотрим на некоторый код:

1
2
3
4
5
6
7
— (void)backgroundUpload {
  RKRequest* request = [[RKClient sharedClient] post:@»somewhere» delegate:self];
  request.backgroundPolicy = RKRequestBackgroundPolicyNone;
  request.backgroundPolicy = RKRequestBackgroundPolicyCancel;
  request.backgroundPolicy = RKRequestBackgroundPolicyContinue;
  request.backgroundPolicy = RKRequestBackgroundPolicyRequeue;
}

Политика по умолчанию — RKRequestBackgroundPolicyNone. После того как вы установили политику и отправили запрос, RestKit обрабатывает все остальное — включение и выключение приложения приведет к выполнению соответствующих действий.

Пример кода — см. RKBackgroundRequestExample в RKCatalog

В первой части нашей серии мы представили концепцию Object Mapping — процесс RestKit для преобразования полезных нагрузок JSON в объекты локального домена. В своей самой простой форме Object Mapping уменьшает утомление, связанное с анализом простых полей из словаря и назначением их целевому объекту. Но это еще не конец истории для картографа объекта.

RestKit также предоставляет поддержку для отображения иерархий объектов, выраженных через отношения. Это мощная функция для импорта большого объема данных через один HTTP-запрос. В этом разделе мы подробно рассмотрим сопоставление отношений и посмотрим, как RestKit поддерживает неидиоматические структуры JSON посредством кодирования значения ключа.

Большинство примеров сопоставления объектов, которые мы рассмотрели до сих пор, выполняли простые сопоставления из одного поля в другое (то есть созданный_ат становится созданным). Если у вас есть полный контроль над выводом JSON серверной системы или вы точно моделируете вывод на стороне сервера, это может быть все, что вам когда-либо потребуется. Но иногда реалии бэкэнд-системы, с которой нам нужно интегрироваться, не так хорошо соответствуют взгляду RestKit на мир. Если ваш целевой JSON содержит вложенные данные, к которым вы хотите получить доступ без разложения структур на несколько типов объектов, вам потребуется использовать мощь кодирования ключ-значение в ваших сопоставлениях.

Кодирование ключ-значение — это механизм косвенного доступа к данным в Какао с использованием строки, содержащей имена свойств, вместо вызова методов доступа или прямого доступа к переменным экземпляра. Кодирование ключ-значение является одним из наиболее важных шаблонов в Какао и является основой системы сопоставления объектов RestKit. Когда RestKit обнаруживает полезную нагрузку данных, которую он знает, как обрабатывать, полезная нагрузка передается анализатору для оценки. Затем анализатор оценивает данные и возвращает совместимое с кодированием значение-представление данных в виде массивов, словарей и базовых типов. Отсюда средство отображения объектов начинает анализировать представление и использовать средства доступа к ключу-значению для извлечения данных и назначения их экземпляру целевого объекта. Каждый раз, когда вы регистрируете сопоставление элемента с классом или определяете сопоставление элемента с свойством, вы указываете путь сопоставления, соответствующий кодированию значения ключа, для сопоставителя. Это важный момент — у вас есть все возможности шаблона ключ-значение в вашем распоряжении. Например, вы можете перемещаться по графу объектов через синтаксис точечной нотации и использовать операторы коллекций для выполнения действий с коллекциями в вашей полезной нагрузке.

Давайте посмотрим на некоторый код, чтобы лучше понять, как работает кодирование ключ-значение в RestKit. Рассмотрим следующую структуру JSON для упрощенного приложения для банковского счета:

01
02
03
04
05
06
07
08
09
10
{
    «id»: 1234,
    «name»: «Personal Checking»,
    «balance»: 5013.26,
    «transactions»: [
      {«id»: 1, «payee»: «Joe Blow», «amount»: 50.16},
      {«id»: 2, «payee»: «Grocery Store», «amount»: 200.15},
      {«id»: 3, «payee»: «John Doe», «amount»: 325.00},
      {«id»: 4, «payee»: «Grocery Store», «amount»: 25.15}]
}

Мы собираемся использовать кодирование ключ-значение для доступа к некоторой информации в полезной нагрузке: количестве транзакций, средней сумме транзакций и отдельной группе получателей в списке транзакций. Вот наша модель:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@interface SimpleAccount : RKObject {
  NSNumber* _accountID;
  NSString* _name;
  NSNumber* _balance;
  NSNumber* _transactionsCount;
  NSNumber* _averageTransactionAmount;
  NSArray* _distinctPayees;
}
 
@property (nonatomic, retain) NSNumber* accountID;
@property (nonatomic, retain) NSString* name;
@property (nonatomic, retain) NSNumber* balance;
@property (nonatomic, retain) NSNumber* transactionsCount;
@property (nonatomic, retain) NSNumber* averageTransactionAmount;
@property (nonatomic, retain) NSArray* distinctPayees;
 
@end
 
@implementation SimpleAccount
 
+ (NSDictionary*)elementToPropertyMappings {
  return [NSDictionary dictionaryWithKeysAndObjects:
          @»id», @»accountID»,
          @»name», @»name»,
          @»balance», @»balance»,
          @»transactions.@count», @»transactionsCount»,
          @»[email protected]», @»averageTransactionAmount»,
          @»[email protected]», @»distinctPayees»,
          nil];
}
 
@end
 
// — snip —
 
— (void)workWithKVC {
  [[RKObjectManager sharedManager] loadObjectsAtResourcePath:@»/accounts.json» objectClass:[SimpleAccount class] delegate:self];
}
 
— (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {
  SimpleAccount* account = [objects objectAtIndex:0];
 
  // Will output «The count is 4»
  NSLog(@»The count is %@», [account transactionsCount]);
 
  // Will output «The average transaction amount is 150.115»
  NSLog(@»The average transaction amount is %@», [account averageTransactionAmount]);
 
  // Will output «The distinct list of payees is: Joe Blow, Grocery Store, John Doe»
  NSLog(@»The distinct list of payees is: %@», [[account distinctPayees] componentsJoinedByString:@», «]);
}

Теперь все становится интересным. Обратите внимание на новый синтаксис, используемый после определения баланса. Мы использовали кодирование ключ-значение для перехода к массиву транзакций и выполнения операций с данными. Отсюда мы видим использование нескольких операторов коллекции Key-value. Эти операторы предоставлены нам Какао и подробно описаны в «Руководстве по программированию кодирования значения ключа», доступном в документации Xcode. Ключевой урок здесь состоит в том, чтобы помнить, что средство отображения объектов построено с учетом кодирования значения ключа и имеется дополнительная огневая мощь, доступная помимо простых сопоставлений «один к одному».

Использование преимуществ кодирования значения ключа в ваших сопоставлениях становится очень полезным при работе с большими и сложными полезными нагрузками JSON, когда вам важна только часть данных. Но иногда мы действительно заботимся обо всей этой дополнительной информации — мы просто хотим, чтобы она была доступна в более доступном формате. В этих обстоятельствах мы можем вместо этого обратиться к использованию моделирования отношений, чтобы помочь RestKit преобразовать полезную нагрузку больших данных в граф объектов.

Пример кода — см. RKKeyValueMappingExample в RKCatalog

Моделирование отношений выражается с помощью метода elementToRelationshipMappings в протоколе RKObjectMappable. Этот метод инструктирует мапперу брать вложенный словарь JSON, выполнять операции отображения и назначать объект результата (или объекты) свойству с заданным именем. Этот процесс повторяется для каждой операции отображения, позволяя построить графы объектов произвольной глубины.

Чтобы понять, как это работает, давайте рассмотрим пример. Мы собираемся пройтись по реализации модели данных списка задач, чтобы проиллюстрировать принципы моделирования отношений. Код примера списка задач содержится в коде RKRelationshipMappingExample в приложении RKCatalog. В RKRelationshipMappingExample есть три модели данных, которые связаны друг с другом: пользователи, проекты и задачи. Пользователи — это люди, работающие в системе. Проекты содержат отдельный набор шагов, направленных на достижение конкретной цели, которая может быть выполнена. Задачи представляют каждый из этих конкретных шагов в рамках проекта. Отношения между ними:

  • У пользователя много проектов
  • Каждый проект принадлежит одному пользователю
  • Каждая задача принадлежит одному проекту
  • Каждая задача может быть назначена одному пользователю

Модели данных можно найти в каталоге Code / Models примера приложения.

Наше приложение очень просто с точки зрения пользовательского интерфейса. У нас есть одно табличное представление, которое показывает все проекты в системе и имя пользователя, который создал проект. Нажатие на Project толкает вторичное табличное представление в представление, которое показывает все Задачи, содержащиеся в Проекте. Вместо того, чтобы делать несколько запросов к отдельным коллекциям ресурсов для построения представления, мы собираемся запросить весь граф объектов из единственного пути ресурса ‘/ task_list’. JSON, возвращаемый путем к ресурсу, выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
[{«project»: {
     «id»: 123,
     «name»: «Produce RestKit Sample Code»,
     «description»: «We need more sample code!»,
     «user»: {
         «id»: 1,
         «name»: «Blake Watters»,
         «email»: «[email protected]»
     },
     «tasks»: [
         {«id»: 1, «name»: «Identify samples to write», «assigned_user_id»: 1},
         {«id»: 2, «name»: «Write the code», «assigned_user_id»: 1},
         {«id»: 3, «name»: «Push to Github», «assigned_user_id»: 1},
         {«id»: 4, «name»: «Update the mailing list», «assigned_user_id»: 1}
     ]
 }},
 {«project»: {
     «id»: 456,
     «name»: «Document Object Mapper»,
     «description»: «The object mapper could really use some docs!»,
     «user»: {
         «id»: 2,
         «name»: «Jeremy Ellison»,
         «email»: «[email protected]»
     },
     «tasks»: [
         {«id»: 5, «name»: «Mark up methods with Doxygen markup», «assigned_user_id»: 2},
         {«id»: 6, «name»: «Generate docs and review formatting», «assigned_user_id»: 2},
         {«id»: 7, «name»: «Review docs for accuracy and completeness», «assigned_user_id»: 1},
         {«id»: 8, «name»: «Publish to Github», «assigned_user_id»: 2}
     ]
 }},
 {«project»: {
     «id»: 789,
     «name»: «Wash the Cat»,
     «description»: «Mr. Fluffy is looking like Mr. Scruffy! Time for a bath!»,
     «user»: {
         «id»: 3,
         «name»: «Rachit Shukla»,
         «email»: «[email protected]»
     },
     «tasks»: [
         {«id»: 9, «name»: «Place cat in bathtub», «assigned_user_id»: 3},
         {«id»: 10, «name»: «Run water», «assigned_user_id»: 3},
         {«id»: 11, «name»: «Try not to get scratched», «assigned_user_id»: 3}
     ]
 }}]

Эта коллекция JSON ориентирована на массив моделей Project с вложенными структурами отношений. Давайте посмотрим на реализацию нашего проекта класса:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@interface Project : RKObject {
   NSNumber* _projectID;
   NSString* _name;
   NSString* _description;
   User* _user;
   NSArray* _tasks;
 }
 
 @property (nonatomic, retain) NSNumber* projectID;
 @property (nonatomic, retain) NSString* name;
 @property (nonatomic, retain) NSString* description;
 @property (nonatomic, retain) User* user;
 @property (nonatomic, retain) NSArray* tasks;
 
 @end
 
 @implementation Project
 
 + (NSDictionary*)elementToPropertyMappings {
   return [NSDictionary dictionaryWithKeysAndObjects:
           @»id», @»projectID»,
           @»name», @»name»,
           @»description», @»description»,
           nil];
 }
 
 + (NSDictionary*)elementToRelationshipMappings {
   return [NSDictionary dictionaryWithKeysAndObjects:
           @»user», @»user»,
           @»tasks», @»tasks»,
           nil];
 }
 
 @end

Здесь мы видим новый вызов elementToRelationshipMappings. Если вы оглянетесь назад на структуру JSON, то увидите, что объявление инструктирует маппер объектов брать данные, содержащиеся в под-словарях ‘user’ и ‘tasks’, отображать их в объекты и назначать пользователя и массив Task объекты к проекту. Когда все это будет выполнено, средство отображения объектов вернет результаты, и полный граф объектов будет отправлен вашему делегату загрузчика объектов для обработки.

Пример кода — см. RKRelationshipMappingExample в RKCatalog

ПРИМЕЧАНИЕ. — Для целей данного руководства предполагается, что читатель знаком с Базовыми данными.

Возможно, самым мощным оружием в арсенале RestKit является бесшовная интеграция с технологией Core Data от Apple. Базовые данные предоставляют запрашиваемую структуру сохранения объектов, доступную в OS X и iOS. Построенный на основе сопоставления объектов, RestKit позволяет разработчику создавать постоянное зеркало данных, содержащихся в удаленной серверной системе, с очень небольшим количеством кода. Есть много движущихся частей, вовлеченных в обеспечение такого высокого уровня абстракции, поэтому давайте встретимся с ключевыми игроками, прежде чем углубляться в детали:

  • RKManagedObjectStore — хранилище объектов оборачивает инициализацию и конфигурацию внутренних классов базовых данных, включая NSManagedObjectModel, NSPersistentStoreCoordinator и NSManagedObjectContext. Хранилище объектов также отвечает за управление контекстами объектов для каждого потока и управление изменениями между потоками. Как правило, хранилище объектов стремится удалить как можно больше стандартного кода базовых данных из основного приложения.
  • RKManagedObject — Суперкласс всех постоянных объектов RestKit. RKManagedObject наследуется от NSManagedObject и расширяет API рядом полезных методов. Это класс RKObjectMappable, и он настроен для сопоставления так же, как и временные экземпляры RKObject.
  • RKManagedObjectLoader — когда поддержка Core Data включена в ваше приложение, этот потомок
    RKObjectLoader обрабатывает запросы загрузки объекта. Он знает, как уникальным образом идентифицировать объекты, поддерживаемые Core Data, и скрывает сложности передачи NSManagedObject между потоками. Также отвечает за удаление объектов из локального хранилища при успешной обработке DELETE.
  • RKManagedObjectCache — Протокол кеширования объектов определяет один метод для сопоставления путей ресурсов к коллекции запросов выборки для извлечения локальных копий объектов, которые «живут» по заданному пути ресурса. Мы рассмотрим это подробно ниже.
  • RKManagedObjectSeeder — Сеялка объектов предоставляет интерфейс для создания базы данных SQLite, которая загружается с локальными копиями удаленных объектов. Это может быть использовано для начальной загрузки большой локальной базы данных, поэтому не требуется длительный процесс синхронизации при первой загрузке приложения из App Store. Посев подробно описан ниже.

Стоит отметить, что в использовании RestKit Core Data нет ничего особенного. Каркас использует стандартные API и упрощает общие задачи. Вы можете без проблем интегрировать RestKit в существующее приложение с базовыми данными. После интеграции вы можете использовать все знакомые API Core Data — вам не нужно придерживаться того, что RestKit предоставляет через RKManagedObject.

Включение постоянного сопоставления объектов является относительно простым процессом. Он отличается от временного отображения объектов только несколькими способами:

  1. libRKCoreData.a должна быть связана с вашей целью
  2. CoreData.framework от Apple должен быть связан с вашей целью
  3. Ресурс модели данных должен быть добавлен к вашей цели и настроен в XCode
  4. Заголовки данных RestKit Core должны быть импортированы через #import <RestKit/CoreData/CoreData.h>
  5. Экземпляр RKManagedObjectStore должен быть настроен и назначен менеджеру объектов
  6. Постоянные модели наследуются от RKManagedObject, а не от RKObject
  7. Свойство первичного ключа должно быть определено в каждой постоянной модели путем реализации метода primaryKeyProperty
  8. Реализация свойств для управляемых объектов генерируется с помощью @dynamic, а не @synthesize

Как только эти изменения конфигурации будут завершены, RestKit загрузит и отобразит полезные нагрузки в классы с базовыми данными.

При работе с Core Data необходимо учитывать несколько общих моментов:

  1. Вы можете использовать сочетание постоянных и переходных моделей в приложении — даже в пределах одной полезной нагрузки JSON. RestKit определит, поддерживается ли целевой объект Core Data во время выполнения, и
    возвращать управляемые и неуправляемые объекты в зависимости от ситуации.
  2. RestKit ожидает, что каждый экземпляр объекта может быть уникально идентифицирован с помощью одного первичного ключа, который присутствует в полезной нагрузке. Это позволяет мапперу различать новые, обновленные и удаленные объекты.
  3. При настройке ресурса модели данных необходимо позаботиться о том, чтобы в качестве целевого класса был выбран нужный класс модели. По умолчанию это NSManagedObject и должно быть соответствующим образом обновлено. Невыполнение этого требования приведет к возникновению исключений из сопоставителя при вызове методов RestKit для экземпляра NSManagedObject.
  4. Использование потоков в Core Data требует особого внимания.Вы не можете безопасно передавать экземпляры управляемого объекта через границы потоков. Они должны быть сериализованы в NSManagedObjectID и переданы между потоками, а затем повторно получены из контекста управляемого объекта. RKObjectLoader выполняет синтаксический анализ JSON и сопоставление объектов в фоновых потоках, а также обрабатывает переходы и выборку объектов. Но вы должны позаботиться, если вы введете многопоточность (включая использование executeSelector: withDelay ? в своем коде приложения.
  5. Apple рекомендует использовать один экземпляр контекста управляемого объекта на поток. Когда вы извлекаете контекст управляемого объекта из RKManagedObjectStore, новый экземпляр создается и сохраняется в локальном хранилище потока, если вызывающий поток не является основным потоком. Вам не нужно беспокоиться об управлении жизненным циклом контекстов управляемого объекта или слиянием изменений — хранилище объектов наблюдает за этими локальными контекстами потока и обрабатывает слияния изменений обратно в контекст основного объекта.
  6. RestKit делает некоторые общие предположения о том, как вы используете базовые данные, которые могут не подходить для вашего приложения. Это включает в себя политику слияния, используемую в контексте объекта, параметры, предоставляемые во время инициализации координатора постоянного хранилища, и т. Д. Если вам требуется больше гибкости, чем предусмотрено из коробки, обратитесь к команде, и мы поможем ослабить эти предположения ,
  7. RestKit предполагает, что вы используете сущность с тем же именем, что и у вашего класса модели в модели данных.
  8. В настоящее время нет никакой помощи на уровне инфраструктуры для работы с миграциями магазинов.

Для получения справки по началу работы с Core Data, пожалуйста, обратитесь к проектам RKTwitter и RKTwitterCoreData в каталоге examples / дистрибутива RestKit. Эти проекты предоставляют идентичные реализации простого моделирования временной шкалы Twitter, за исключением того, что один из них постоянно поддерживается Базовыми данными.

Теперь, когда у нас есть базовые требования для добавления базовых данных в проект RestKit, давайте взглянем на некоторый код, который поможет нам добиться успеха. Весь код в этом разделе содержится в проекте examples / RKCoreDataExamples для справки. Пожалуйста, откройте проект и следите за его примерами.

Во-первых, нам нужно инициализировать RestKit и Core Data. Откройте RKCDAppDelegate.m и обратите внимание на следующие фрагменты кода:

1
2
3
4
5
// Import RestKit's Core Data support
#import &lt;RestKit/CoreData/CoreData.h&gt;
 
RKObjectManager* manager = [RKObjectManager objectManagerWithBaseURL:@"http://restkit.org"];
manager.objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:@"RKCoreDataExamples.sqlite"];

Здесь мы создали экземпляр экземпляра менеджера объектов и экземпляр хранилища управляемых объектов. Внутри RKManagedObject для вас были созданы NSManagedObjectModel и NSPersistentStoreCoordinator. Файл постоянного хранилища создается или открывается заново для вас в каталоге документов приложения и настраивается для использования SQLite в качестве технологии поддержки. Отсюда у вас есть готовая к работе рабочая среда Core Data.

Теперь давайте взглянем на довольно анемичную модель в примерах / RKCatalog / examples / RKCoreDataExample / RKCoreDataExample.m:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
@implementation Article
 
  + (NSDictionary*)elementToPropertyMappings {
    return [NSDictionary dictionaryWithKeysAndObjects:
            @"id", @"articleID",
            @"title", @"title",
            @"body", @"body",
            nil];
  }
 
  + (NSString*)primaryKeyProperty {
    return @"articleID";
  }
 
@end

Здесь мы видим знакомый метод elementToPropertyMappings из RKObjectMappable. Единственным нововведением здесь является реализация метода, указывающего первичный ключ. Это позволяет объекту сопоставления знать, что при работе с экземплярами Article он должен обращаться к articleIDсвойству, чтобы получить значение первичного ключа для экземпляра. Это позволяет RestKit обновлять свойства этого объекта независимо от того, с какого пути ресурса он загружен.

Теперь давайте рассмотрим некоторые API, предоставляемые через RKManagedObject. Загрузка всех объектов данного типа тривиальна:

1
2
3
4
- (void)loadAllObjects {
    NSArray* objects = [Article allObjects];
    NSLog(@"We loaded %d objects", [objects count]);
}

Здесь мы извлекаем все объекты для данного класса из Core Data. Это оборачивает инициализацию, настройку и выполнение запроса выборки, нацеленного на сущность для нашего класса. Мы также можем настроить наши собственные запросы на выборку или использовать ряд вспомогательных методов для быстрого выполнения общих задач:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
- (void)funWithFetchRequests {
    // Grab a fetch request configured to target the Article entity
    NSFetchRequest* fetchRequest = [Article fetchRequest];
    NSLog(@"My fetch request is: %@", fetchRequest);
 
    // Configure a fetch request to sort the results by title
    NSFetchRequest* sortedRequest = [Article fetchRequest];
    NSSortDescriptor* sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"title" ascending:YES];
    [sortedRequest setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
    NSArray* sortedObjects = [Article objectsWithFetchRequest:fetchRequest];
    NSLog(@"Here are the objects sorted: %@", sortedObjects);
 
    // Fetch an object by primary key
    Article* firstArticle = [Article objectWithPrimaryKeyValue:[NSNumber numberWithInt:1]];
    NSLog(@"This is the Article with ID 1: %@", firstArticle);
 
    // Find Articles where the body contains the word 'something' case insensitively
    NSPredicate* predicate = [NSPredicate predicateWithFormat:@"body CONTAINS[c] %@", @"something"];
    NSArray* matches = [Article objectsWithPredicate:predicate];
    NSLog(@"Found the following Articles that match: %@", matches);
}

Все эти методы определены в RKManagedObject и предоставляют ярлыки для функций, предоставляемых непосредственно Core Data. Конечно, вы можете полностью настроить свой запрос на выборку:

1
2
3
4
5
6
- (NSFetchRequest*)constructMyOwnFetchRequest {
    NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
    NSEntityDescription *entity = [Article entity]; // The entity is available via RKManagedObject helper method...
    [fetchRequest setEntity:entity];
    return fetchRequest;
}

Эти вспомогательные методы Core Data используются для управления простым табличным представлением в RKCoreDataExample в RKCatalog.

Пример кода — см. RKCoreDataExample в RKCatalog

Одним из самых приятных преимуществ использования Core Data с RestKit является то, что вы получаете хорошо гидратированный объектный граф, который позволяет вам естественным образом обходить объектные отношения. Популяция отношений обрабатывается с помощью elementToRelationshipMappingsметода, который мы ввели в предыдущем разделе о моделировании отношений. Вспомните, что elementToRelationshipMappingsинструктирует
маппер искать связанные объекты, вложенные в словарь в полезной нагрузке JSON. Но это может представлять проблему для приложения с поддержкой Core Data — если вы не вернете все отношения, которые вы смоделировали в своей полезной нагрузке, график может устареть и не синхронизироваться с сервером. И не говоря уже о том, что возвращение всех отношений часто некорректно с точки зрения дизайна API или производительности. Так что же нам делать?

RestKit решает эту проблему путем введения новой директивы конфигурации картографа , специфичной для объектов основных данных: relationshipToPrimaryKeyPropertyMappings. Определение связи с определениями первичного ключа дает указание мапперу подключить связь базовых данных, используя значение, сохраненное в другом свойстве, для поиска целевого объекта. Это легко понять, вернувшись к модели данных списка задач, которую мы исследовали ранее. Напомним, что JSON для отдельной задачи выглядел так:

1
{"id": 5, "name": "Place cat in bathtub", "assigned_user_id": 3}

Обратите внимание на assigned_user_idэлемент в полезной нагрузке — это значение первичного ключа для объекта User, которому назначена задача. Давайте посмотрим на код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@interface Task : RKManagedObject {
}
 
@property (nonatomic, retain) NSNumber* taskID;
@property (nonatomic, retain) NSString* name;
@property (nonatomic, retain) NSNumber* assignedUserID;
@property (nonatomic, retain) User* assignedUser;
 
@end
 
@implementation Task
 
+ (NSDictionary*)elementToPropertyMappings {
  return [NSDictionary dictionaryWithKeysAndObjects:
          @"id", @"taskID",
          @"name", @"name",
          @"assigned_user_id", @"assignedUserID",
          nil];
}
 
+ (NSDictionary*)relationshipToPrimaryKeyPropertyMappings {
  return [NSDictionary dictionaryWithObject:@"assignedUserID" forKey:@"assignedUser"];
}
 
@end

Обратите внимание на определение relationshipToPrimaryKeyPropertyMappings— мы сообщили мапперу, что assignedUserIDсвойство содержит значение первичного ключа для assignedUserотношения. Когда картограф видит это, он будет отражать отношения, чтобы определить его тип (в данном случае, пользователя) и назначить object.user = User.findByPrimaryKeyValue(object.assignedUserID). Целевой объект должен существовать в локальном хранилище данных, иначе отношение будет установлено равным нулю.

Пример кода — см. RKRelationshipMappingExample в RKCatalog

Основным вариантом использования интеграции основных данных RestKit является предоставление автономного доступа к удаленному контенту. Фактически именно из-за этого и возникла потребность в RestKit — когда началась разработка GateGuru (отличного, незаменимого приложения для путешественников, владеющих iPhone), основным требованием была возможность доступа к информации на высоте 30 000 футов. Что нам действительно нужно, так это общий интерфейс программирования, который будет работать независимо от состояния онлайн или офлайн — если у нас есть сетевое подключение, пингует удаленный сервер и выдает результаты, в противном случае запускается кэш-память и предоставляются самые последние доступные локальные результаты. Поскольку мы разработали наши веб-сервисы RESTful, мы могли легко создать URL-адреса, которые будут обращаться к данным для конкретного аэропорта, терминала и т. Д.Мы поняли, что можем достичь нирваны нашего API, используя путь к ресурсу, для которого загружаем удаленные объекты в качестве ключа в наше постоянное хранилище. Это именно то, чтоRKManagedObjectCache Протокол позволяет сделать:

1
2
3
4
5
/**
 * Must return an array containing NSFetchRequests for use in retrieving locally
 * cached objects associated with a given request resourcePath.
 */
- (NSArray*)fetchRequestsForResourcePath:(NSString*)resourcePath;

Чтобы использовать кэш объектов, необходимо предоставить реализацию RKManagedObjectCacheи назначить ее хранилищу управляемых объектов. Реализация метода должна проанализировать путь к ресурсу и создать один или несколько запросов на выборку, которые можно использовать для извлечения объектов, которые «живут» по этому пути к ресурсу. Например, в приложении, подобном GateGuru, в котором есть коллекция объектов аэропорта, ваша реализация может выглядеть примерно так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
{
 
}
 
@end
 
@implementation MyObjectCache
 
- (NSArray*)fetchRequestsForResourcePath:(NSString*)resourcePath {
  if ([resourcePath isEqualToString:@"/airports"]) {
    NSFetchRequest* fetchRequest = [Airport fetchRequest]; // A fetch request with an entity set, but nothing else fetches all objects
    return [NSArray arrayWithObject:fetchRequest];
  }
 
  return nil;
}
 
@end
 
MyObjectCache* cache = [[MyObjectCache alloc] init];
[RKObjectManager sharedManager].objectStore.managedObjectCache = cache;
[cache release];

Возвращается массив запросов на выборку для поддержки случая, когда ваша удаленная конечная точка возвращает объекты более одного типа — запросы на выборку базовых данных могут предназначаться только для одного объекта. После того как вы предоставили реализацию и охватили все пути к ресурсам, вы можете извлечь локально кэшированные объекты для пути к ресурсам из хранилища объектов:

1
NSArray* objects = [[RKObjectManager sharedManager].objectStore objectsForResourcePath:@"/airports"];

Кэш объектов широко используется на уровне интеграции Three20, который мы обсудим более подробно ниже. Таким образом, если вы используете Three20 в своем приложении, RestKit поставляется с реализацией TTModel с поддержкой кэша объектов, которую можно использовать для заполнения таблицы данными из загрузчика объектов или кэша.

Помимо обеспечения автономной поддержки, кэш объектов предоставляет еще одну важную функцию: интеллектуальная обработка удаления объектов на стороне сервера. Если вы предоставили реализацию кэша объектов в своем приложении, RestKit удалит объекты, которые в настоящее время существуют в локальном хранилище, но исчезли из удаленной полезной нагрузки для кэшированного пути ресурса. Если в вашем приложении много путей к ресурсам, которые могут загружать одни и те же объекты, важно, чтобы вы обрабатывали каждый путь и возвращали запросы выборки, охватывающие все объекты.

Если вы не используете кеш объекта, вы должны вручную обработать удаление объекта на стороне сервера каким-либо другим способом.

В приложении с поддержкой Core Data может быть очень желательно отправить ваше приложение в App Store с содержимым, уже доступным в локальном магазине. RestKit включает в себя простую реализацию заполнения объекта для этой цели через RKObjectSeederкласс. RKObjectSeederпредоставляет интерфейс для открытия одного или нескольких файлов JSON, хранящихся в локальном пакете, обработки их с помощью средства отображения, а затем вывода инструкций о том, как получить начальную базу данных для использования в вашем приложении. Процесс посева обычно выглядит так:

  1. Создайте файл дампа для каждого из ваших постоянных типов объектов из вашей серверной системы в формате JSON.
  2. Дублируйте существующую цель приложения и назовите новую цель «Создать базу данных семян».
  3. Просмотрите параметры сборки для своей цели и найдите раздел GCC — Предварительная обработка.
  4. В разделе под названием «Препроцессор Макросы», добавить новый макрос препроцессора: RESTKIT_GENERATE_SEED_DB. Это значение будет определено при создании и запуске цели сеялки.
  5. Добавьте файлы дампа JSON в цель «Сгенерировать базу данных семян» и убедитесь, что они скопированы в комплект приложения.
  6. Обновите делегат приложения, чтобы проверить RESTKIT_GENERATE_SEED_DBи создать экземпляр RKObjectSeeder.
  7. Инициализируйте экземпляр RKObjectSeederс вашим полностью настроенным экземпляромRKObjectManager
  8. Вызовите соответствующие методы в RKObjectSeederэкземпляре для каждого из ваших файлов дампа JSON.
  9. Когда закончите, вызовите finalizeSeedingAndExitметод на RKObjectSeederэкземпляре.

Сеялка предназначена для работы в симуляторе на вашем Mac. Когда вы вызываете finalizeSeedingAndExit, библиотека выводит на консоль подробности о том, где вы можете получить начальную базу данных SQLite. Получив копию исходной базы данных, вы добавляете ее в свой проект в качестве ресурса для копирования в комплект приложения. После того, как вы добавили начальную базу данных в свое приложение, вы просто изменили свою инициализацию RKManagedObjectStore, чтобы указать, что у вас есть начальная база данных для запуска, а не с чистого листа.

Давайте рассмотрим пример кода, взятый из примера RKTwitterCoreData, который показывает, как работать с сеялкой:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Database seeding is configured as a copied target of the main application. There are only two differences
// between the main application target and the 'Generate Seed Database' target:
// 1) RESTKIT_GENERATE_SEED_DB is defined in the 'Preprocessor Macros' section of the build setting for the target
// This is what triggers the conditional compilation to cause the seed database to be built
// 2) Source JSON files are added to the 'Generate Seed Database' target to be copied into the bundle. This is required
// so that the object seeder can find the files when run in the simulator.
#ifdef RESTKIT_GENERATE_SEED_DB   
    RKManagedObjectSeeder* seeder = [RKManagedObjectSeeder objectSeederWithObjectManager:objectManager];
 
    // Seed the database with instances of RKTStatus from a snapshot of the RestKit Twitter timeline
    [seeder seedObjectsFromFile:@"restkit.json" toClass:[RKTStatus class] keyPath:nil];
 
    // Seed the database with RKTUser objects. The class will be inferred via element registration
    [seeder seedObjectsFromFiles:@"users.json", nil];
 
    // Finalize the seeding operation and output a helpful informational message
    [seeder finalizeSeedingAndExit];
 
    // NOTE: If all of your mapped objects use element -&gt; class registration, you can perform seeding in one line of code:
    // [RKManagedObjectSeeder generateSeedDatabaseWithObjectManager:objectManager fromFiles:@"users.json", nil];
#endif
 
    // Initialize object store
    objectManager.objectStore = [RKManagedObjectStore objectStoreWithStoreFilename:@"RKTwitterData.sqlite" usingSeedDatabaseName:RKDefaultSeedDatabaseFileName managedObjectModel:nil];

Вкратце стоит отметить, что RestKit поставляется с некоторыми уровнями интеграции, чтобы помочь разработчикам работать с некоторыми дополнительными технологиями. На момент написания этой статьи существует две такие точки интеграции:

  1. RKRailsRouter — реализация маршрутизатора, осведомленная о идиомах Ruby on Rails
  2. RKRequestTTModel — реализация протокола TTModel для Three20, которая позволяет загрузчикам объектов RestKit управлять таблицами Three20

RKRailsRouter наследуется от RKDynamicRouter, представленного в первом руководстве. Маршрутизатор Rails изменяет поведение маршрутизации по умолчанию несколькими способами:

  1. Позволяет регистрировать имена моделей на стороне сервера с целью вложения атрибутов перед отправкой запросов.
  2. Предотвращает кодирование любых данных параметров в тело запроса для запросов DELETE.

Вложенность атрибута понимается просто на примере. Представьте, что у нас есть серверная модель под названием «Article» с двумя атрибутами «title» и «body». Мы настроили бы Rails роутер так:

01
02
03
04
05
06
07
08
09
10
RKRailsRouter* router = [[RKRailsRouter alloc] init];
[router setModelName:@"article" forClass:[Article class]];
[router routeClass:[Article class] toResourcePath:@"/articles/(articleID)"];
[router routeClass:[Article class] toResourcePath:@"/articles" forMethod:RKRequestMethodPOST];
 
Article* article = [Article object];
article.title = @"This is the title";
article.body = @"This is the body";
 
[[RKObjectManager sharedManager] postObject:article delegate:self];

Когда объект сериализуется для запроса POST, RestKit вложит атрибуты в хеш-код:

1
2
article[title]=This is the title&amp;
article[body]=This is the body

Это соответствует формату, в котором контроллеры Rail ожидают доставки атрибутов. Изменения в полезной нагрузке DELETE не требуют пояснений — Rails просто ожидает, что параметры будут пустыми во время запросов DELETE, и маршрутизатор Rails прекратит работу.

В Two Toasters подавляющее большинство наших приложений для iOS построено на двух платформах: RestKit и Three20. Мы обнаружили, что Three20 значительно упрощает и упрощает ряд общих шаблонов в наших приложениях для iOS (например, поддержку диспетчеризации на основе URL) и предоставляет богатую библиотеку компонентов и помощников пользовательского интерфейса, которые делают нас более счастливыми и продуктивными программистами. И RestKit, очевидно, делает работу с данными намного приятнее. Поэтому неудивительно, что между двумя платформами есть точки интеграции.

Интеграция между RestKit и Three20 принимает форму реализации протокола TTModel. TTModel определяет интерфейс для абстрактных моделей данных, чтобы информировать компоненты пользовательского интерфейса Three20 об их состоянии и предоставить им данные. TTModel является основой для всех контроллеров табличного представления Three20, а также ряда других компонентов. RestKit поставляется с необязательной libRestKitThree20целью, которая обеспечивает интерфейс для выведения таблиц Three20 из загрузчика объектов RestKit через RKRequestTTModelкласс. RKRequestTTModelпозволяет нам обрабатывать все моделирование, анализ и сопоставление объектов с помощью RestKit, а затем подключать нашу модель данных непосредственно к Three20 для представления.RKRequestTTModelтакже обеспечивает прозрачную автономную поддержку и периодическое обновление данных в наших пользовательских интерфейсах. Когда вы используете Core Data для поддержки своей модели данных и использования RKRequestTTModelв своих контроллерах, RestKit автоматически извлекает любые объекты из кэша, которые находятся на пути к ресурсу, который вы загружаете, в случае, если вы находитесь в автономном режиме. RKRequestTTModelтакже можно настроить подключение к сети только через определенное время путем настройки refreshRateсвойства.

В дополнение к RKRequestTTModel, детский класс также RKRequestFilterableTTModelпредоставляется. RKRequestFilterableTTModelобеспечивает поддержку сортировки и поиска в коллекции загруженных объектов и может быть полезен для обеспечения операций фильтрации на стороне клиента.

Поддержка Three20 находится на вершине большой технологической пирамиды и опирается почти на каждую часть платформы, которую мы обсуждали до сих пор. Объем кода, необходимый для того, чтобы увидеть все преимущества инфраструктуры на этом уровне, уместен в тексте этого текста. Полнофункциональное приложение RestKit, использующее Core Data, Ruby on Rails и Three20, доступно в каталоге examples / RKDiscussionBoardExample. Пожалуйста, внимательно посмотрите на пример доски обсуждений и присоединитесь к списку рассылки RestKit. Сообщество довольно активно и с радостью помогает новым пользователям.

Мы надеемся, что вы узнали о RestKit весело и полезно. На данный момент мы рассмотрели подавляющее большинство фреймворков, и вы должны быть готовы использовать RestKit в вашем следующем приложении RESTful для iOS. Платформа быстро созревает и быстро повторяется, поэтому, пожалуйста, не забудьте присоединиться к списку рассылки или подписаться на @RestKit в Twitter, чтобы быть в курсе последних событий. Удачного кодирования!