I have an Objective-C protocol which is used by mostly objective-C objects and one or two Swift objects.
I would like to extend the protocol in Swift and add 2 functions. One to register for a notification and another to handle the notification.
If I add these
func registerForPresetLoadedNotification() { NSNotificationCenter.defaultCenter().addObserver(self as AnyObject, selector: #selector(presetLoaded(_:)), name: kPresetLoadedNotificationName, object: nil) } func presetLoaded(notification: NSNotification) { }
I get an error on the #selector which says:
Argument of '#selector' refers to a method that is not exposed to Objective-C
If I then mark presetLoaded as @objc
I get an error which says:
@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes
I also cannot mark the protocol extension as @objc
When I create the Objective-C protocol as a Swift protocol I get the same error.
Is there a way to achieve this that will work for Objective-C and Swift classes that use the protocol?
Protocols let you describe what methods something should have, but don't provide the code inside. Extensions let you provide the code inside your methods, but only affect one data type – you can't add the method to lots of types at the same time.
You cannot “extend” a protocol because by definition a protocol doesn't have an implementation - so nothing to extend. (You could say that we “extend a protocol WITH some functionality”, but even an extended protocol is not something we can apply a function to.)
Advertisements. Objective-C allows you to define protocols, which declare the methods expected to be used for a particular situation. Protocols are implemented in the classes conforming to the protocol.
In Swift, you can even extend a protocol to provide implementations of its requirements or add additional functionality that conforming types can take advantage of. For more details, see Protocol Extensions. Extensions can add new functionality to a type, but they can't override existing functionality.
Indeed, you can't really mark a function of a protocol extension as @objc (or dynamic, which is equivalent by the way). Only methods of a class are allowed to be dispatched by Objective-C runtime.
In your particular case, if you really want to make it through protocol extension, I can propose the following solution (assuming your original protocol is named ObjcProtocol).
Let's make a wrapper for our notification handler:
final class InternalNotificationHandler { private let source: ObjcProtocol init(source: ObjcProtocol) { // We require source object in case we need access some properties etc. self.source = source } @objc func presetLoaded(notification: NSNotification) { // Your notification logic here } }
Now we need extend our ObjcProtocol to introduce required logic
import Foundation import ObjectiveC internal var NotificationAssociatedObjectHandle: UInt8 = 0 extension ObjcProtocol { // This stored variable represent a "singleton" concept // But since protocol extension can only have stored properties we save it via Objective-C runtime private var notificationHandler: InternalNotificationHandler { // Try to an get associated instance of our handler guard let associatedObj = objc_getAssociatedObject(self, &NotificationAssociatedObjectHandle) as? InternalNotificationHandler else { // If we do not have any associated create and store it let newAssociatedObj = InternalNotificationHandler(source: self) objc_setAssociatedObject(self, &NotificationAssociatedObjectHandle, newAssociatedObj, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC) return newAssociatedObj } return associatedObj } func registerForPresetLoadedNotification() { NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(notificationHandler.presetLoaded(_:)), name: kPresetLoadedNotificationName, object: self) } func unregisterForPresetLoadedNotification() { // Clear notification observer and associated objects NSNotificationCenter.defaultCenter().removeObserver(self, name: kPresetLoadedNotificationName, object: self) objc_removeAssociatedObjects(self) } }
I know this might look not so elegant, so I'd really consider changing a core approach.
One note: You do might want to restrict your protocol extension
extension ObjcProtocol where Self: SomeProtocolOrClass
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With