1

I have a dictionary that contains function calls plus a generic method for adding functions to that dictionary. The moment I downcast that generic function so that I can add it to the dictionary, the compiler crashes.

You can see that when you put the following code in a playground:

import Cocoa

class Email:NSObject {
    var Subject:String = ""
}
class SMS:NSObject {
    var Message:String = ""
}

var allfunc = Dictionary<String, (item: NSObject) -> Void>()

func test<T:NSObject>(type:T, myfunc:(item: T) -> Void) {
    allfunc[NSStringFromClass(type.dynamicType)] = myfunc as? (item:NSObject) -> Void
}

test(Email(), {item in NSLog("\(item.Subject)")})
test(SMS(), {item in NSLog("\(item.Message)")})

for (key: String, value: (item: NSObject) -> Void) in allfunc {
    if key == NSStringFromClass(Email().dynamicType) {
        var mail = Email()
        mail.Subject = "testing mail"
        value(item: mail)
    } else {
        var sms = SMS()
        sms.Message = "testing SMS"
        value(item: sms)
    }
}

When I change the test function to this then I't won't crash, but I will lose autocomplete:

func test<T:NSObject>(type:T, myfunc:(item: NSObject) -> Void) {
    allfunc[NSStringFromClass(type.dynamicType)] = myfunc
}

Is this a compiler bug that you cannot downcast functions, or shouldn't it be possible anyway? Is there an alternative that I can use to create similar functionality?

Maybe the code in the playground looks a little strange. It's a modified extract from my project. If you want to see the complete project with the same crash, then have a look at https://github.com/evermeer/EVCloudKitDao

Edwin Vermeer
  • 13,017
  • 2
  • 34
  • 58
  • 1
    If you are trying to diagnose compiler crashes, it's not really something that can be solved through code, i.e. not within stackoverflow's scope. Try apple's dev forums, Chris is active there, as well. – Mazyod Sep 07 '14 at 07:07
  • Thanks Mazyod, I will look around in the dev forums if there is something there and if there isn't I will post a message there. – Edwin Vermeer Sep 07 '14 at 07:49
  • 1
    @EdwinVermeer: You cannot cast between closures of different signature. Compare http://stackoverflow.com/questions/24586293/cast-closures-blocks. – Martin R Sep 07 '14 at 09:35
  • 2
    But if you have a way to crash the compiler, be sure to open a radar (bugreport.apple.com). The compiler should never crash, even if your code is wrong. You may also want to submit your test case to https://github.com/practicalswift/swift-compiler-crashes. – Rob Napier Sep 07 '14 at 15:22

1 Answers1

0

You can not cast functions. In the current Xcode version 6.2 you will get the following run time exception: Swift dynamic cast failure

There is however a workaround for this problem which I implemented in my connect function of https://github.com/evermeer/EVCloudKitDao The solution is to wrap the function instead of casting it. The code will look like this :

public var insertedHandlers = Dictionary<String, (item: EVCloudKitDataObject) -> Void>()
public var updateHandlers = Dictionary<String, (item: EVCloudKitDataObject, dataIndex:Int) -> Void>()

public func connect<T:EVCloudKitDataObject>(
    type:T,
    completionHandler: (results: [T]) -> Void,
    insertedHandler:(item: T) -> Void,
    updatedHandler:(item: T, dataIndex:Int) -> Void,
    deletedHandler:(recordId: String, dataIndex:Int) -> Void,
    errorHandler:(error: NSError) -> Void
    ) -> Void {
        func insertedHandlerWrapper(item:EVCloudKitDataObject) -> Void {
            insertedHandler(item: item as T)
        }
        func updatedHandlerWrapper(item:EVCloudKitDataObject, dataIndex:Int) -> Void {
            updatedHandler(item: item as T, dataIndex: dataIndex)
        }
        self.insertedHandlers[filterId] = insertedHandlerWrapper
        self.updateHandlers[filterId] = updatedHandlerWrapper
        ...

Now the updateHandler still uses the T instead of the EVCloudKitDataObject and in the handler itself you can use the original type and does not need to cast it.

Edwin Vermeer
  • 13,017
  • 2
  • 34
  • 58