0

I have a simple Swift extension on NSManagedObject, in which I have a parametrized method for finding a single object - the signature looks like:

public class func findFirst<T:NSManagedObject>(inContext context : NSManagedObjectContext? = .None) -> T?

I'm trying to call this from Objective-C, but it seems like it cannot be seen. If I create a non-parameterized version I can see and call it just fine from Objective-C:

public class func findFirstUntypedWithPredicate(predicate:NSPredicate?, inContext context : NSManagedObjectContext? = .None) -> NSManagedObject?

Is there any way for ObjectiveC to be able to reach the parameterized version of the call?

I would use Self like so:

public class func findFirst(inContext context : NSManagedObjectContext? = .None) -> Self?

using the technique found here:

How can I create instances of managed object subclasses in a NSManagedObject Swift extension?

However, that causes the Swift compiler to segfault when compiling the code (Xcode 6.3.1, or Xcode 6.4 beta 2).

Edit: Here's a link with the full source of the framework I'm trying to build, including bonus Swift compiler crashes caused by templated methods:

https://www.dropbox.com/s/fixaj9ygdoi4arp/KiGiCoreData.zip?dl=0

Community
  • 1
  • 1
Kendall Helmstetter Gelner
  • 74,769
  • 26
  • 128
  • 150
  • Generics can *not* be used from Objective-C, see "Using Swift from Objective-C" in https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-XID_78. – The second part of your question is unclear to me. Does the referenced code cause a compiler crash or your code which is based on that? Can you give an example demonstrating the problem? – Martin R May 01 '15 at 19:17
  • The referenced code based on Self causes a compiler crash, yes. Very sad as I think that would work exactly as I wanted. I'll upload a sample project later after I've run a few more tests, and make sure the latest XCode beta does not fix this issue. – Kendall Helmstetter Gelner May 01 '15 at 20:17
  • I have checked my code from http://stackoverflow.com/a/27112385/1187415 again. It compiles and works as expected. I have added a `findFirst()` variant as an answer, hope that is what you are looking for. – Martin R May 01 '15 at 20:22

1 Answers1

1

Generic methods are not visible from Objective-C. However you can use the ideas from How to use generic types to get object with same type to define a findFirst() class method which returns Self? (the Swift equivalent of instancetype) without being generic:

// Used to cast `AnyObject?` to `Self?`, `T` is inferred from the context.
func objcast<T>(obj: AnyObject?) -> T? {
    return obj as! T?
}

extension NSManagedObject
{
    class func entityName() -> String {
        let classString = NSStringFromClass(self)
        // The entity is the last component of dot-separated class name:
        let components = split(classString) { $0 == "." }
        return components.last ?? classString
    }

    // Return any matching object, or `nil` if none exists or an error occurred
    class func findFirst(context : NSManagedObjectContext, withPredicate pred : NSPredicate?) -> Self? {
        let name = entityName()
        let request = NSFetchRequest(entityName: name)
        request.predicate = pred
        var error : NSError?
        let result = context.executeFetchRequest(request, error: &error)
        if let objects = result  {
            return objcast(objects.first)
        } else {
            println("Fetch failed: \(error?.localizedDescription)")
            return nil
        }
    }
}

This can be used from Swift

if let obj = YourEntity.findFirst(context, withPredicate: nil) {
    // found
} else {
    // not found
}

and from Objective-C:

YourEntity *obj = [YourEntity findFirst:context withPredicate:nil];
Community
  • 1
  • 1
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Self is great, but as I explained I cannot use Self because it cause the Swift compiler to segfault (So I cannot even compile the code). – Kendall Helmstetter Gelner May 02 '15 at 19:55
  • @KendallHelmstetterGelner: Strange, that code *does* compile in my Xcode 6.3.1. – Martin R May 02 '15 at 19:57
  • The code probably compiles in most circumstances. In the case of my specific class it segfaults he compiler using any form I can think of to try. – Kendall Helmstetter Gelner May 02 '15 at 22:19
  • @KendallHelmstetterGelner: Are you using Xcode 6.3.1? Could you provide a (minimal) project demonstrating the problem? – Martin R May 02 '15 at 22:30
  • Will put one up after I verify it's still a problem with the latest Beta XCode. – Kendall Helmstetter Gelner May 04 '15 at 04:35
  • Added a link to sample code at the end of the question... give that a try. It does still crash in the latest Xcode beta, going to submit a radar on this also. – Kendall Helmstetter Gelner May 04 '15 at 14:49
  • @KendallHelmstetterGelner: The main culprit is `findAllWithPredicate` which is still a generic method. It seems that defining *any* generic extension method for an Objective-C class causes a compiler crash. Simple example: `extension NSObject { func foo() { } }`. Unfortunately, something like `func findAllWithPredicate(...) -> [Self]` does not compile. – If you remove this method (and its dependents) then only two minor errors remain: You need *two* versions of `objcast()` (see http://stackoverflow.com/a/28233753/1187415), and the return type of `create(...)` must be `Self?`. – Martin R May 04 '15 at 18:01
  • (cont.) After fixing that as well, everything compiles in Xcode 6.3.1. – Martin R May 04 '15 at 18:02
  • Impressive effort, I'll give that a try... if I can get it to compile I'll mark this as accepted. Thanks! P.S. I was also sad you could not use 'Self" as an array type, I had tried that before the templating approach. I'll still submit a radar so the Swift guys can fix this. – Kendall Helmstetter Gelner May 05 '15 at 01:08
  • Can you post your code changes for NSManagedObject+KGStore.swift somewhere? I tried making the edits, including the two versions of objcast(), changing create to return Self? and also the findFirst methods, then removing the findAll methods - still get a swift compiler crash. Any use of Self? at all seems to bring out the compiler crash. :-( – Kendall Helmstetter Gelner May 05 '15 at 01:43
  • @KendallHelmstetterGelner: https://gist.github.com/anonymous/1470febf497ef3cb8d27. (I did not *test* the code, but it compiles in my Xcode 6.3.1.) – Martin R May 05 '15 at 05:22
  • I still can't use Self without crashing the Swift compiler, but I've marked this as correct anyways, for thanks for the effort you put in. I'll just have to hope my Radar is noticed and they fix the MANY swift compiler crashes caused by this one project... – Kendall Helmstetter Gelner May 06 '15 at 06:52