2013-08-30 2 views
1

Я использую AFNetworking и MagicalRecord (текущая ветвь разработки), и я пытаюсь выяснить, как импортировать множество объектов, которые зависят друг от друга. Каждый ресурс/объект имеет несколько загрузок. У меня есть класс, управляющий загрузками для данного объекта и сохраняющий их с помощью MagicalDataImport (что было потрясающе).Импорт с MagicalRecord + AFNetworking

Я считаю, что моя проблема заключается в том, что импорт не происходит в той же теме. Поэтому я думаю, что происходит следующее:

  • В одном потоке EntityA правильно сохраняется и распространяется на родительский объект.
  • Затем в другом потоке EntityB сохраняется, и вместе с этим создается его отношение к EntityA. Это означает, что создается пустой объект (ошибка?). Затем, когда он распространяется на родительский объект, я считаю, что EntityA перезаписывает EntityA, который есть. Таким образом, у меня остались некоторые объекты, у которых нет всех атрибутов.

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

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

Вот мой AFJSONRequest:

AFJSONRequestOperation *operation = [AFJSONRequestOperation 
    JSONRequestOperationWithRequest:request 
    success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) 
    { 
     [self saveResources:[JSON objectForKey:@"data"]]; 
    } 
    failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) 
    { 
     DLog(@"%@",error.userInfo); 
     [self.webService command:self didFail:error.localizedDescription]; 
    }]; 

[operation setQueuePriority:self.priority]; 

И это вызывает saveResources::

- (void)saveResources:(NSArray*)resources { 
    BOOL stopDownloads = [self stopDownloadsBasedOnDate:resources]; 
    if ([resources count] > 0 && !stopDownloads){ 
     self.offset = @([offset intValue] + [resources count]); 
     [self send]; 
    } 

    [MagicalRecord saveWithBlock:^(NSManagedObjectContext *blockLocalContext) { 
     [self.classRef MR_importFromArray:resources inContext:blockLocalContext]; 
    } completion:^(BOOL success, NSError *error) { 
     if (error){ 
      // ... handle errors 
     } 
     else { 
      // ... handle callbacks 
     } 
    }]; 
} 

Это пинает другую загрузку ([self send]), а затем сохраняет объекты.

Я знаю, что по умолчанию AFNetworking вызывает обратный вызов в основной очереди, и я попытался установить SuccessCallbackQueue/FailureCallbackQueue на мой фоновый поток, но это, похоже, не решает все проблемы, у меня все еще есть отношения к поврежденным объектам, хотя я думаю, что мне нужно сделать это, чтобы все происходило в фоновом потоке.

Есть ли что-то еще, что мне нужно для вызова, чтобы правильно распространять эти изменения в главном контексте? Или есть другой способ, которым я должен настроить это, чтобы убедиться, что все объекты сохранены правильно, и отношения правильно построены?

Обновление Я переписал вопрос, чтобы попытаться дать больше разъяснений по этим вопросам.

Update

Если вам нужно больше кода я created a gist с (я считаю) все.

+0

Можете ли вы опубликовать весь блок завершения AFNetworking, который вы пытаетесь использовать? Может помочь дать немного больше контекста. –

+0

Что касается вопроса о формате JSON Format, мы делаем импорт так, как этот вопрос предполагает, что он не должен работать, и он работает нормально. Вам просто нужно убедиться, что идентификаторы правильно отображаются в качестве первичного ключа и что ключ сопоставления отношений правильный. –

+0

@CoryImdieke - Я обновил свой вопрос. Я считаю, что все мои сопоставления отношений верны (я все время смотрю на это). У меня есть первичный ключ ('relatedByAttribute'), определенный на самой модели и на отношении. У отношений есть 'relatedByAttribute' и' mappedKeyName'. Благодарю. – RyanJM

ответ

2

В итоге у меня была такая же проблема несколько дней назад. Моя проблема заключалась в том, что я получил запись клиента из моего API с AFNetworking. У этого клиента могли быть домашние животные, но на данный момент у меня не было petTypes, чтобы соответствовать записи домашних животных клиентов.

Что я сделал для этого, это создать трансформируемый атрибут с NSArray, который временно сохранил бы моих питомцев, пока мои petTypes не будут импортированы. После импорта petTypes я затем вызвал NSNotificationCenter postNotification (или вы можете просто импортировать домашних животных в завершение).

я перечислил через временный трансформируемой атрибут, который хранится мое домашнее животное записи, а затем связывал с petType

Кроме того, я вижу, что вы делаете свой импорт внутри обработчика сохранения. Это не нужно. Выполнение вашего MR_importFromArray будет автоматически сэкономить. Если вы не используете метод MR_import, вы должны использовать методы saveToPersistentStore.

Одна вещь: я не вижу, где вы связываете отношения. Is EntityB отношение к EntityA отправлено через JSON с EntityA Возражение в EntityB?

Если это так, то здесь отношения перепутались, поскольку они создают/перезаписывают существующие EntityA для тех, которые указаны в EntityB. Моя рекомендация - сделать что-то подобное.

NSArray *petFactors = [responseObject valueForKeyPath:@"details.items"]; 
NSManagedObjectContext *currentContext = [NSManagedObjectContext MR_context]; 
Pets *pet = [Pets MR_findFirstByAttribute:@"id" withValue:petId inContext:currentContext]; 

pet.petFactors = nil; 
for (id factor in petFactors) { 
    [pet addPetFactorsObject:[PetFactors MR_findFirstByAttribute:@"id" withValue:[factor valueForKey:@"factorId"]]]; 
} 

[currentContext MR_saveToPersistentStoreWithCompletion:^(BOOL success, NSError *error) { 
    if (success) { 
     NSLog(@"SAVED PET FACTORS"); 
     [[NSNotificationCenter defaultCenter] postNotificationName:kPetFactorsSavedSuccessfully object:nil]; 
    } else { 
     NSLog(@"Error: %@", error); 
    } 
}]; 
+0

Спасибо, Bot. Я должен попробовать это. Связь создается через вызовы importFromArray.Я полагал, что это связано с переписыванием друг друга, я просто не знаю, как этого избежать. Кроме того, я не верю, что importFromArray фактически сохраняет в [развитии филиала] (https://github.com/magicalpanda/MagicalRecord/blob/develop/MagicalRecord/Categories/NSManagedObject/NSManagedObject%2BMagicalDataImport.m#L303), am Я чего-то не хватает? – RyanJM

+0

@ RyanJM это должно быть. Я также использую ветку dev, и мои сохраняются. – Bot

+0

@ RyanJM как это получилось? – Bot

1

Я поставил это как ответ, хотя я не уверен на 100%, если это ваша проблема или нет. Я думаю, проблема связана с вашим localContext. Вот пример метода веб-запроса из приложения, которое мы написали, которое использует импорт данных, вы можете использовать его в качестве примера, чтобы заставить вас работать.

Обратите внимание, что AFNetworking выполняет свой блок завершения в основном потоке, затем метод MagicalRecord saveInBackground переключается обратно в фоновый поток для импорта и обработки, тогда окончательный блок завершения MR снова выполняет блок обработчика в основном потоке , LocalContext, который используется для импорта, создается/управляется методом saveInBackground. Как только этот метод будет завершен, контекст будет сохранен и объединен с основным контекстом приложения, и тогда все данные будут доступны.

- (void)listWithCompletionHandler:(void (^)(BOOL success))handler{ 
    [[MyAPIClient sharedClient] getPath:@"list.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject){ 
     NSString *statusString = [responseObject objectForKey:@"status"]; 

     // Handle an error response 
     if(![statusString isKindOfClass:[NSString class]] || ![statusString isEqualToString:@"success"]){ 
      // Request failure 
      NSLog(@"List Request Error: %@", statusString); 
      NSLog(@"%@", [responseObject objectForKey:@"message"]); 
      if(handler) 
       handler(NO); 
      return; 
     } 

     NSArray *itemsArray = [responseObject objectForKey:@"items"]; 

     [MagicalRecord saveInBackgroundWithBlock:^(NSManagedObjectContext *localContext){ 
      // Load into internal database 
      NSArray *fetchedItems = [Item importFromArray:itemsArray inContext:localContext]; 

      NSLog(@"Loaded %d Items", [fetchedItems count]); 
     } completion:^{ 
      if(handler) 
       handler(YES); 
     }]; 
    } failure:^(AFHTTPRequestOperation *operation, NSError *error){ 
     NSLog(@"Fail: %@", error); 
     if(handler) 
      handler(NO); 
    }]; 
} 
+0

Спасибо @Cory, 'saveInBackgroundWithBlock' теперь обесценивается, поэтому я все еще пытаюсь выяснить, как это сделать правильно. Я изменил использование saveWithBlock, но по-прежнему вызывает проблемы. Я обновлю код в своем вопросе. – RyanJM

+0

Работало ли оно с saveInBackground до того, как оно устарело? В последнее время они много работали над МР, которые еще не были выпущены для освоения, возможно, решение вашей проблемы находится в отделе разработки МР. –

+0

Я использую ветвь разработки MR. Он также отлично работает с saveInBackground. – RyanJM

Смежные вопросы