Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Added methods for saving and fetching with completion blocks #77

Closed
wants to merge 1 commit into from

2 participants

@adamhowardprice

Created a category on NSManagedObjectContext to wrap existing NSManagedObjectContext methods with special AFIncrementalStore-related key/value pairs added to the userInfo dictionary.

Added methods for saving and fetching without triggering network requests.

Fixed crash issue referenced in https://github.com/AFNetworking/AFIncrementalStore/issues/76

@adamhowardprice adamhowardprice Created category on NSManagedObjectContext, adding methods for saving…
… and fetching with completion blocks, as well as saving and fetching without triggering network requests
3555071
@mattt
Owner

Thanks for your fix to Issue #76. I cherry-picked just those lines with attribution in 18805da.

As for the rest of the changes, I'm not the biggest fan of that approach. Necessary categories on Core Data classes goes down the path of API bloat and dependencies that plagues Core Data meta-frameworks. Communicating vital state through userInfo like this also feels somewhat fragile.

As far as conditionally triggering network requests for inserts / updates / deletes, I think the terms of that could just be determined based on whether the client implements the optional protocol methods (or returns non-nil request, even).

Block methods for context saving would be better than the current use of notifications. Someone recently suggested that I could accomplish this with a manager object that saves the context and listens for the relevant notifications, triggering the specified blocks when appropriate. Manager as an interface to blocks from notifications seems like the best pattern in this case.

Anyway, thanks again for that one fix. I'll those aforementioned changes in soon.

@mattt mattt closed this
@adamhowardprice

I've been thinking more about this problem, and I like the idea of an object listening for these notifications and executing blocks as needed. I think you make a good point about trying to avoid required categories on Core Data classes. However, since the two important method calls AFIncrementalStore works on are both on NSManagedObjectContext, I can't think of another way to structure the method that would both execute a fetchRequest or saveRequest and include a completion block. What are you leaning towards at the moment?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Oct 16, 2012
  1. @adamhowardprice

    Created category on NSManagedObjectContext, adding methods for saving…

    adamhowardprice authored
    … and fetching with completion blocks, as well as saving and fetching without triggering network requests
This page is out of date. Refresh to see the latest.
View
243 AFIncrementalStore/AFIncrementalStore.m
@@ -23,6 +23,7 @@
#import "AFIncrementalStore.h"
#import "AFHTTPClient.h"
#import "ISO8601DateFormatter.h"
+#import "NSManagedObjectContext+AFIncrementalStore.h"
#import <objc/runtime.h>
NSString * const AFIncrementalStoreUnimplementedMethodException = @"com.alamofire.incremental-store.exceptions.unimplemented-method";
@@ -257,10 +258,10 @@ - (void)insertOrUpdateObjectsFromRepresentations:(id)representationOrArrayOfRepr
if ([relationship isToMany]) {
if ([relationship isOrdered]) {
[managedObject setValue:[NSOrderedSet orderedSetWithArray:managedObjects] forKey:relationship.name];
- [backingObject setValue:[NSOrderedSet orderedSetWithArray:managedObjects] forKey:relationship.name];
+ [backingObject setValue:[NSOrderedSet orderedSetWithArray:backingObjects] forKey:relationship.name];
} else {
[managedObject setValue:[NSSet setWithArray:managedObjects] forKey:relationship.name];
- [backingObject setValue:[NSSet setWithArray:managedObjects] forKey:relationship.name];
+ [backingObject setValue:[NSSet setWithArray:backingObjects] forKey:relationship.name];
}
} else {
[managedObject setValue:[managedObjects lastObject] forKey:relationship.name];
@@ -301,34 +302,50 @@ - (id)executeFetchRequest:(NSFetchRequest *)fetchRequest
withContext:(NSManagedObjectContext *)context
error:(NSError *__autoreleasing *)error
{
- NSURLRequest *request = [self.HTTPClient requestForFetchRequest:fetchRequest withContext:context];
- if ([request URL]) {
- AFHTTPRequestOperation *operation = [self.HTTPClient HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
- id representationOrArrayOfRepresentations = [self.HTTPClient representationOrArrayOfRepresentationsFromResponseObject:responseObject];
-
- NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
- childContext.parentContext = context;
- childContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
-
- [childContext performBlock:^{
- [self insertOrUpdateObjectsFromRepresentations:representationOrArrayOfRepresentations ofEntity:fetchRequest.entity fromResponse:operation.response withContext:childContext error:error completionBlock:^(NSArray *managedObjects, NSArray *backingObjects) {
- if (![[self backingManagedObjectContext] save:error] || ![childContext save:error]) {
- NSLog(@"Error: %@", *error);
- }
- }];
-
- [self notifyManagedObjectContext:context aboutRequestOperation:operation forFetchRequest:fetchRequest];
- }];
- } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
- NSLog(@"Error: %@", error);
- [self notifyManagedObjectContext:context aboutRequestOperation:operation forFetchRequest:fetchRequest];
- }];
-
- operation.successCallbackQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
-
- [self notifyManagedObjectContext:context aboutRequestOperation:operation forFetchRequest:fetchRequest];
- [self.HTTPClient enqueueHTTPRequestOperation:operation];
- }
+ BOOL shouldEnqueueNetworkRequest = YES;
+ if ([context.userInfo valueForKey:AFContextShouldDisableAutomaticTriggeringNetworkRequestKey] == @(YES))
+ {
+ [context.userInfo setValue:@(NO) forKey:AFContextShouldDisableAutomaticTriggeringNetworkRequestKey];
+ shouldEnqueueNetworkRequest = NO;
+ }
+
+ if (shouldEnqueueNetworkRequest)
+ {
+ NSURLRequest *request = [self.HTTPClient requestForFetchRequest:fetchRequest withContext:context];
+ if ([request URL]) {
+ AFHTTPRequestOperation *operation = [self.HTTPClient HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
+ id representationOrArrayOfRepresentations = [self.HTTPClient representationOrArrayOfRepresentationsFromResponseObject:responseObject];
+
+ NSManagedObjectContext *childContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
+ childContext.parentContext = context;
+ childContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy;
+
+ [childContext performBlock:^{
+ [self insertOrUpdateObjectsFromRepresentations:representationOrArrayOfRepresentations ofEntity:fetchRequest.entity fromResponse:operation.response withContext:childContext error:error completionBlock:^(NSArray *managedObjects, NSArray *backingObjects) {
+ if (![[self backingManagedObjectContext] save:error] || ![childContext save:error]) {
+ NSLog(@"Error: %@", *error);
+ }
+ AFFetchRequestCompletionBlock completionBlock = [context.userInfo objectForKey:AFContextFetchCompletionBlockKey];
+ if (completionBlock)
+ {
+ completionBlock(managedObjects);
+ [context.userInfo removeObjectForKey:AFContextFetchCompletionBlockKey];
+ }
+ }];
+
+ [self notifyManagedObjectContext:context aboutRequestOperation:operation forFetchRequest:fetchRequest];
+ }];
+ } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
+ NSLog(@"Error: %@", error);
+ [self notifyManagedObjectContext:context aboutRequestOperation:operation forFetchRequest:fetchRequest];
+ }];
+
+ operation.successCallbackQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
+
+ [self notifyManagedObjectContext:context aboutRequestOperation:operation forFetchRequest:fetchRequest];
+ [self.HTTPClient enqueueHTTPRequestOperation:operation];
+ }
+ }
NSManagedObjectContext *backingContext = [self backingManagedObjectContext];
NSArray *results = nil;
@@ -374,76 +391,100 @@ - (id)executeSaveChangesRequest:(NSSaveChangesRequest *)saveChangesRequest
withContext:(NSManagedObjectContext *)context
error:(NSError *__autoreleasing *)error
{
- NSMutableArray *mutableOperations = [NSMutableArray array];
- NSManagedObjectContext *backingContext = [self backingManagedObjectContext];
+ BOOL shouldEnqueueNetworkRequest = YES;
+ if ([context.userInfo valueForKey:AFContextShouldDisableAutomaticTriggeringNetworkRequestKey] == @(YES))
+ {
+ [context.userInfo setValue:@(NO) forKey:AFContextShouldDisableAutomaticTriggeringNetworkRequestKey];
+ shouldEnqueueNetworkRequest = NO;
+ }
+
+ if (shouldEnqueueNetworkRequest)
+ {
+ NSMutableArray *mutableOperations = [NSMutableArray array];
+ NSManagedObjectContext *backingContext = [self backingManagedObjectContext];
+
+ __block NSMutableSet *returnedInsertedObjects;
+ if ([self.HTTPClient respondsToSelector:@selector(requestForInsertedObject:)]) {
+ returnedInsertedObjects = [NSMutableSet setWithCapacity:[[saveChangesRequest insertedObjects] count]];
+ for (NSManagedObject *insertedObject in [saveChangesRequest insertedObjects]) {
+ NSURLRequest *request = [self.HTTPClient requestForInsertedObject:insertedObject];
+ AFHTTPRequestOperation *operation = [self.HTTPClient HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
+ NSString *resourceIdentifier = [self.HTTPClient resourceIdentifierForRepresentation:responseObject ofEntity:[insertedObject entity] fromResponse:operation.response];
+ NSManagedObjectID *objectID = [self objectIDForEntity:[insertedObject entity] withResourceIdentifier:resourceIdentifier];
+ insertedObject.af_resourceIdentifier = resourceIdentifier;
+ [insertedObject setValuesForKeysWithDictionary:[self.HTTPClient attributesForRepresentation:responseObject ofEntity:insertedObject.entity fromResponse:operation.response]];
+ [returnedInsertedObjects addObject:insertedObject];
+
+ [backingContext performBlockAndWait:^{
+ NSManagedObject *backingObject = (objectID != nil) ? [backingContext existingObjectWithID:objectID error:nil] : [NSEntityDescription insertNewObjectForEntityForName:insertedObject.entity.name inManagedObjectContext:backingContext];
+ [backingObject setValue:resourceIdentifier forKey:kAFIncrementalStoreResourceIdentifierAttributeName];
+ [backingObject setValuesForKeysWithDictionary:[insertedObject dictionaryWithValuesForKeys:nil]];
+ [backingContext save:nil];
+ }];
+
+ [context obtainPermanentIDsForObjects:[NSArray arrayWithObject:insertedObject] error:nil];
+
+ } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
+ NSLog(@"Insert Error: %@", error);
+ }];
+
+ [mutableOperations addObject:operation];
+ }
+ }
+
+ __block NSMutableSet *returnedUpdatedObjects;
+ if ([self.HTTPClient respondsToSelector:@selector(requestForUpdatedObject:)]) {
+ returnedUpdatedObjects = [NSMutableSet setWithCapacity:[[saveChangesRequest updatedObjects] count]];
+ for (NSManagedObject *updatedObject in [saveChangesRequest updatedObjects]) {
+ NSURLRequest *request = [self.HTTPClient requestForUpdatedObject:updatedObject];
+ AFHTTPRequestOperation *operation = [self.HTTPClient HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
+ [updatedObject setValuesForKeysWithDictionary:[self.HTTPClient attributesForRepresentation:responseObject ofEntity:updatedObject.entity fromResponse:operation.response]];
+ [returnedUpdatedObjects addObject:updatedObject];
+
+ [backingContext performBlockAndWait:^{
+ NSManagedObject *backingObject = [backingContext existingObjectWithID:updatedObject.objectID error:nil];
+ [backingObject setValuesForKeysWithDictionary:[updatedObject dictionaryWithValuesForKeys:nil]];
+ [backingContext save:nil];
+ }];
+ } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
+ NSLog(@"Update Error: %@", error);
+ }];
+
+ [mutableOperations addObject:operation];
+ }
+ }
+
+ if ([self.HTTPClient respondsToSelector:@selector(requestForDeletedObject:)]) {
+ for (NSManagedObject *deletedObject in [saveChangesRequest deletedObjects]) {
+ NSURLRequest *request = [self.HTTPClient requestForDeletedObject:deletedObject];
+ AFHTTPRequestOperation *operation = [self.HTTPClient HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
+ [backingContext performBlockAndWait:^{
+ NSManagedObject *backingObject = [backingContext existingObjectWithID:deletedObject.objectID error:nil];
+ [backingContext deleteObject:backingObject];
+ [backingContext save:nil];
+ }];
+ } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
+ NSLog(@"Delete Error: %@", error);
+ }];
+
+ [mutableOperations addObject:operation];
+ }
+ }
+
+ [self notifyManagedObjectContext:context aboutRequestOperations:mutableOperations forSaveChangesRequest:saveChangesRequest];
+
+ [self.HTTPClient enqueueBatchOfHTTPRequestOperations:mutableOperations progressBlock:nil completionBlock:^(NSArray *operations) {
+ [self notifyManagedObjectContext:context aboutRequestOperations:operations forSaveChangesRequest:saveChangesRequest];
+
+ AFSaveRequestCompletionBlock completionBlock = [context.userInfo valueForKey:AFContextSaveCompletionBlockKey];
+ if (completionBlock)
+ {
+ completionBlock(returnedInsertedObjects, returnedUpdatedObjects);
+ [context.userInfo removeObjectForKey:AFContextSaveCompletionBlockKey];
+ }
+ }];
+ }
- if ([self.HTTPClient respondsToSelector:@selector(requestForInsertedObject:)]) {
- for (NSManagedObject *insertedObject in [saveChangesRequest insertedObjects]) {
- NSURLRequest *request = [self.HTTPClient requestForInsertedObject:insertedObject];
- AFHTTPRequestOperation *operation = [self.HTTPClient HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
- NSString *resourceIdentifier = [self.HTTPClient resourceIdentifierForRepresentation:responseObject ofEntity:[insertedObject entity] fromResponse:operation.response];
- NSManagedObjectID *objectID = [self objectIDForEntity:[insertedObject entity] withResourceIdentifier:resourceIdentifier];
- insertedObject.af_resourceIdentifier = resourceIdentifier;
- [insertedObject setValuesForKeysWithDictionary:[self.HTTPClient attributesForRepresentation:responseObject ofEntity:insertedObject.entity fromResponse:operation.response]];
-
- [backingContext performBlockAndWait:^{
- NSManagedObject *backingObject = (objectID != nil) ? [backingContext existingObjectWithID:objectID error:nil] : [NSEntityDescription insertNewObjectForEntityForName:insertedObject.entity.name inManagedObjectContext:backingContext];
- [backingObject setValue:resourceIdentifier forKey:kAFIncrementalStoreResourceIdentifierAttributeName];
- [backingObject setValuesForKeysWithDictionary:[insertedObject dictionaryWithValuesForKeys:nil]];
- [backingContext save:nil];
- }];
-
- [context obtainPermanentIDsForObjects:[NSArray arrayWithObject:insertedObject] error:nil];
- } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
- NSLog(@"Insert Error: %@", error);
- }];
-
- [mutableOperations addObject:operation];
- }
- }
-
- if ([self.HTTPClient respondsToSelector:@selector(requestForUpdatedObject:)]) {
- for (NSManagedObject *updatedObject in [saveChangesRequest updatedObjects]) {
- NSURLRequest *request = [self.HTTPClient requestForUpdatedObject:updatedObject];
- AFHTTPRequestOperation *operation = [self.HTTPClient HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
- [updatedObject setValuesForKeysWithDictionary:[self.HTTPClient attributesForRepresentation:responseObject ofEntity:updatedObject.entity fromResponse:operation.response]];
-
- [backingContext performBlockAndWait:^{
- NSManagedObject *backingObject = [backingContext existingObjectWithID:updatedObject.objectID error:nil];
- [backingObject setValuesForKeysWithDictionary:[updatedObject dictionaryWithValuesForKeys:nil]];
- [backingContext save:nil];
- }];
- } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
- NSLog(@"Update Error: %@", error);
- }];
-
- [mutableOperations addObject:operation];
- }
- }
-
- if ([self.HTTPClient respondsToSelector:@selector(requestForDeletedObject:)]) {
- for (NSManagedObject *deletedObject in [saveChangesRequest deletedObjects]) {
- NSURLRequest *request = [self.HTTPClient requestForDeletedObject:deletedObject];
- AFHTTPRequestOperation *operation = [self.HTTPClient HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
- [backingContext performBlockAndWait:^{
- NSManagedObject *backingObject = [backingContext existingObjectWithID:deletedObject.objectID error:nil];
- [backingContext deleteObject:backingObject];
- [backingContext save:nil];
- }];
- } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
- NSLog(@"Delete Error: %@", error);
- }];
-
- [mutableOperations addObject:operation];
- }
- }
-
- [self notifyManagedObjectContext:context aboutRequestOperations:mutableOperations forSaveChangesRequest:saveChangesRequest];
-
- [self.HTTPClient enqueueBatchOfHTTPRequestOperations:mutableOperations progressBlock:nil completionBlock:^(NSArray *operations) {
- [self notifyManagedObjectContext:context aboutRequestOperations:operations forSaveChangesRequest:saveChangesRequest];
- }];
-
return [NSArray array];
}
@@ -547,10 +588,10 @@ - (id)newValueForRelationship:(NSRelationshipDescription *)relationship
if ([relationship isToMany]) {
if ([relationship isOrdered]) {
[managedObject setValue:[NSOrderedSet orderedSetWithArray:managedObjects] forKey:relationship.name];
- [backingObject setValue:[NSOrderedSet orderedSetWithArray:managedObjects] forKey:relationship.name];
+ [backingObject setValue:[NSOrderedSet orderedSetWithArray:backingObjects] forKey:relationship.name];
} else {
[managedObject setValue:[NSSet setWithArray:managedObjects] forKey:relationship.name];
- [backingObject setValue:[NSSet setWithArray:managedObjects] forKey:relationship.name];
+ [backingObject setValue:[NSSet setWithArray:backingObjects] forKey:relationship.name];
}
} else {
[managedObject setValue:[managedObjects lastObject] forKey:relationship.name];
View
60 AFIncrementalStore/NSManagedObjectContext+AFIncrementalStore.h
@@ -0,0 +1,60 @@
+//
+// NSManagedObjectContext+AFIncrementalStore.h
+// Sharely
+//
+// Created by Adam Price on 10/16/12.
+// Copyright (c) 2012 Fuzz Productions. All rights reserved.
+//
+
+#import <CoreData/CoreData.h>
+
+typedef void (^AFSaveRequestCompletionBlock)(NSSet *insertedObjects, NSSet *updatedObjects);
+typedef void (^AFFetchRequestCompletionBlock)(NSArray *fetchedObjects);
+
+/**
+ A key in the 'userInfo' dictionary of a NSManagedObjectContext.
+ The corresponding value is NSNumber with a boolean value. When YES, executeRequest:withContext:error: will return immediately without triggering a network request, and then set the value of the key to NO. */
+extern NSString * const AFContextShouldDisableAutomaticTriggeringNetworkRequestKey;
+
+/**
+ A key in the 'userInfo' dictionary of a NSManagedObjectContext.
+ The corresponding value is a block set to execute on the successful return of an array of managed objects. */
+extern NSString * const AFContextFetchCompletionBlockKey;
+
+/**
+ A key in the 'userInfo' dictionary of a NSManagedObjectContext.
+ The corresponding value is a block set to execute on the successful insertion and/or updating of sets of managed objects. */
+extern NSString * const AFContextSaveCompletionBlockKey;
+
+@interface NSManagedObjectContext (AFIncrementalStore)
+
+/**
+ This method can be called instead of the typical save: method in order to save the context without implicitly asking AFIncrementalStore to enqueue network requests based on the NSSaveChangesRequest.
+ @discussion There are many potential cases where a normal context save without a network request would be appropriate. For example, when terminating the app.
+ */
+- (void)saveWithoutTriggeringNetworkRequest:(NSError *__autoreleasing*)error;
+
+/**
+ This method can be called instead of the typical executeFetchRequest:error: method in order to fetch local objects from the store without implicitly asking AFIncrementalStore to enqueue network requests based on the NSFetchRequest.
+ @discussion There are many potential cases where it may be appropriate to fetch local objects from Core Data without spawning network requests. For example, when fetching a local object that does not exist on the web service.
+ @return An array of objects that meet the criteria specified by request fetched from the receiver and from the persistent stores associated with the receiver’s persistent store coordinator. If an error occurs, returns nil. If no objects match the criteria specified by request, returns an empty array.
+ */
+- (NSArray *)executeFetchRequestWithoutTriggeringNetworkRequest:(NSFetchRequest *)fetchRequest
+ error:(NSError *__autoreleasing*)error;
+/**
+ This method can be called instead of the typical save: method in order to execute code on the inserted or updated NSManagedObjects that were modeled by AFIncrementalStore following a successful network request based on the NSSaveChangesRequest.
+ @discussion This may be a more appropriate callback than the provided NSNotification in a variety of cases. For example, when chaining a set of asynchronous blocks together.
+ */
+- (BOOL)save:(NSError *__autoreleasing*)error
+ completion:(AFSaveRequestCompletionBlock)completionBlock;
+
+/**
+ This method can be called instead of the typical executeFetchRequest:error: method in order to execute code on the fetched NSManagedObjects that were modeled by AFIncrementalStore following a successful network request based on the NSFetchRequest.
+ @discussion This may be a more appropriate callback than the provided NSNotification in a variety of cases. For example, when a different block of code needs to be executed on the cached Core Data objects and the most recent objects from the network.
+ @return An array of objects that meet the criteria specified by request fetched from the receiver and from the persistent stores associated with the receiver’s persistent store coordinator. If an error occurs, returns nil. If no objects match the criteria specified by request, returns an empty array.
+ */
+- (NSArray *)executeFetchRequest:(NSFetchRequest *)fetchRequest
+ error:(NSError *__autoreleasing*)error
+ completion:(AFFetchRequestCompletionBlock)completionBlock;
+
+@end
View
49 AFIncrementalStore/NSManagedObjectContext+AFIncrementalStore.m
@@ -0,0 +1,49 @@
+//
+// NSManagedObjectContext+AFIncrementalStore.m
+// Sharely
+//
+// Created by Adam Price on 10/16/12.
+// Copyright (c) 2012 Fuzz Productions. All rights reserved.
+//
+
+#import "NSManagedObjectContext+AFIncrementalStore.h"
+
+NSString * const AFContextShouldDisableAutomaticTriggeringNetworkRequestKey = @"AFContextShouldDisableAutomaticTriggeringNetworkRequestKey";
+NSString * const AFContextFetchCompletionBlockKey = @"AFContextFetchCompletionBlockKey";
+NSString * const AFContextSaveCompletionBlockKey = @"AFContextFetchCompletionBlockKey";
+
+@implementation NSManagedObjectContext (AFIncrementalStore)
+
+- (void)saveWithoutTriggeringNetworkRequest:(NSError *__autoreleasing*)error
+{
+ [self.userInfo setValue:@(YES) forKey:AFContextShouldDisableAutomaticTriggeringNetworkRequestKey];
+
+ [self save:error];
+}
+
+- (NSArray *)executeFetchRequestWithoutTriggeringNetworkRequest:(NSFetchRequest *)fetchRequest
+ error:(NSError *__autoreleasing*)error
+{
+ [self.userInfo setValue:@(YES) forKey:AFContextShouldDisableAutomaticTriggeringNetworkRequestKey];
+
+ return [self executeFetchRequest:fetchRequest error:error];
+}
+
+- (BOOL)save:(NSError *__autoreleasing*)error
+ completion:(AFSaveRequestCompletionBlock)completionBlock
+{
+ [self.userInfo setValue:completionBlock forKey:AFContextSaveCompletionBlockKey];
+
+ return [self save:error];
+}
+
+- (NSArray *)executeFetchRequest:(NSFetchRequest *)fetchRequest
+ error:(NSError *__autoreleasing*)error
+ completion:(AFFetchRequestCompletionBlock)completionBlock
+{
+ [self.userInfo setValue:completionBlock forKey:AFContextSaveCompletionBlockKey];
+
+ return [self executeFetchRequest:fetchRequest error:error];
+}
+
+@end
Something went wrong with that request. Please try again.