I've got these models:
extension Notification {
@NSManaged var pk: NSNumber?
@NSManaged var subtitle: String?
@NSManaged var title: String?
@NSManaged var user: User?
}
extension User {
// here's some attributes
@NSManaged var pk: NSNumber?
@NSManaged var notifications: NSSet?
// and here's really some more..
}
A response for listing a user's notification could look like:
[
{
"pk": 2,
"title": "Hi mate",
"subtitle": "O hoy",
"user": 2
}
]
"user": 2
means that it is belong to the user with pk = 2.
How do I properly map this notification to the user? My attempt is:
let mapping = RKEntityMapping(forEntityForName: String(Notification), inManagedObjectStore: RKObjectManager.sharedManager().managedObjectStore)
mapping.identificationAttributes = [ "pk" ]
mapping.addAttributeMappingsFromDictionary([
"pk": "pk",
"title": "title",
"subtitle": "subtitle",
])
mapping.addConnectionForRelationship("user", connectedBy: [
"user.pk": "pk"
])
I can do
mapping.addConnectionForRelationship("user", connectedBy: [
"pk"
])
but then it connects it to a user with the same pk as the Notification
.
The error I get is:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Cannot connect relationship: invalid attributes given for source entity 'Notification': user.pk'
How can I achieve this?
Am I forced to add a transient property users in the notification and then map userPk to pk?
EDIT: I've got it working using transient properties now, and that's the only way?
mapping.identificationAttributes = [ "pk" ]
mapping.addAttributeMappingsFromDictionary([
"pk": "pk",
"title": "title",
"subtitle": "subtitle",
"user": "userPk"
])
mapping.addConnectionForRelationship("user", connectedBy: [
"userPk": "pk"
])
userPk is now a transient property on Notification
As long as you only get the ID of the user, the yes, you need to be using the method addConnectionForRelationship
and a transient property. However, if your backend would serve you the user object rather than the ID, you can use addPropertyMapping
method on the RKEntityMapping
. Then I would suggest a pattern similar to this:
import Foundation
import CoreData
import RestKit
protocol Mappable {
static var entityName: String {get}
static var identityAttribute: String? {get}
static func mapping(managedObjectStore: RKManagedObjectStore) -> RKEntityMapping
static func identificationAttributes() -> [String]?
static func createPropertyMappings(managedObjectStore: RKManagedObjectStore, objectMapping: RKObjectMapping) -> [RKRelationshipMapping]?
static func attributesDictionary() -> Dictionary<String, String>?
static func requestDescriptor(managedObjectStore: RKManagedObjectStore) -> RKRequestDescriptor
}
class ManagedObject: NSManagedObject, Mappable {
//MARK: - ManagedObjectProtocol Methods
class func mapping(managedObjectStore: RKManagedObjectStore) -> RKEntityMapping {
let mapping = RKEntityMapping(
forEntityForName: entityName,
inManagedObjectStore: managedObjectStore
)
if let identityAttribute = identityAttribute {
mapping.identificationAttributes = [identityAttribute]
}
if let attributeMapping = attributes {
mapping.addAttributeMappingsFromDictionary(attributeMapping)
}
if let propertyMappings = createPropertyMappings(managedObjectStore, objectMapping: mapping) {
for propertyMapping in propertyMappings {
mapping.addPropertyMapping(propertyMapping)
}
}
return mapping
}
class var entityName: String {
fatalError("must override")
}
class var identityAttribute: String? {
//override me
return nil
}
class func attributesDictionary() -> Dictionary<String, String>? {
//override me
return nil
}
class func createPropertyMappings(managedObjectStore: RKManagedObjectStore, objectMapping: RKObjectMapping) -> [RKRelationshipMapping]? {
//override me if needed
return nil
}
class func requestDescriptor(managedObjectStore: RKManagedObjectStore) -> RKRequestDescriptor {
let inversedMapping = mapping(managedObjectStore).inverseMapping()
let descriptor = RKRequestDescriptor(mapping: inversedMapping, objectClass: self, rootKeyPath: nil, method: RKRequestMethod.Any)
return descriptor
}
}
class Notification: ManagedObject {
//MARK: - ManagedObjectProtocol Methods
class var entityName: String {
return "Notification"
}
override class func attributesDictionary() -> Dictionary<String, String>? {
let dictionary = [
"pk": "pk",
"title": "title",
"subtitle": "subtitle"
]
return dictionary
}
override class func createPropertyMappings(managedObjectStore: RKManagedObjectStore, objectMapping: RKObjectMapping) -> [RKRelationshipMapping]? {
let bookmarksPropertyMapping = RKRelationshipMapping(
fromKeyPath: "user",
toKeyPath: "user",
withMapping: User.mapping(managedObjectStore)
)
return [bookmarksPropertyMapping]
}
}
class User: ManagedObject {
//MARK: - ManagedObjectProtocol Methods
class var entityName: String {
return "User"
}
override class func attributesDictionary() -> Dictionary<String, String>? {
let dictionary = [
"pk": "pk"
]
return dictionary
}
}