Home > database >  Swift expose the protocol to objc in protocol oriented programming
Swift expose the protocol to objc in protocol oriented programming

Time:06-21

I have this code which works well:

public protocol PermissionProvider {
  static func authorizationStatus(for mediaType: AVMediaType) -> AVAuthorizationStatus
}
extension AVCaptureDevice: PermissionProvider {}

Now I need to expose the protocol to Objc runtime:

@objc // added this attribute
public protocol PermissionProvider {
  static func authorizationStatus(for mediaType: AVMediaType) -> AVAuthorizationStatus
}
extension AVCaptureDevice: PermissionProvider {}

But I got an error saying:

AVFoundation.AVCaptureDevice:4:21: error: Objective-C method 'authorizationStatusForMediaType:' provided by method 'authorizationStatus(for:)' does not match the requirement's selector ('authorizationStatusFor:')
    open class func authorizationStatus(for mediaType: AVMediaType) -> AVAuthorizationStatus

From the error message, it looks like related to some naming convention between objc and swift. I wonder how i can fix the compile error?

The reason I introduced this protocol is that, I need to unit test and inject this "permission provider". And the reason I need to expose it to objc is that, some of the caller is still in ObjC

Edit:

I created an empty project, then created a new file with the above code, and got the error

enter image description here

CodePudding user response:

Add the @objc attribute on the protocol method to specify a different method name to be exposed to Objective-C, in this case match it to the relevant method of AVCaptureDevice (authorizationStatusForMediaType:):

@objc // added this attribute
public protocol PermissionProvider {
    @objc(authorizationStatusForMediaType:) static func authorizationStatus(for mediaType: AVMediaType) -> AVAuthorizationStatus
}
extension AVCaptureDevice: PermissionProvider {}

Why?

When Objective-C methods are mapped into Swift, argument descriptions that match the type of the corresponding parameter are pruned (see SE-0005 'Better Translation of Objective-C APIs into Swift'), but when Swift is mapped into Objective-C a corresponding 'de-pruning' doesn't happen.

For example, if in Objective-C we have defined:

- (NappingStatus)nappingStatusForKoala:(Koala *)koala;

- (NappingStatus)nappingStatusForEchidna:(Koala *)koala;

when mapped into Swift these will become:

func nappingStatus(for: Koala) -> NappingStatus

func nappingStatus(forEchidna: Koala) -> NappingStatus

Notice in the first method Koala has been pruned from the parameter name because it matches the type of the parameter it is describing, but in the second method Echidna hasn't been pruned because this doesn't match the type of of the parameter it is describing (Koala).

When mapping from Swift to Objective-C, reversal of this pruning doesn't happen. For example, if in Swift we have defined

func nappingStatus(for: Koala) -> NappingStatus

when mapped into Objective-C it will become:

- (NappingStatus)nappingStatusFor:(Koala *)koala;

So for your case the AVCaptureDevice method in Objective-C that is:

  (AVAuthorizationStatus)authorizationStatusForMediaType:(AVMediaType)mediaType;

in Swift is mapped to (notice MediaType pruned):

class func authorizationStatus(for mediaType: AVMediaType) -> AVAuthorizationStatus

However your protocol method, which in Swift is:

static func authorizationStatus(for mediaType: AVMediaType) -> AVAuthorizationStatus

when mapped into Objective-C will be (notice MediaType not `de-pruned'):

  (AVAuthorizationStatus)authorizationStatusFor:(AVMediaType)mediaType;

which doesn't match the Objective-C method in AVCaptureDevice, so we just provide the correct Objective-C method name via the @objc attribute:

@objc(authorizationStatusForMediaType:) static func authorizationStatus(for mediaType: AVMediaType) -> AVAuthorizationStatus
  • Related