Skip to content

Commit

Permalink
Merge branch 'release/0.25.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
segiddins committed Aug 7, 2015
2 parents 0b898aa + fecde9d commit 0e7bf23
Show file tree
Hide file tree
Showing 71 changed files with 1,114 additions and 1,100 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -33,3 +33,4 @@ DerivedData
# Bundler binstubs
.bundle
bin
RestKit.xcscmblueprint
15 changes: 0 additions & 15 deletions .gitmodules
@@ -1,15 +0,0 @@
[submodule "Vendor/AFNetworking"]
path = Vendor/AFNetworking
url = https://github.com/AFNetworking/AFNetworking.git
[submodule "Vendor/TransitionKit"]
path = Vendor/TransitionKit
url = https://github.com/blakewatters/TransitionKit.git
[submodule "Vendor/RKValueTransformers"]
path = Vendor/RKValueTransformers
url = https://github.com/RestKit/RKValueTransformers.git
[submodule "Vendor/ISO8601DateFormatterValueTransformer"]
path = Vendor/ISO8601DateFormatterValueTransformer
url = https://github.com/blakewatters/ISO8601DateFormatterValueTransformer.git
[submodule "Vendor/SOCKit"]
path = Vendor/SOCKit
url = https://github.com/NimbusKit/sockit.git
2 changes: 1 addition & 1 deletion .ruby-version
@@ -1 +1 @@
2.2.1
2.2.2
2 changes: 1 addition & 1 deletion .travis.yml
@@ -1,5 +1,5 @@
language: objective-c
rvm: 2.2.1
rvm: 2.2.2
install:
- bundle install
- bundle exec pod install
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
@@ -1,6 +1,6 @@
## Issues

GitHub Issues is for reporting bugs in RestKit and discussing changes to RestKit itself. Please check the [documentation](http://cocoadocs.org/docsets/RestKit/), [wiki](https://github.com/RestKit/RestKit/wiki), and [existing issues](https://github.com/RestKit/RestKit/issues?state=closed) before opening a new issue.
GitHub Issues is for reporting bugs in RestKit and discussing changes to RestKit itself. Please check the [documentation](http://cocoadocs.org/docsets/RestKit/), [wiki](https://github.com/RestKit/RestKit/wiki), and [existing issues](https://github.com/RestKit/RestKit/issues?q=is:issue) before opening a new issue.

Additionaly, please do not post general usage questions to Issues, but instead take them to [Stack Overflow](http://stackoverflow.com/questions/tagged/restkit).

Expand Down
7 changes: 7 additions & 0 deletions Code/CoreData/RKEntityMapping.h
Expand Up @@ -114,6 +114,13 @@
*/
@property (nonatomic, copy) NSPredicate *identificationPredicate;

/**
An optional block which returns a predicate used to filter identified objects during mapping.
@return The identification predicate block.
*/
@property (nonatomic, copy) NSPredicate *(^identificationPredicateBlock)(NSDictionary *representation, NSManagedObjectContext *managedObjectContext);

/**
An optional attribute of the receiver's entity that can be used to detect modification of a given instance. This is used to improve the performance of mapping operations by skipping the property mappings for a given object if it is found to be not modified.
Expand Down
1 change: 1 addition & 0 deletions Code/CoreData/RKEntityMapping.m
Expand Up @@ -190,6 +190,7 @@ - (id)copyWithZone:(NSZone *)zone
copy.entity = self.entity;
copy.identificationAttributes = self.identificationAttributes;
copy.identificationPredicate = self.identificationPredicate;
copy.identificationPredicateBlock = self.identificationPredicateBlock;
copy.deletionPredicate = self.deletionPredicate;
copy.modificationAttribute = self.modificationAttribute;
copy.mutableConnections = [NSMutableArray array];
Expand Down
10 changes: 9 additions & 1 deletion Code/CoreData/RKManagedObjectMappingOperationDataSource.m
Expand Up @@ -253,7 +253,11 @@ - (id)mappingOperation:(RKMappingOperation *)mappingOperation targetObjectForRep
if (existingObjectsOfRelationship && !RKObjectIsCollection(existingObjectsOfRelationship)) existingObjectsOfRelationship = @[ existingObjectsOfRelationship ];
NSSet *setWithNull = [NSSet setWithObject:[NSNull null]];
for (NSManagedObject *existingObject in existingObjectsOfRelationship) {
if (! identificationAttributes && ![existingObject isDeleted]) {
if(existingObject.isDeleted) {
continue;
}

if (!identificationAttributes) {
managedObject = existingObject;
[existingObjectsOfRelationship removeObject:managedObject];
break;
Expand All @@ -273,6 +277,10 @@ - (id)mappingOperation:(RKMappingOperation *)mappingOperation targetObjectForRep
attributeValues:entityIdentifierAttributes
inManagedObjectContext:self.managedObjectContext];
if (entityMapping.identificationPredicate) objects = [objects filteredSetUsingPredicate:entityMapping.identificationPredicate];
if (entityMapping.identificationPredicateBlock) {
NSPredicate *predicate = entityMapping.identificationPredicateBlock(representation, self.managedObjectContext);
if (predicate) objects = [objects filteredSetUsingPredicate:predicate];
}
if ([objects count] > 0) {
managedObject = [objects anyObject];
if ([objects count] > 1) RKLogWarning(@"Managed object cache returned %ld objects for the identifier configured for the '%@' entity, expected 1.", (long) [objects count], [entity name]);
Expand Down
25 changes: 25 additions & 0 deletions Code/CoreData/RKManagedObjectStore.m
Expand Up @@ -103,6 +103,31 @@ - (void)handleManagedObjectContextDidSaveNotification:(NSNotification *)notifica
NSAssert([notification object] == self.observedContext, @"Received Managed Object Context Did Save Notification for Unexpected Context: %@", [notification object]);
if (! [self.objectIDsFromChildDidSaveNotification isEqual:RKSetOfManagedObjectIDsFromManagedObjectContextDidSaveNotification(notification)]) {
[self.mergeContext performBlock:^{

/*
Fault updated objects before merging changes into mainQueueManagedObjectContext.
This enables NSFetchedResultsController to update and re-sort its fetch results and to call its delegate methods
in response Managed Object updates merged from another context.
See:
http://stackoverflow.com/a/3927811/489376
http://stackoverflow.com/a/16296365/489376
for issue details.
*/
for (NSManagedObject *object in [[notification userInfo] objectForKey:NSUpdatedObjectsKey]) {
NSManagedObjectID *objectID = [object objectID];
if (objectID && ![objectID isTemporaryID]) {
NSError *error = nil;
NSManagedObject * updatedObject = [self.mergeContext existingObjectWithID:objectID error:&error];
if (error) {
RKLogDebug(@"Failed to get existing object for objectID (%@). Failed with error: %@", objectID, error);
}
else {
[updatedObject willAccessValueForKey:nil];
}
}
}

[self.mergeContext mergeChangesFromContextDidSaveNotification:notification];
}];
} else {
Expand Down
23 changes: 17 additions & 6 deletions Code/CoreData/RKRelationshipConnectionOperation.m
Expand Up @@ -148,7 +148,14 @@ - (id)findConnectedValueForConnection:(RKConnectionDescription *)connection shou
{
*shouldConnectRelationship = YES;
id connectionResult = nil;
if (connection.sourcePredicate && ![connection.sourcePredicate evaluateWithObject:self.managedObject]) return nil;
if (connection.sourcePredicate) {
__block BOOL evaluationResult;
[self.managedObject.managedObjectContext performBlockAndWait:^{
evaluationResult = [connection.sourcePredicate evaluateWithObject:self.managedObject];
}];

if (!evaluationResult) return nil;
}

if ([connection isForeignKeyConnection]) {
NSDictionary *attributeValues = RKConnectionAttributeValuesWithObject(connection, self.managedObject);
Expand All @@ -157,11 +164,15 @@ - (id)findConnectedValueForConnection:(RKConnectionDescription *)connection shou
*shouldConnectRelationship = NO;
return nil;
}
NSSet *managedObjects = [self.managedObjectCache managedObjectsWithEntity:[connection.relationship destinationEntity]
attributeValues:attributeValues
inManagedObjectContext:self.managedObjectContext];
if (connection.destinationPredicate) managedObjects = [managedObjects filteredSetUsingPredicate:connection.destinationPredicate];
if (!connection.includesSubentities) managedObjects = [managedObjects filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"entity == %@", [connection.relationship destinationEntity]]];
__block NSSet *managedObjects = [self.managedObjectCache managedObjectsWithEntity:[connection.relationship destinationEntity]
attributeValues:attributeValues
inManagedObjectContext:self.managedObjectContext];

[self.managedObjectContext performBlockAndWait:^{
if (connection.destinationPredicate) managedObjects = [managedObjects filteredSetUsingPredicate:connection.destinationPredicate];
if (!connection.includesSubentities) managedObjects = [managedObjects filteredSetUsingPredicate:[NSPredicate predicateWithFormat:@"entity == %@", [connection.relationship destinationEntity]]];
}];

if ([connection.relationship isToMany]) {
connectionResult = managedObjects;
} else {
Expand Down
4 changes: 2 additions & 2 deletions Code/Network/RKHTTPRequestOperation.h
Expand Up @@ -18,8 +18,8 @@
// limitations under the License.
//

#import "AFHTTPClient.h"
#import "AFHTTPRequestOperation.h"
#import <AFNetworking/AFHTTPClient.h>
#import <AFNetworking/AFHTTPRequestOperation.h>

// Expose the default headers from AFNetworking's AFHTTPClient
@interface AFHTTPClient ()
Expand Down
43 changes: 31 additions & 12 deletions Code/Network/RKManagedObjectRequestOperation.m
Expand Up @@ -335,22 +335,22 @@ - (RKMappingResult *)refetchedMappingResult
return URL;
}

static NSSet *RKGatherManagedObjectsFromObjectWithRelationshipMapping(id object, RKRelationshipMapping *relationshipMapping)
static void RKGatherManagedObjectsFromObjectWithRelationshipMapping(id object, RKRelationshipMapping *relationshipMapping, NSMutableSet *managedObjects)
{
NSMutableSet *managedObjects = [NSMutableSet set];
NSSet *relationshipValue = RKFlattenCollectionToSet([object valueForKeyPath:relationshipMapping.destinationKeyPath]);
for (id relatedObject in relationshipValue) {
if ([managedObjects containsObject:relatedObject]) continue;
if ([relatedObject isKindOfClass:[NSManagedObject class]]) [managedObjects addObject:relatedObject];

if ([relationshipMapping.mapping isKindOfClass:[RKObjectMapping class]]) {
for (RKRelationshipMapping *childRelationshipMapping in [(RKObjectMapping *)relationshipMapping.mapping relationshipMappings]) {
[managedObjects unionSet:RKGatherManagedObjectsFromObjectWithRelationshipMapping(relatedObject, childRelationshipMapping)];
RKGatherManagedObjectsFromObjectWithRelationshipMapping(relatedObject, childRelationshipMapping, managedObjects);
}
} else if ([relationshipMapping.mapping isKindOfClass:[RKDynamicMapping class]]) {
for (RKObjectMapping *objectMapping in [(RKDynamicMapping *)relationshipMapping.mapping objectMappings]) {
@try {
for (RKRelationshipMapping *childRelationshipMapping in objectMapping.relationshipMappings) {
[managedObjects unionSet:RKGatherManagedObjectsFromObjectWithRelationshipMapping(relatedObject, childRelationshipMapping)];
RKGatherManagedObjectsFromObjectWithRelationshipMapping(relatedObject, childRelationshipMapping, managedObjects);
}
}
@catch (NSException *exception) {
Expand All @@ -359,7 +359,6 @@ - (RKMappingResult *)refetchedMappingResult
}
}
}
return managedObjects;
}

static NSSet *RKManagedObjectsFromObjectWithMappingInfo(id object, RKMappingInfo *mappingInfo)
Expand All @@ -373,15 +372,32 @@ - (RKMappingResult *)refetchedMappingResult
if ([[mappingInfo propertyMappings] count] == 0) {
// This object was matched, but no changes were made. Gather all related objects
for (RKRelationshipMapping *relationshipMapping in [mappingInfo.objectMapping relationshipMappings]) {
[managedObjects unionSet:RKGatherManagedObjectsFromObjectWithRelationshipMapping(object, relationshipMapping)];
RKGatherManagedObjectsFromObjectWithRelationshipMapping(object, relationshipMapping, managedObjects);
}
} else {
} else {
for (NSString *destinationKeyPath in mappingInfo.relationshipMappingInfo) {
id relationshipValue = [object valueForKeyPath:destinationKeyPath];
id relationshipValue = nil;
// Objects in collection may have different types, so destination keypath may be not applicable to each of them
if([object conformsToProtocol:@protocol(NSFastEnumeration)]) {
NSMutableSet* results = [NSMutableSet set];
for (id item in object) {
@try {
id value = [item valueForKeyPath:destinationKeyPath];
[results addObject:value];
} @catch(NSException*) {
continue;
}
}

relationshipValue = results;
} else {
relationshipValue = [object valueForKeyPath:destinationKeyPath];
}

NSArray *mappingInfos = (mappingInfo.relationshipMappingInfo)[destinationKeyPath];
for (RKMappingInfo *relationshipMappingInfo in mappingInfos) {
NSUInteger index = [mappingInfos indexOfObject:relationshipMappingInfo];
id mappedObjectAtIndex = ([relationshipValue respondsToSelector:@selector(objectAtIndex:)]) ? [NSSet setWithObject:relationshipValue[index]] : relationshipValue;
id mappedObjectAtIndex = ([relationshipValue respondsToSelector:@selector(objectAtIndex:)]) ? relationshipValue[index] : relationshipValue;
[managedObjects unionSet:RKFlattenCollectionToSet(RKManagedObjectsFromObjectWithMappingInfo(mappedObjectAtIndex, relationshipMappingInfo))];
}
}
Expand All @@ -400,7 +416,7 @@ - (RKMappingResult *)refetchedMappingResult
id objectsAtRoot = mappingResultDictionary[rootKey];
for (RKMappingInfo *mappingInfo in mappingInfoArray) {
NSUInteger index = [mappingInfoArray indexOfObject:mappingInfo];
id mappedObjectAtIndex = ([objectsAtRoot respondsToSelector:@selector(objectAtIndex:)]) ? [NSSet setWithObject:objectsAtRoot[index]] : objectsAtRoot;
id mappedObjectAtIndex = ([objectsAtRoot respondsToSelector:@selector(objectAtIndex:)]) ? objectsAtRoot[index] : objectsAtRoot;

NSSet *managedObjects = RKManagedObjectsFromObjectWithMappingInfo(mappedObjectAtIndex, mappingInfo);
if (managedObjects) {
Expand Down Expand Up @@ -733,7 +749,10 @@ - (BOOL)deleteLocalObjectsMissingFromMappingResult:(RKMappingResult *)mappingRes
if (! [fetchRequests count]) return YES;

// Proceed with cleanup
NSSet *managedObjectsInMappingResult = RKManagedObjectsFromMappingResultWithMappingInfo(mappingResult, self.mappingInfo) ?: [NSSet set];
__block NSSet *managedObjectsInMappingResult;
[self.privateContext performBlockAndWait:^{
managedObjectsInMappingResult = RKManagedObjectsFromMappingResultWithMappingInfo(mappingResult, self.mappingInfo) ?: [NSSet set];
}];
NSSet *localObjects = [self localObjectsFromFetchRequests:fetchRequests matchingRequestURL:error];
if (! localObjects) {
RKLogError(@"Failed when attempting to fetch local candidate objects for orphan cleanup: %@", error ? *error : nil);
Expand Down
9 changes: 5 additions & 4 deletions Code/Network/RKObjectManager.h
Expand Up @@ -21,12 +21,13 @@
#import "RKRouter.h"
#import "RKPaginator.h"
#import "RKMacros.h"
#import "AFNetworking.h"

#import <AFNetworking/AFNetworking.h>

#ifdef _COREDATADEFINES_H
#if __has_include("RKCoreData.h")
#define RKCoreDataIncluded
#endif
# if __has_include("RKCoreData.h")
# define RKCoreDataIncluded
# endif
#endif

@protocol RKSerialization;
Expand Down
14 changes: 7 additions & 7 deletions Code/Network/RKObjectManager.m
Expand Up @@ -38,16 +38,16 @@
#import "RKRouteSet.h"

#ifdef _COREDATADEFINES_H
#if __has_include("RKCoreData.h")
#define RKCoreDataIncluded
#import "RKManagedObjectStore.h"
#import "RKManagedObjectRequestOperation.h"
#endif
# if __has_include("RKCoreData.h")
# define RKCoreDataIncluded
# import "RKManagedObjectStore.h"
# import "RKManagedObjectRequestOperation.h"
# endif
#endif

#if !__has_feature(objc_arc)
#error RestKit must be built with ARC.
// You can turn on ARC for only RestKit files by adding "-fobjc-arc" to the build phase for each of its files.
#error RestKit must be built with ARC. \
You can turn on ARC for only RestKit files by adding "-fobjc-arc" to the build phase for each of its files.
#endif

//////////////////////////////////
Expand Down
10 changes: 6 additions & 4 deletions Code/Network/RKResponseMapperOperation.m
Expand Up @@ -311,10 +311,12 @@ - (void)willFinish
{
if (self.isCancelled && !self.error) self.error = [NSError errorWithDomain:RKErrorDomain code:RKOperationCancelledError userInfo:@{ NSLocalizedDescriptionKey: @"The operation was cancelled." }];

if (self.didFinishMappingBlock) {
if (self.error) self.didFinishMappingBlock(nil, self.error);
else self.didFinishMappingBlock(self.mappingResult, nil);
[self setDidFinishMappingBlock:nil];
@synchronized(self) {
if (self.didFinishMappingBlock) {
if (self.error) self.didFinishMappingBlock(nil, self.error);
else self.didFinishMappingBlock(self.mappingResult, nil);
[self setDidFinishMappingBlock:nil];
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion Code/ObjectMapping.h
Expand Up @@ -18,10 +18,11 @@
// limitations under the License.
//

#import <RKValueTransformers/RKValueTransformers.h>

#import "RKObjectMapping.h"
#import "RKAttributeMapping.h"
#import "RKRelationshipMapping.h"
#import "RKValueTransformers.h"
#import "RKMappingResult.h"
#import "RKMapperOperation.h"
#import "RKDynamicMapping.h"
Expand Down
29 changes: 26 additions & 3 deletions Code/ObjectMapping/RKHTTPUtilities.m
Expand Up @@ -344,8 +344,13 @@ RKRequestMethod RKRequestMethodFromString(NSString *methodName)
int parsed = 0, cs = 1;
NSDate *date = NULL;

#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && (__IPHONE_OS_VERSION_MAX_ALLOWED < 70000)) || \
(defined(MAC_OS_X_VERSION_MAX_ALLOWED) && (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9))
CFGregorianDate gdate;
memset(&gdate, 0, sizeof(CFGregorianDate));
#else
NSDateComponents *gdate = [[NSDateComponents alloc] init];
#endif

{
int _slen, _trans;
Expand Down Expand Up @@ -397,11 +402,29 @@ RKRequestMethod RKRequestMethodFromString(NSString *methodName)
_out: {}
}

static CFTimeZoneRef gmtTimeZone;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ gmtTimeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0.0); });

if(parsed == 1) { date = [NSDate dateWithTimeIntervalSinceReferenceDate:CFGregorianDateGetAbsoluteTime(gdate, gmtTimeZone)]; }
#if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && (__IPHONE_OS_VERSION_MAX_ALLOWED < 70000)) || \
(defined(MAC_OS_X_VERSION_MAX_ALLOWED) && (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9))
static CFTimeZoneRef gmtTimeZone;
dispatch_once(&onceToken, ^{
gmtTimeZone = CFTimeZoneCreateWithTimeIntervalFromGMT(NULL, 0.0);
});

if (parsed == 1) {
date = [NSDate dateWithTimeIntervalSinceReferenceDate:CFGregorianDateGetAbsoluteTime(gdate, gmtTimeZone)];
}
#else
static NSCalendar *gregorian;
dispatch_once(&onceToken, ^{
gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
gregorian.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
});

if (parsed == 1) {
date = [gregorian dateFromComponents:gdate];
}
#endif

return(date);
}
Expand Down

0 comments on commit 0e7bf23

Please sign in to comment.