1

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

7
  • When you add @objc to the protocol does it give you the error? @Paulw11 Commented Jun 20, 2022 at 7:24
  • The 2nd chunk of code is everything I have that will reproduce the error. Commented Jun 20, 2022 at 7:25
  • Did you forget the extension AVCaptureDevice: PermissionProvider {} Commented Jun 20, 2022 at 7:25
  • Just created a new project, and got the error reproduced. (See screenshot) Commented Jun 20, 2022 at 7:29
  • What do you mean by swift definition? AVFoundation is in ObjC i believe. Commented Jun 20, 2022 at 7:36

1 Answer 1

2

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
Sign up to request clarification or add additional context in comments.

2 Comments

can you explain a bit more? why should i rename it? is it because it was renamed in avfoundation code itself using NS_SWIFT_NAME?
Have added some more information to the answer to explain why this is needed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.