Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename hyper.remoteKey to sync.remoteKey #388

Merged
merged 13 commits into from
Mar 21, 2017
62 changes: 62 additions & 0 deletions Demo.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ If you add the flag `hyper.isPrimaryKey` to the attribute `contractID` then:
- Local primary key will be: `contractID`
- Remote primary key will be: `contract_id`

If you want to use `id` for the remote primary key you also have to add the flag `hyper.remoteKey` and write `id` as the value.
If you want to use `id` for the remote primary key you also have to add the flag `sync.remoteKey` and write `id` as the value.

- Local primary key will be: `articleBody`
- Remote primary key will be: `id`
Expand All @@ -205,7 +205,7 @@ There are some exception to this rule:
* Reserved attributes should be prefixed with the `entityName` (`type` becomes `userType`, `description` becomes `userDescription` and so on). In the JSON they don't need to change, you can keep `type` and `description` for example. A full list of reserved attributes can be found [here](https://github.com/SyncDB/SyncPropertyMapper/blob/master/Sources/NSManagedObject-SyncPropertyMapper/NSManagedObject%2BSyncPropertyMapperHelpers.m#L282-L284)
* Attributes with acronyms will be normalized (`id`, `pdf`, `url`, `png`, `jpg`, `uri`, `json`, `xml`). For example `user_id` will be mapped to `userID` and so on. You can find the entire list of supported acronyms [here](https://github.com/SyncDB/SyncPropertyMapper/blob/master/Sources/NSString-SyncInflections/NSString%2BSyncInflections.m#L204-L206).

If you want to map your Core Data attribute with a JSON attribute that has different naming, you can do by adding `hyper.remoteKey` in the user info box with the value you want to map.
If you want to map your Core Data attribute with a JSON attribute that has different naming, you can do by adding `sync.remoteKey` in the user info box with the value you want to map.

![Custom remote key](https://raw.githubusercontent.com/SyncDB/Sync/master/Images/custom-remote-key-v2.png)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ static NSString * const SyncCustomLocalPrimaryKey = @"hyper.isPrimaryKey";
static NSString * const SyncCustomLocalPrimaryKeyValue = @"YES";
static NSString * const SyncCustomLocalPrimaryKeyAlternativeValue = @"true";

static NSString * const SyncCustomRemoteKey = @"hyper.remoteKey";
static NSString * const SyncCustomRemoteKey = @"sync.remoteKey";
static NSString * const SyncCompatibilityCustomRemoteKey = @"hyper.remoteKey";

@interface NSEntityDescription (SyncPrimaryKey)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#import "NSEntityDescription+SyncPrimaryKey.h"

#import "NSString+SyncInflections.h"
#import "NSPropertyDescription+Sync.h"

@implementation NSEntityDescription (SyncPrimaryKey)

Expand Down Expand Up @@ -35,7 +36,7 @@ - (nonnull NSString *)sync_localPrimaryKey {

- (nonnull NSString *)sync_remotePrimaryKey {
NSAttributeDescription *primaryKeyAttribute = [self sync_primaryKeyAttribute];
NSString *remoteKey = primaryKeyAttribute.userInfo[SyncCustomRemoteKey];
NSString *remoteKey = primaryKeyAttribute.customKey;

if (!remoteKey) {
if ([primaryKeyAttribute.name isEqualToString:SyncDefaultLocalPrimaryKey] || [primaryKeyAttribute.name isEqualToString:SyncDefaultLocalCompatiblePrimaryKey]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

static NSString * const SyncPropertyMapperDestroyKey = @"destroy";
static NSString * const SyncPropertyMapperCustomValueTransformerKey = @"hyper.valueTransformer";
static NSString * const SyncPropertyMapperCustomRemoteKey = @"hyper.remoteKey";
static NSString * const SyncPropertyMapperNonExportableKey = @"hyper.nonExportable";

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#import "NSString+SyncInflections.h"
#import "NSEntityDescription+SyncPrimaryKey.h"
#import "NSDate+SyncPropertyMapper.h"
#import "NSPropertyDescription+Sync.h"

@implementation NSManagedObject (SyncPropertyMapperHelpers)

Expand Down Expand Up @@ -43,8 +44,8 @@ - (NSAttributeDescription *)attributeDescriptionForRemoteKey:(NSString *)remoteK
if ([propertyDescription isKindOfClass:[NSAttributeDescription class]]) {
NSAttributeDescription *attributeDescription = (NSAttributeDescription *)propertyDescription;

NSDictionary *userInfo = [self.entity.propertiesByName[attributeDescription.name] userInfo];
NSString *customRemoteKey = userInfo[SyncPropertyMapperCustomRemoteKey];
NSString *customRemoteKey = [self.entity.propertiesByName[attributeDescription.name] customKey];

BOOL currentAttributeHasTheSameRemoteKey = (customRemoteKey.length > 0 && [customRemoteKey isEqualToString:remoteKey]);
if (currentAttributeHasTheSameRemoteKey) {
foundAttributeDescription = attributeDescription;
Expand Down Expand Up @@ -104,8 +105,7 @@ - (NSArray *)attributeDescriptionsForRemoteKeyPath:(NSString *)remoteKey {
if ([propertyDescription isKindOfClass:[NSAttributeDescription class]]) {
NSAttributeDescription *attributeDescription = (NSAttributeDescription *)propertyDescription;

NSDictionary *userInfo = [self.entity.propertiesByName[attributeDescription.name] userInfo];
NSString *customRemoteKeyPath = userInfo[SyncPropertyMapperCustomRemoteKey];
NSString *customRemoteKeyPath = self.entity.propertiesByName[attributeDescription.name].customKey;
NSString *customRootRemoteKey = [[customRemoteKeyPath componentsSeparatedByString:@"."] firstObject];
NSString *rootRemoteKey = [[remoteKey componentsSeparatedByString:@"."] firstObject];
BOOL currentAttributeHasTheSameRootRemoteKey = (customRootRemoteKey.length > 0 && [customRootRemoteKey isEqualToString:rootRemoteKey]);
Expand Down Expand Up @@ -135,11 +135,10 @@ - (NSString *)remoteKeyForAttributeDescription:(NSAttributeDescription *)attribu
- (NSString *)remoteKeyForAttributeDescription:(NSAttributeDescription *)attributeDescription
usingRelationshipType:(SyncPropertyMapperRelationshipType)relationshipType
inflectionType:(SyncPropertyMapperInflectionType)inflectionType {
NSDictionary *userInfo = attributeDescription.userInfo;
NSString *localKey = attributeDescription.name;
NSString *remoteKey;

NSString *customRemoteKey = userInfo[SyncPropertyMapperCustomRemoteKey];
NSString *customRemoteKey = attributeDescription.customKey;
if (customRemoteKey) {
remoteKey = customRemoteKey;
} else if ([localKey isEqualToString:SyncDefaultLocalPrimaryKey] || [localKey isEqualToString:SyncDefaultLocalCompatiblePrimaryKey]) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@import CoreData;

@interface NSPropertyDescription (Sync)

@property (nonatomic, nullable, readonly) NSString *customKey;

@end
16 changes: 16 additions & 0 deletions Source/NSPropertyDescription-Sync/NSPropertyDescription+Sync.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#import "NSPropertyDescription+Sync.h"

#import "NSEntityDescription+SyncPrimaryKey.h"

@implementation NSPropertyDescription (Sync)

- (NSString *)customKey {
NSString *keyName = self.userInfo[SyncCustomRemoteKey];
if (keyName == nil) {
keyName = self.userInfo[SyncCompatibilityCustomRemoteKey];
}

return keyName;
}

@end
1 change: 1 addition & 0 deletions Source/Sync.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ FOUNDATION_EXPORT const unsigned char SyncVersionString[];
#import "SyncPropertyMapper.h"
#import "NSEntityDescription+SyncPrimaryKey.h"
#import "NSManagedObject+SyncPropertyMapperHelpers.h"
#import "NSPropertyDescription+Sync.h"
14 changes: 14 additions & 0 deletions Source/Sync/NSEntityDescription+Sync.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,20 @@ extension NSEntityDescription {
return relationships
}

/// Finds the attributes for the current entity.
///
/// - Returns: An array of attributes for the current entity.
func sync_attributes() -> [NSAttributeDescription] {
var attributes = [NSAttributeDescription]()
for propertyDescription in properties {
if let attributeDescription = propertyDescription as? NSAttributeDescription {
attributes.append(attributeDescription)
}
}

return attributes
}

/**
Finds the parent for the current entity, if there are many parents nil will be returned.
- returns The parent relationship for the current entity
Expand Down
8 changes: 4 additions & 4 deletions Source/Sync/NSManagedObject+Sync.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ extension NSManagedObject {
for relationship in entity.sync_relationships() {
let suffix = relationship.isToMany ? "_ids" : "_id"
let constructedKeyName = relationship.name.hyp_snakeCase() + suffix
let keyName = relationship.userInfo?[SyncCustomRemoteKey] as? String ?? constructedKeyName
let keyName = relationship.customKey ?? constructedKeyName

if relationship.isToMany {
if let localPrimaryKey = dictionary[keyName], localPrimaryKey is Array < String> || localPrimaryKey is Array < Int> || localPrimaryKey is NSNull {
Expand Down Expand Up @@ -169,15 +169,15 @@ extension NSManagedObject {
*/
func sync_toManyRelationship(_ relationship: NSRelationshipDescription, dictionary: [String: Any], parent: NSManagedObject?, parentRelationship: NSRelationshipDescription?, context: NSManagedObjectContext, operations: Sync.OperationOptions, shouldContinueBlock: (() -> Bool)?, objectJSONBlock: ((_ objectJSON: [String: Any]) -> [String: Any])?) throws {
var children: [[String: Any]]?
let childrenIsNull = relationship.userInfo?[SyncCustomRemoteKey] is NSNull || dictionary[relationship.name.hyp_snakeCase()] is NSNull || dictionary[relationship.name] is NSNull
let childrenIsNull = (relationship.customKey as Any?) is NSNull || dictionary[relationship.name.hyp_snakeCase()] is NSNull || dictionary[relationship.name] is NSNull
if childrenIsNull {
children = [[String: Any]]()

if value(forKey: relationship.name) != nil {
setValue(nil, forKey: relationship.name)
}
} else {
if let customRelationshipName = relationship.userInfo?[SyncCustomRemoteKey] as? String {
if let customRelationshipName = relationship.customKey {
if customRelationshipName.contains(".") {
if let deepMapingRootKey = customRelationshipName.components(separatedBy: ".").first {
if let rootObject = dictionary[deepMapingRootKey] as? [String: Any] {
Expand Down Expand Up @@ -352,7 +352,7 @@ extension NSManagedObject {
func sync_toOneRelationship(_ relationship: NSRelationshipDescription, dictionary: [String: Any], context: NSManagedObjectContext, operations: Sync.OperationOptions, shouldContinueBlock: (() -> Bool)?, objectJSONBlock: ((_ objectJSON: [String: Any]) -> [String: Any])?) {
var filteredObjectDictionary: [String: Any]?

if let customRelationshipName = relationship.userInfo?[SyncCustomRemoteKey] as? String {
if let customRelationshipName = relationship.customKey {
filteredObjectDictionary = dictionary[customRelationshipName] as? [String: Any]
} else if let result = dictionary[relationship.name.hyp_snakeCase()] as? [String: Any] {
filteredObjectDictionary = result
Expand Down
2 changes: 1 addition & 1 deletion Source/Sync/Sync.swift
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ public protocol SyncDelegate: class {
}

if remotePrimaryKey.isEmpty {
fatalError("Remote primary key not found for entity: \(entityName), we were looking for id, if your remote ID has a different name consider using hyper.remoteKey to map to the right value")
fatalError("Remote primary key not found for entity: \(entityName), we were looking for id, if your remote ID has a different name consider using sync.remoteKey to map to the right value")
}

let dataFilterOperations = DataFilter.Operation(rawValue: operations.rawValue)
Expand Down
10 changes: 8 additions & 2 deletions Tests/NSEntityDescription-SyncPrimaryKey/PrimaryKeyTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ - (void)testPrimaryKeyAttribute {
XCTAssertEqualObjects(attribute.name, @"alternativeID");
}

- (void)testLocalKey {
- (void)testLocalPrimaryKey {
NSEntityDescription *entity = [self entityForName:@"User"];
XCTAssertEqualObjects([entity sync_localPrimaryKey], @"remoteID");

Expand All @@ -74,9 +74,12 @@ - (void)testLocalKey {

entity = [self entityForName:@"AlternativeID"];
XCTAssertEqualObjects([entity sync_localPrimaryKey], @"alternativeID");

entity = [self entityForName:@"Compatibility"];
XCTAssertEqualObjects([entity sync_localPrimaryKey], @"id");
}

- (void)testRemoteKey {
- (void)testRemotePrimaryKey {
NSEntityDescription *entity = [self entityForName:@"User"];
XCTAssertEqualObjects([entity sync_remotePrimaryKey], @"id");

Expand All @@ -94,6 +97,9 @@ - (void)testRemoteKey {

entity = [self entityForName:@"AlternativeID"];
XCTAssertEqualObjects([entity sync_remotePrimaryKey], @"alternative_id");

entity = [self entityForName:@"Compatibility"];
XCTAssertEqualObjects([entity sync_remotePrimaryKey], @"greeting");
}

@end
18 changes: 13 additions & 5 deletions ...escription-SyncPrimaryKey/SyncPrimaryKey.xcdatamodeld/SyncPrimaryKey.xcdatamodel/contents
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="10174" systemVersion="15E65" minimumToolsVersion="Automatic">
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="11759" systemVersion="16D32" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
<entity name="AlternativeID" syncable="YES">
<attribute name="alternativeID" optional="YES" attributeType="String" syncable="YES">
<userInfo>
<entry key="hyper.isPrimaryKey" value="true"/>
</userInfo>
</attribute>
</entity>
<entity name="Compatibility" syncable="YES">
<attribute name="id" optional="YES" attributeType="String" syncable="YES">
<userInfo>
<entry key="hyper.remoteKey" value="greeting"/>
</userInfo>
</attribute>
</entity>
<entity name="NoID" syncable="YES">
<attribute name="attribute" optional="YES" attributeType="String" syncable="YES"/>
</entity>
<entity name="Note" syncable="YES">
<attribute name="attribute" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="uniqueID" optional="YES" attributeType="Integer 16" defaultValueString="0" syncable="YES">
<attribute name="uniqueID" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" syncable="YES">
<userInfo>
<entry key="hyper.isPrimaryKey" value="YES"/>
</userInfo>
Expand All @@ -27,20 +34,21 @@
<attribute name="randomId" optional="YES" attributeType="String" syncable="YES">
<userInfo>
<entry key="hyper.isPrimaryKey" value="YES"/>
<entry key="hyper.remoteKey" value="id"/>
<entry key="sync.remoteKey" value="id"/>
</userInfo>
</attribute>
</entity>
<entity name="User" representedClassName="" syncable="YES">
<attribute name="attribute" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="remoteID" attributeType="Integer 32" defaultValueString="0" syncable="YES"/>
<attribute name="remoteID" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
</entity>
<elements>
<element name="AlternativeID" positionX="-27" positionY="18" width="128" height="60"/>
<element name="NoID" positionX="-27" positionY="18" width="128" height="60"/>
<element name="Note" positionX="-20" positionY="18" width="128" height="75"/>
<element name="SimpleID" positionX="-27" positionY="18" width="128" height="75"/>
<element name="Tag" positionX="160" positionY="0" width="128" height="75"/>
<element name="User" positionX="-207" positionY="-15" width="128" height="73"/>
<element name="AlternativeID" positionX="-27" positionY="18" width="128" height="60"/>
<element name="Compatibility" positionX="-27" positionY="18" width="128" height="60"/>
</elements>
</model>
14 changes: 14 additions & 0 deletions Tests/Sync/JSONs/388.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"id": 0,
"name": "Note 0"
},
{
"id": 1,
"name": "Note 1"
},
{
"id": 2,
"name": "Note 2"
}
]
7 changes: 7 additions & 0 deletions Tests/Sync/JSONs/hyper_remote_key.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
{
"id": 1,
"custom_old": "old",
"custom_current": "current"
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<relationship name="awesomeComments" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="AwesomeComment" inverseName="awesomeStory" inverseEntity="AwesomeComment" syncable="YES"/>
<relationship name="awesomeSummarize" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="AwesomeSummarize" inverseName="awesomeStory" inverseEntity="AwesomeSummarize" syncable="YES">
<userInfo>
<entry key="hyper.remoteKey" value="summarize_text"/>
<entry key="sync.remoteKey" value="summarize_text"/>
</userInfo>
</relationship>
</entity>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<attribute name="noteID" attributeType="Integer 32" defaultValueString="0" syncable="YES">
<userInfo>
<entry key="hyper.isPrimaryKey" value="true"/>
<entry key="hyper.remoteKey" value="id"/>
<entry key="sync.remoteKey" value="id"/>
</userInfo>
</attribute>
<relationship name="tags" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Tag" inverseName="notes" inverseEntity="Tag" syncable="YES"/>
Expand All @@ -15,7 +15,7 @@
<attribute name="tagID" optional="YES" attributeType="String" syncable="YES">
<userInfo>
<entry key="hyper.isPrimaryKey" value="true"/>
<entry key="hyper.remoteKey" value="id"/>
<entry key="sync.remoteKey" value="id"/>
</userInfo>
</attribute>
<relationship name="notes" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Note" inverseName="tags" inverseEntity="Note" syncable="YES"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<attribute name="noteID" attributeType="Integer 32" defaultValueString="0" syncable="YES">
<userInfo>
<entry key="hyper.isPrimaryKey" value="YES"/>
<entry key="hyper.remoteKey" value="id"/>
<entry key="sync.remoteKey" value="id"/>
</userInfo>
</attribute>
<relationship name="user" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="User" inverseName="notes" inverseEntity="User" syncable="YES"/>
Expand All @@ -15,7 +15,7 @@
<attribute name="userID" attributeType="Integer 32" defaultValueString="0" syncable="YES">
<userInfo>
<entry key="hyper.isPrimaryKey" value="YES"/>
<entry key="hyper.remoteKey" value="id"/>
<entry key="sync.remoteKey" value="id"/>
</userInfo>
</attribute>
<relationship name="notes" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="Note" inverseName="user" inverseEntity="Note" syncable="YES"/>
Expand Down
4 changes: 2 additions & 2 deletions Tests/Sync/Models/157.xcdatamodeld/157.xcdatamodel/contents
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<attribute name="cityID" attributeType="Integer 16" defaultValueString="0" syncable="YES">
<userInfo>
<entry key="hyper.isPrimaryKey" value="YES"/>
<entry key="hyper.remoteKey" value="id"/>
<entry key="sync.remoteKey" value="id"/>
</userInfo>
</attribute>
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
Expand All @@ -14,7 +14,7 @@
<attribute name="locationID" attributeType="Integer 16" defaultValueString="0" syncable="YES">
<userInfo>
<entry key="hyper.isPrimaryKey" value="YES"/>
<entry key="hyper.remoteKey" value="id"/>
<entry key="sync.remoteKey" value="id"/>
</userInfo>
</attribute>
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
Expand Down
Loading