Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Protocol extension on an ObjC protocol

Tags:

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?

like image 822
some_id Avatar asked Aug 16 '16 17:08

some_id


People also ask

What is a protocol extension?

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.

How do you extend a protocol?

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

What is a protocol OBJC?

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.

Can we extend protocol in Swift?

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.


1 Answers

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 
like image 116
Dmytro Kabyshev Avatar answered Sep 23 '22 09:09

Dmytro Kabyshev