Search code examples
swiftgenericsswift-extensions

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?


Solution

  • 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.)