Skip to content
This repository has been archived by the owner on Aug 24, 2019. It is now read-only.

Commit

Permalink
Several improvements:
Browse files Browse the repository at this point in the history
* Allow any type for remote ID #20 #22
* Configurable remote ID key in dictionaries #22
* Improved documentation #22
  • Loading branch information
soffes committed Mar 2, 2014
1 parent 5b6f5e6 commit 664cb12
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 40 deletions.
135 changes: 117 additions & 18 deletions SSDataKit/SSRemoteManagedObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,45 +12,139 @@

@interface SSRemoteManagedObject : SSManagedObject

@property (nonatomic, strong) NSNumber *remoteID;
@property (nonatomic, strong) NSDate *createdAt;
@property (nonatomic, strong) NSDate *updatedAt;
///-------------------------
/// @name Default Properties
///-------------------------

/**
This attribute is required! You must add an attribute named `remoteID` to your data model if you want to use
SSRemoteManagedObject with it. It can be any type.
*/
@property (nonatomic) id remoteID;

/**
Optional attribute.
*/
@property (nonatomic) NSDate *createdAt;

/**
Optional attribute.
*/
@property (nonatomic) NSDate *updatedAt;


///--------------------
/// @name Configuration
///--------------------

/**
Key in remote dictionary for the object's remoteID attribute.
The default is `id`.
@return The key of the remote ID attribute.
*/
+ (NSString *)remoteIDDictionaryKey;


///---------------------
/// @name Find or Create
///---------------------

/**
Find an existing object with a given remote ID. The class' entity is used in the find. Therefore, this should only be
called on a subclass. The object will be created if it is not found. If a context is not specified, the `mainContext`
will be used.
called on a subclass. The object will be created if it is not found. The `mainQueueContext` will be used.
@param remoteID The remote ID of the object.
@return An existing object with the given remote ID or a new object with the remoteID set.
*/
+ (id)objectWithRemoteID:(NSNumber *)remoteID;
+ (id)objectWithRemoteID:(NSNumber *)remoteID context:(NSManagedObjectContext *)context;
+ (instancetype)objectWithRemoteID:(id)remoteID;

/**
Find an existing object with a given remote ID. The class' entity is used in the find. Therefore, this should only be
called on a subclass. `nil` is returned if the object is not found. If a context is not specified, the `mainContext`
will be used.
called on a subclass. The object will be created if it is not found.
@param remoteID The remote ID of the object.
@param context The context to use.
@return An existing object with the given remote ID or a new object with the remoteID set.
*/
+ (instancetype)objectWithRemoteID:(id)remoteID context:(NSManagedObjectContext *)context;

/**
Find an existing object with a given remote ID. The class' entity is used in the find. Therefore, this should only be
called on a subclass. `nil` is returned if the object is not found. The `mainQueueContext` will be used.
@param remoteID The remote ID of the object.
@return An existing object with the given remote ID.
*/
+ (id)existingObjectWithRemoteID:(NSNumber *)remoteID;
+ (id)existingObjectWithRemoteID:(NSNumber *)remoteID context:(NSManagedObjectContext *)context;
+ (instancetype)existingObjectWithRemoteID:(id)remoteID;

/**
Find an existing object with a given remote ID. The class' entity is used in the find. Therefore, this should only be
called on a subclass. The object will be created if it is not found. If a context is not specified, the `mainContext`
called on a subclass. `nil` is returned if the object is not found.
@param remoteID The remote ID of the object.
@param context The context to use.
@return An existing object with the given remote ID.
*/
+ (instancetype)existingObjectWithRemoteID:(id)remoteID context:(NSManagedObjectContext *)context;

/**
Find an existing object with a given remote ID. The class' entity is used in the find. Therefore, this should only be
called on a subclass. The object will be created if it is not found. The `mainQueueContext` will be used.
The dictionary will be unpacked if `shouldUnpackDictionary:` returns `YES`. The remote ID will be extracted from the
dictionary using `remoteIDDictionaryKey`.
@param dictionary The dictionary to unpack.
@return An existing object with the given dictionary or a new object with the dictionary unpacked.
*/
+ (instancetype)objectWithDictionary:(NSDictionary *)dictionary;

/**
Find an existing object with a given remote ID. The class' entity is used in the find. Therefore, this should only be
called on a subclass. The object will be created if it is not found.
The dictionary will be unpacked if `shouldUnpackDictionary:` returns `YES`. The remote ID will be extracted from the
dictionary using `remoteIDDictionaryKey`.
@param dictionary The dictionary to unpack.
@param context The context to use.
@return An existing object with the given dictionary or a new object with the dictionary unpacked.
*/
+ (instancetype)objectWithDictionary:(NSDictionary *)dictionary context:(NSManagedObjectContext *)context;

/**
Find an existing object with a given remote ID. The class' entity is used in the find. Therefore, this should only be
called on a subclass. `nil` is returned if the object is not found. If a context is not specified, the `mainContext`
will be used.
The dictionary will be unpacked if `shouldUnpackDictionary:` returns `YES`.
The dictionary will be unpacked if `shouldUnpackDictionary:` returns `YES` and there is an object with the given ID.
The remote ID will be extracted from the dictionary using `remoteIDDictionaryKey`.
@param dictionary The dictionary to unpack.
@return An existing object with the given dictionary.
*/
+ (id)objectWithDictionary:(NSDictionary *)dictionary;
+ (id)objectWithDictionary:(NSDictionary *)dictionary context:(NSManagedObjectContext *)context;
+ (instancetype)existingObjectWithDictionary:(NSDictionary *)dictionary;

/**
Find an existing object with a given remote ID. The class' entity is used in the find. Therefore, this should only be
called on a subclass. `nil` is returned if the object is not found. If a context is not specified, the `mainContext`
will be used.
The dictionary will be unpacked if `shouldUnpackDictionary:` returns `YES` and there is an object with the given ID.
The remote ID will be extracted from the dictionary using `remoteIDDictionaryKey`.
@param dictionary The dictionary to unpack.
@param context The context to use.
@return An existing object with the given dictionary.
*/
+ (id)existingObjectWithDictionary:(NSDictionary *)dictionary;
+ (id)existingObjectWithDictionary:(NSDictionary *)dictionary context:(NSManagedObjectContext *)context;
+ (instancetype)existingObjectWithDictionary:(NSDictionary *)dictionary context:(NSManagedObjectContext *)context;


///----------------
/// @name Unpacking
///----------------

/**
Map the attributes of the dictionary onto the properties of the object. The default implementation just unpacks
Expand All @@ -65,8 +159,13 @@
*/
- (BOOL)shouldUnpackDictionary:(NSDictionary *)dictionary;


///----------------
/// @name Utilities
///----------------

/**
Returns `YES` if the `remoteID` property is greater than 0. `NO` is returned if the `remoteID` is `nil` or `0`.
Returns `YES` if the `remoteID` property is not `nil`.
*/
- (BOOL)isRemote;

Expand Down
54 changes: 32 additions & 22 deletions SSDataKit/SSRemoteManagedObject.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,24 @@ @implementation SSRemoteManagedObject
@dynamic createdAt;
@dynamic updatedAt;

#pragma mark -
#pragma mark - Configuration

+ (id)objectWithRemoteID:(NSNumber *)remoteID {
+ (NSString *)remoteIDDictionaryKey {
return @"id";
}


#pragma mark - Find or Create

+ (instancetype)objectWithRemoteID:(id)remoteID {
return [self objectWithRemoteID:remoteID context:nil];
}


+ (id)objectWithRemoteID:(NSNumber *)remoteID context:(NSManagedObjectContext *)context {
+ (instancetype)objectWithRemoteID:(id)remoteID context:(NSManagedObjectContext *)context {

// If there isn't a suitable remoteID, we won't find the object. Return nil.
if (!remoteID ||
![remoteID respondsToSelector:@selector(integerValue)] ||
[remoteID integerValue] == 0) {
if (!remoteID) {
return nil;
}

Expand All @@ -49,17 +54,15 @@ + (id)objectWithRemoteID:(NSNumber *)remoteID context:(NSManagedObjectContext *)
}


+ (id)existingObjectWithRemoteID:(NSNumber *)remoteID {
+ (instancetype)existingObjectWithRemoteID:(id)remoteID {
return [self existingObjectWithRemoteID:remoteID context:nil];
}


+ (id)existingObjectWithRemoteID:(NSNumber *)remoteID context:(NSManagedObjectContext *)context {
+ (instancetype)existingObjectWithRemoteID:(id)remoteID context:(NSManagedObjectContext *)context {

// If there isn't a suitable remoteID, we won't find the object. Return nil.
if (!remoteID ||
![remoteID respondsToSelector:@selector(integerValue)] ||
[remoteID integerValue] == 0) {
if (!remoteID) {
return nil;
}

Expand All @@ -82,20 +85,19 @@ + (id)existingObjectWithRemoteID:(NSNumber *)remoteID context:(NSManagedObjectCo
}


+ (id)objectWithDictionary:(NSDictionary *)dictionary {
+ (instancetype)objectWithDictionary:(NSDictionary *)dictionary {
return [self objectWithDictionary:dictionary context:nil];
}


+ (id)objectWithDictionary:(NSDictionary *)dictionary context:(NSManagedObjectContext *)context {

+ (instancetype)objectWithDictionary:(NSDictionary *)dictionary context:(NSManagedObjectContext *)context {
// Make sure we have a dictionary
if (![dictionary isKindOfClass:[NSDictionary class]]) {
return nil;
}

// Extract the remoteID from the dictionary
NSNumber *remoteID = @([[dictionary objectForKey:@"id"] integerValue]);
id remoteID = [dictionary objectForKey:[self remoteIDDictionaryKey]];

// Find object by remoteID
SSRemoteManagedObject *object = [[self class] objectWithRemoteID:remoteID context:context];
Expand All @@ -110,20 +112,20 @@ + (id)objectWithDictionary:(NSDictionary *)dictionary context:(NSManagedObjectCo
}


+ (id)existingObjectWithDictionary:(NSDictionary *)dictionary {
+ (instancetype)existingObjectWithDictionary:(NSDictionary *)dictionary {
return [self existingObjectWithDictionary:dictionary context:nil];
}


+ (id)existingObjectWithDictionary:(NSDictionary *)dictionary context:(NSManagedObjectContext *)context {
+ (instancetype)existingObjectWithDictionary:(NSDictionary *)dictionary context:(NSManagedObjectContext *)context {

// Make sure we have a dictionary
if (![dictionary isKindOfClass:[NSDictionary class]]) {
return nil;
}

// Extract the remoteID from the dictionary
NSNumber *remoteID = @([[dictionary objectForKey:@"id"] integerValue]);
id remoteID = [dictionary objectForKey:[self remoteIDDictionaryKey]];

// Find object by remoteID
SSRemoteManagedObject *object = [[self class] existingObjectWithRemoteID:remoteID context:context];
Expand All @@ -138,6 +140,8 @@ + (id)existingObjectWithDictionary:(NSDictionary *)dictionary context:(NSManaged
}


#pragma mark - Unpacking

- (void)unpackDictionary:(NSDictionary *)dictionary {
if (!self.isRemote) {
self.remoteID = @([[dictionary objectForKey:@"id"] integerValue]);
Expand Down Expand Up @@ -167,8 +171,10 @@ - (BOOL)shouldUnpackDictionary:(NSDictionary *)dictionary {
}


#pragma mark - Utilities

- (BOOL)isRemote {
return self.remoteID.integerValue > 0;
return self.remoteID != nil;
}


Expand Down Expand Up @@ -243,10 +249,14 @@ + (NSDate *)parseDate:(id)dateStringOrDateNumber {


+ (NSArray *)defaultSortDescriptors {
return [NSArray arrayWithObjects:
if ([self instancesRespondToSelector:@selector(createdAt)]) {
return @[
[NSSortDescriptor sortDescriptorWithKey:@"createdAt" ascending:NO],
[NSSortDescriptor sortDescriptorWithKey:@"remoteID" ascending:NO],
nil];
[NSSortDescriptor sortDescriptorWithKey:@"remoteID" ascending:NO]
];
}

return @[[NSSortDescriptor sortDescriptorWithKey:@"remoteID" ascending:NO]];
}

@end

0 comments on commit 664cb12

Please sign in to comment.