Playgrounds 4 Preview Feature causes compile errors when using methods with same name, different type signature

Create a new App project in Playgrounds 4 and paste the following code into a new swift file or inside the MyApp file:

import CoreBluetooth

class BTDelegate:NSObject, CBPeripheralDelegate{

    public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor descriptor: CBDescriptor, error: Error?){
    }

    public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?){
    }
}

This causes the following error to appear:

Method 'preview__peripheral(:didUpdateValueFor:error:)' withObjective-C selector '_preview__peripheral:didUpdateValueFor:error:' conflicts with previous declaration with the same Objective-C selector

Obviously some Playgrounds preview magic can't handle methods with the same name, but different type signature. Unfortunately I can't rename the methods because they are part of the CBPeripheralDelegate protocol.

Does anyone know a way to solve this problem?

Accepted Reply

I found the following workaround: If you separate the two conflicting methods into two different files as extensions on BTDelegate Playgrounds App compiles the code without an error. Unfortunately I could not yet test if in this case both delegate methods are still being called by CoreBluetooth.

Replies

this post on stackoverflow might help. adding @nonobjc dynamic at least makes the error disappear. @objc(newNameMethod:) could be an option, too.

  • Thank you for your answer. Unfortunately in this case this would not work, because Objective-C must be able to call these methods exactly with the specified names otherwise my app would not konform to the CBPeripheralDelegate protocol.

Add a Comment

You should definitely file a bug about this. It seems that the preview infrastructure creates its methods based on the Swift view of the original method, not the Objective-C view, and so you get this collision.

Please post your bug number, just for the record.

I tried a bunch of things to get around this, without any success. It’s possible that there a sneaky way to do this that I’ve missed. Hopefully someone else will chime in.

Failing that, my next step involves Silly Runtime Hacks™. You could declare a method with a completely different signature and then use the Objective-C runtime to add it as a new method with the right signature.

The code pasted in below compiles on Swift Playgrounds. I don’t have time to test it, alas, but I figured that you could take care of that (-:

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"


import CoreBluetooth

class BTDelegate: NSObject, CBPeripheralDelegate {

    public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor descriptor: CBDescriptor, error: Error?) {
    }
    
    @objc
    public func peripheral(_ peripheral: CBPeripheral, qqqDidUpdateValueForQQQCharacteristic characteristic: CBCharacteristic, error: Error?) {
    }
}

func hack() {
    let cls = BTDelegate.self
    let srcSelStr = "peripheral:qqqDidUpdateValueForQQQCharacteristic:error:"
    let dstSelStr = "peripheral:didUpdateValueForCharacteristic:error:"
    let srcSel = Selector(srcSelStr)
    let dstSel = Selector(dstSelStr)

    let method = class_getInstanceMethod(cls, srcSel)!
    let types = method_getTypeEncoding(method)
    let imp = method_getImplementation(method)
    let success = class_addMethod(cls, dstSel, imp, types)
    precondition(success)
}
  • Thanks for your help and assessment of the problem. It already filed a bug report: FB9919924

    Unfortunately all my bug reports of the last year never got any attention.

Add a Comment

I found the following workaround: If you separate the two conflicting methods into two different files as extensions on BTDelegate Playgrounds App compiles the code without an error. Unfortunately I could not yet test if in this case both delegate methods are still being called by CoreBluetooth.

It already filed a bug report: FB9919924

Thanks for that.

I found the following workaround

Oh, that’s much nicer (-:

I tried using extensions but I didn’t try putting them in separate files.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"