I'm finding the documentation on the new codegen feature in the Core Data Editor in Xcode 8 a bit sparse.
This is a "in Objective-C, I would...." kind of question.
I'm trying to declare a protocol that has 2 methods:
@property (strong, readonly) NSNumber *serverId;
+ (instancetype)objectWithServerId:(NSNumber*)serverId inContext:(NSManagedObjectContext*)moc;
In Objective-C, I would use mogenerator to declare that the baseclass generated should be "MyBaseClass".
and in that baseclass I can implement that class method once. In the Core Data editor, I just have to make sure my entity has that attribute. In the 'human readable' file, I would declare that it conforms to that protocol, and because it inherits from that baseclass (which is basically abstract), it can call that class method listed above.
I think with strong typing, this may not be possible. I have made it work, but each subclass I create (which uses the Xcode generated Extensions) has to implement this method, whereas I would prefer to write the method once as a generic.
In Swift, I added that attribute to the entity (no parent, therefore it is a subclass from NSManagedObject), and did this:
protocol RemoteObjectProtocol {
var serverId: Int64 {get}
static func object<T: NSManagedObject>(WithServerId serverId: Int64, context: NSManagedObjectContext!) -> T?
}
import CoreData
@objc(TestObject)
class TestObject: NSManagedObject {
}
extension TestObject: RemoteObjectProtocol {
// by nature of it being in the generated core data model, we just need to declare its conformance.
static func object<T: NSManagedObject>(WithServerId serverId: Int64, context: NSManagedObjectContext!) -> T? {
// IF YOU FIND A BETTER WAY TO SOLVE THIS, PLEASE DO!
// I would have loved to use a baseclass RemoteObject: NSManagedObject, where you can just implement this
// Method. But there was no way for it to play nicely with the CoreData Editor
// in addition, the convenience method generated by Xcode .fetchRequest() only seems to work outside of this extension.
// Extensions can't make use of other extensions
// So we create the fetch request 'by hand'
let fetchRequest = NSFetchRequest<TestObject>(entityName: "TestObject")
fetchRequest.predicate = NSPredicate(format: "serverId == %i", serverId)
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "serverId", ascending: true)]
fetchRequest.fetchLimit = 1
do {
let fetchedObjects = try context.fetch(fetchRequest)
return fetchedObjects.first as! T?
} catch {
log.warning("Failed to fetch object with serverId:\(serverId) error:\(error)")
}
return nil
}
}
Codegen classes can conform to protocols. And, Swift protocols can provide a default implementation of any functions.
So, in theory at least, it should be simpler to achieve what you're doing in Swift than in Objective-C. But, getting the syntax right can be a bit tricky.
The main issues are the following:
@objc
so that it can play with CoreData and NSManagedObjectSo, for the protocol, the following code should do it:
import CoreData
@objc protocol RemoteObject {
var serverId: Int64 { get }
}
extension RemoteObject where Self: NSManagedObject {
static func objectWithServerId(_ serverId: Int64, context: NSManagedObjectContext) -> Self? {
let fetchRequest: NSFetchRequest<Self> = NSFetchRequest(entityName: Self.entity().name!)
let predicate = NSPredicate(format: "serverId == %d", serverId)
let descriptor = NSSortDescriptor(key: #keyPath(RemoteObject.serverId), ascending: true)
fetchRequest.predicate = predicate
fetchRequest.sortDescriptors = [descriptor]
fetchRequest.fetchLimit = 1
do {
let results = try context.fetch(fetchRequest)
return results.first
} catch {
print("Failed to fetch object with serverId:\(serverId) error:\(error)")
}
return nil
}
}
And, as you noted, every entity already has the serverId
attribute. So, you need only declare that it conforms to the protocol:
extension MyCoreDataEntity: RemoteObject {
}
Note that for some reason the compiler rejects the somewhat simpler line:
let fetchRequest: NSFetchRequest<Self> = Self.fetchRequest()
That simpler line generates the following error: Cannot convert value of type 'NSFetchRequest<NSFetchRequestResult>' to specified type 'NSFetchRequest<Self>'
. But, for some reason, the above workaround doesn't.
Any ideas on why this occurs are very welcome.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With