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