How can an NSManagedObject conform to the NSItemProviderReading protocol? - swift

After creating an NSManagedObject subclass of a Core Data entity, how can I make it conform properly to the NSItemProviderReading protocol? The protocol has a required initializer that must be declared directly in the class. But which designated initializer should NSItemProviderReading's init(itemProviderData:, typeIdentifier:) call?
This is what I have below:
import Foundation
import CoreData
#objc(Something)
public class Something: NSManagedObject, NSItemProviderReading {
public override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) {
super.init(entity: entity, insertInto: context)
}
// MARK: - Item Provider Reading
public static var readableTypeIdentifiersForItemProvider: [String] {
return []
}
public required init(itemProviderData data: Data, typeIdentifier: String) throws {
// This seems very hack-y…
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
self.init(context: context)
}
}
Is calling self.init(context:) really the right way to go here?

To conform to NSItemProviderReading protocol requires conformance to the init you mentioned and also the static var readableTypeIdentifiersForItemProvider:
init(itemProviderData: Data, typeIdentifier: String)
static var readableTypeIdentifiersForItemProvider: [String]
The documentation indicates this with the Required note.
I don't see anything wrong with your init so if it compiles and works with how you plan on using the class I don't see an issue. What I would recommend is using dependency injection to pass Core Data context throughout your app, to the view controllers that need them. That way you don't have to do the annoying AppDelegate code every time (and it's safer because you can be sure the context is always available).

Related

Prevent Usage of generated Initializer in CoreData

I want to prevent the use of the standard Xcode generated Initializer for a CoreData ManagedObject, because I want to wire some things up myself.
My plan was to write a Factory or a special Initializer.
I tried to make the Xcode generated Initializer private in an extension and provide another Initializer, that uses this private.
Unfortunately that doesn't compile.
extension Item {
private init(context : NSManagedObjectContext?){ // NOT ALLOWED !!
self.init(context: context)
}
static func ItemFactory(i: Int)-> Item{
let moc = PersistenceController.moc
let item = Item(context: moc)
// do some Initialisation
item.i = I
return item
}
}
How can this be achieved?

How to init with a generic NSFetchedResultsController?

Wondering how to init a class that contains a fetched results controller?
The compiler says that T is an undeclared type. I have a couple of dynamic tables that draw their data from a fetchedResultsController. If i could make the inject frc generic that would eliminate a bunch of duplicate code and make things more maintainable.
class DynamicTableData: NSObject {
let frc: NSFetchedResultsController<T>
init(frc: NSFetchedResultsController<T>) {
self.frc = frc
super.init()
}
}
Try this. Your your generic type should conform to NSFetchRequestResult
class DynamicTableData<T:NSFetchRequestResult>: NSObject {
let frc: NSFetchedResultsController<T>
init(frc: NSFetchedResultsController<T>) {
self.frc = frc
super.init()
}
}

How to return instance of subclass with method declared in superclass? [duplicate]

This question already has answers here:
How can I create instances of managed object subclasses in a NSManagedObject Swift extension?
(3 answers)
Closed 4 years ago.
In objc I have created extension of NSManagedObject for creating objects. Now all subclasses can use it to create objects of its own type. I have tried something similar in swift, but quite as I would like, by using generics.
extension NSManagedObject {
// MARK: - Creation Methods
class func create<T: NSManagedObject>(in context: NSManagedObjectContext) -> T {
let entity = NSEntityDescription.entity(forEntityName: T.description(), in: context)!
return T(entity: entity, insertInto: context)
}
}
While in Person class which is subclass of NSManagedObject, I can call it like:
let person = Person.create(context)
person will be NSManagedObject type, and I will have to cast it to Person, to access properties like name, phone etc...
I would like to somehow avoid this and make method create return instancetype of sorts, but I am unsure how to do this?
Rather than an extension of NSManagedObject I'd prefer a protocol extension
protocol Managed
{
associatedtype ManagedType: NSManagedObject = Self
static var entityName : String { get }
static func create(in context: NSManagedObjectContext) -> ManagedType
}
extension Managed where Self : NSManagedObject
{
static var entityName : String {
return NSStringFromClass(self).components(separatedBy: ".").last!
}
static func create(in context: NSManagedObjectContext) -> ManagedType
{
return NSEntityDescription.insertNewObject(forEntityName: entityName, into: context) as! ManagedType
}
}
Make all NSManagedObject subclasses adopt the protocol Managed

Can I implement default initialization in protocol in Swift

I have a variety of classes that all conform to a single protocol and share the same initialization method. Is there a way to implement the initialization in the protocol? So I don't have to copy the code in every class. This is what I have so far
protocol someProtocol {
init(data: Data)
}
class ObjectA: someProtocol {
let data: Data
required init(data: Data) {
self.data = data
}
}
class ObjectB: someProtocol {
let data: Data
required init(data: Data) {
self.data = data
}
}
You can’t do this as the protocol and protocol extension have no knowledge about the properties in the objects that conform to them and so you cannot initialise all the story properties.
I’m sure there are other runtime reason about type inference too but this one is probably the most simple to explain.

How to use generic types to get object with same type

I have extension for NSManagedObject that should help me to transfer objects between contexts:
extension NSManagedObject {
func transferTo(#context: NSManagedObjectContext) -> NSManagedObject? {
return context.objectWithID(objectID)
}
}
for now it return object of NSManagedObject and i should cast it to class what i want, like this:
let someEntity: MyEntity = // ...create someEntity
let entity: MyEntity = someEntity.transferTo(context: newContext) as? MyEntity
Is there a way in Swift to avoid that useless casting and if i call transferTo(context: ...) from object of class MyEntity make it return type to MyEntity?
I've liked Martin's solution for a long time, but I recently ran into trouble with it. If the object has been KVO observed, then this will crash. Self in that case is the KVO subclass, and the result of objectWithID is not that subclass, so you'll get a crash like "Could not cast value of type 'myapp.Thing' (0xdeadbeef) to 'myapp.Thing' (0xfdfdfdfd)." There are two classes that call themselves myapp.Thing, and as! uses the actual class object. So Swift is not fooled by the noble lies of KVO classes.
The solution is to replace Self with a static type parameter by moving this to the context:
extension NSManagedObjectContext {
func transferredObject<T: NSManagedObject>(object: T) -> T {
return objectWithID(object.objectID) as! T
}
}
T is purely defined at compile-time, so this works even if object is secretly a subclass of T.
Update: For a better solution, see Rob's answer.
Similarly as in How can I create instances of managed object subclasses in a NSManagedObject Swift extension?,
this can be done with a generic helper method:
extension NSManagedObject {
func transferTo(context context: NSManagedObjectContext) -> Self {
return transferToHelper(context: context)
}
private func transferToHelper<T>(context context: NSManagedObjectContext) -> T {
return context.objectWithID(objectID) as! T
}
}
Note that I have changed the return type to Self.
objectWithID() does not return an optional
(in contrast to objectRegisteredForID(), so there is no need to
return an optional here.
Update: Jean-Philippe Pellet's suggested
to define a global reusable function instead of the helper method
to cast the return value to the appropriate type.
I would suggest to define two (overloaded) versions, to make this
work with both optional and non-optional objects (without an unwanted
automatic wrapping into an optional):
func objcast<T>(obj: AnyObject) -> T {
return obj as! T
}
func objcast<T>(obj: AnyObject?) -> T? {
return obj as! T?
}
extension NSManagedObject {
func transferTo(context context: NSManagedObjectContext) -> Self {
let result = context.objectWithID(objectID) // NSManagedObject
return objcast(result) // Self
}
func transferUsingRegisteredID(context context: NSManagedObjectContext) -> Self? {
let result = context.objectRegisteredForID(objectID) // NSManagedObject?
return objcast(result) // Self?
}
}
(I have updated the code for Swift 2/Xcode 7. The code for earlier
Swift versions can be found in the edit history.)
This will do the trick:
func transferTo(#context: NSManagedObjectContext) -> Self?
At call site, Self resolves to the statically known type of the object you're calling this method on. This is also especially handy to use in protocols when you don't know the final type that will conform to the protocol but still want to reference it.
Update: Martin R's answer points out that you can't cast the obtained object right away. I'd then do something like this:
// top-level utility function
func cast<T>(obj: Any?, type: T.Type) -> T? {
return obj as? T
}
extension NSManagedObject {
func transferTo(#context: NSManagedObjectContext) -> NSManagedObject? {
return cast(context.objectWithID(objectID), self.dynamicType)
}
}