1
votes

I am attempting to use the addFetchRequestBlock method to remove orphaned objects from an API call I'm making. However, the objects I'd like it to remove are the objects from various relationships to the main object.

The outermost object is the Day object which has many FoodEntry objects.

API call to /nutrition/days/2014-07-14

RKManagedObjectRequestOperation *operation = [[RKObjectManager sharedManager] appropriateObjectRequestOperationWithObject:nil method:RKRequestMethodGET path:[NSString stringWithFormat:@"nutrition/days/%@", dateFormatted] parameters:nil];

[operation setDeletesOrphanedObjects:YES];

[operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
    self.day = (Day *)mappingResult.firstObject;
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
    failure([operation decodeErrors]);
}];

[RKObjectManager.sharedManager enqueueObjectRequestOperation:operation];

results in:

{
 "day": {
    "date": "2014-07-24",
        "food_entries": [
            {
                "id": "53d14b973861330009de0300",
                "consumed_on": "2014-07-24",
                "food_id": 36394,
                "serving_id": 34187,
                "quantity": 1,
                "name": "Waffle",
                "brand": null,
                "description": "1 cup slices",
                "meal_type": "breakfast",
                "calories": 25,
                "carbohydrate": 6.68,
                "protein": 0.48,
                "fat": 0.04,
                "saturated_fat": null,
                "polyunsaturated_fat": null,
                "monounsaturated_fat": null,
                "trans_fat": null,
                "cholesterol": 0,
                "sodium": 9,
                "potassium": 43,
                "fiber": null,
                "sugar": null
            },
            {
                "id": "53d151c138313800099c0100",
                "consumed_on": "2014-07-24",
                "food_id": 38720,
                "serving_id": 48329,
                "quantity": 0.5,
                "name": "Australian Lamb Leg (Sirloin Chops, Boneless, Trimmed to 1/8\" Fat)",
                "brand": null,
                "description": "1 lb",
                "meal_type": "breakfast",
                "calories": 471.5,
                "carbohydrate": 0,
                "protein": 41.57,
                "fat": 32.615,
                "saturated_fat": 15.6445,
                "polyunsaturated_fat": 1.472,
                "monounsaturated_fat": 13.3315,
                "trans_fat": null,
                "cholesterol": 149.5,
                "sodium": 134,
                "potassium": 698.5,
                "fiber": null,
                "sugar": null
            }]
      }
 }

The fetch request block I'm using is as follows:

 [[RKObjectManager sharedManager] addFetchRequestBlock:^NSFetchRequest *(NSURL *URL) {
    RKPathMatcher *pathMatcher = [RKPathMatcher pathMatcherWithPattern:@"nutrition/days/:date"];
    NSDictionary *argsDict = nil;
    BOOL match = [pathMatcher matchesPath:[URL relativePath] tokenizeQueryStrings:NO parsedArguments:&argsDict];
    NSDate *date;
    if (match) {
        date = [[[RDFormatters sharedManager] dashFormatUTC] dateFromString:[argsDict objectForKey:@"date"]];
        NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"FoodEntry"];
        fetchRequest.predicate = [NSPredicate predicateWithFormat:@"day.date = %@", date];
        return fetchRequest;
    }
    return nil;
}];

This FetchRequest returns all the FoodEntries associated with the day call, however, I do not think I'm performing the call correctly. I realize this because

  1. It crashes (duh!)
  2. The response of the API call is usually a Day object, but after performing the FetchRequest it becomes a FoodEntry object – the very same FoodEntry object I'm attempting to delete.

Is there a way to delete orphaned objects in relationships via Restkit? What's the proper method for this?

================================

Here is the crash log

2014-07-25 08:38:18.158 Euco[6918:60b] I restkit.network:RKObjectRequestOperation.m:180 GET 'https://example.com/api/v1/nutrition/days/2014-07-24'
2014-07-25 08:38:18.831 Euco[6918:f03] I restkit.network:RKObjectRequestOperation.m:250 GET 'https://example.com/api/v1/nutrition/days/2014-07-24' (200 OK / 4 objects) [request=0.6671s mapping=0.0053s total=0.6881s]
2014-07-25 08:38:18.831 Euco[6918:60b] -[FoodEntry nutrition_plan]: unrecognized selector sent to instance 0x10ab5ff20
(lldb) po self.day
<FoodEntry: 0x10ab5ff20> (entity: FoodEntry; id: 0xd000000000040012 <x-coredata://1CAD1FBC-F8EE-4A9C-A1B4-7DCDF2A5F1D7/FoodEntry/p1> ; data: <fault>)
2014-07-25 08:38:58.103 Euco[6918:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[FoodEntry nutrition_plan]: unrecognized selector sent to instance 0x10ab5ff20'
*** First throw call stack:
(
    0   CoreFoundation                      0x00000001038a1495 __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x00000001032c299e objc_exception_throw + 43
    2   CoreFoundation                      0x000000010393265d -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
    3   CoreFoundation                      0x0000000103892d8d ___forwarding___ + 973
    4   CoreFoundation                      0x0000000103892938 _CF_forwarding_prep_0 + 120
    5   Euco                                0x0000000100036ff8 -[JournalNutritionViewController numberOfSectionsInTableView:] + 88
    6   UIKit                               0x00000001024ec8e2 -[UITableViewRowData(UITableViewRowDataPrivate) _updateNumSections] + 86
    7   UIKit                               0x00000001024ed5b5 -[UITableViewRowData invalidateAllSections] + 66
    8   UIKit                               0x000000010238b0cc -[UITableView _updateRowData] + 191
    9   UIKit                               0x000000010239c225 -[UITableView noteNumberOfRowsChanged] + 91
    10  UIKit                               0x000000010239bd27 -[UITableView reloadData] + 717
    11  Euco                                0x00000001000368c8 __42-[JournalNutritionViewController setDate:]_block_invoke + 184
    12  Euco                                0x0000000100067f7e __57+[Day(ModelFunctions) read:withType:withSuccess:failure:]_block_invoke + 142
    13  Euco                                0x0000000100200b3b __66-[RKObjectRequestOperation setCompletionBlockWithSuccess:failure:]_block_invoke242 + 91
    14  libdispatch.dylib                   0x0000000103e50851 _dispatch_call_block_and_release + 12
    15  libdispatch.dylib                   0x0000000103e6372d _dispatch_client_callout + 8
    16  libdispatch.dylib                   0x0000000103e533fc _dispatch_main_queue_callback_4CF + 354
    17  CoreFoundation                      0x00000001038ff289 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
    18  CoreFoundation                      0x000000010384c854 __CFRunLoopRun + 1764
    19  CoreFoundation                      0x000000010384bd83 CFRunLoopRunSpecific + 467
    20  GraphicsServices                    0x0000000104af0f04 GSEventRunModal + 161
    21  UIKit                               0x00000001022d7e33 UIApplicationMain + 1010
    22  Euco                                0x0000000100063113 main + 115
    23  libdyld.dylib                       0x00000001040b45fd start + 1
    24  ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

When I add an exception breakpoint, it stops here before crashing:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return self.day.nutrition_plan.meal_goals.count
}

When it breaks there, if I run po self.day in the console, it returns <FoodEntry: 0x10ab5ff20> so it is somehow thinking the Day object is a FoodEntry object.

1
Show the crash details (message and stack trace) and the code that you use to test it becomes a FoodEntry object - Wain
hey @Wain thanks for reaching out! I just updated my question (on the bottom) with the trace. - Brian Weinreich
It seems unrelated to the fetch block. Where do you set self.day? - Wain
@Wain once I get a response back I set self.day. I think the problem is that the FetchRequest blocks are performing FetchRequests on the FoodEntry entity while the API call is made to the Day entity (even though they are connected via a relationship). [operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) { self.day = (Day *)mappingResult.firstObject; } failure:^(RKObjectRequestOperation *operation, NSError *error) { failure([operation decodeErrors]); }]; - Brian Weinreich
I keep reading this github.com/RestKit/RestKit/issues/1737 and this restkit.org/api/development/Classes/… and can't seem to grasp how to perform this type of orphaned object cleanup via the built-in Restkit methods. Is it possible that I need to roll my own solution or am I overlooking something? - Brian Weinreich

1 Answers

0
votes

From your description your orphan block looks fine. It verifies the URL that is being processed so should be auctioned at the appropriate times.

This code:

self.day = (Day *)mappingResult.firstObject;

is a risk in a number of cases because you can't always be sure what the first object in the mapping result will be. If:

  1. You have multiple mapped objects (nested)
  2. You have multiple response descriptors (which leads to 1.)

then the mapping result should be queried as a dictionary and you should check the contents before casting them to what you hope they are. Basically never access the mapping result as an array.