I have a Swift class called LoginCoordinator
that performs a "Sign In with Apple" request. In order respond to callbacks it conforms to the ASAuthorizationControllerDelegate
. Because of this requirement, I also need to make this class a subclass of NSObject
:
class LoginCoordinator: NSObject { ... }
extension LoginCoordinator: ASAuthorizationControllerDelegate { ... }
This class will not be used by any Objective-C classes at all. It is intended to be consumed by other Swift classes (i.e. LoginViewController
) which will indeed be used by Objective-C classes
The problem is as follows. I eventually import the top-level Swift classes into Objective-C by doing:
#import "MyProject-Swift.h"
This causes a compile-time error in the generated MyProject-Swift.h
file that says:
Cannot find protocol declaration for 'ASAuthorizationControllerDelegate'; did you mean 'UINavigationControllerDelegate'?
This error appears on the line for the generated Objective-C category that corresponds to the extension that I wrote above in Swift, where I make LoginCoordinator
conform to ASAuthorizationControllerDelegate
.
If, before importing MyProject-Swift.h
, I also import <AuthenticationServices/AuthenticationServices.h>
, then the error goes away. But I do not want to import this everywhere I import Swift.
If I understand correctly, what is going on is this: when I import my Swift files into Objective-C, it imports all the generated headers in MyProject-Swift.h
. But one of those category headers (LoginCoordinator
) references a protocol (ASAuthorizationControllerDelegate
) that has not been imported, which raises an error.
I noticed that MyProject-Swift.h
only began to generate code for LoginCoordinator
once I made it a subclass of NSObject
. Removing the subclassing removes the generated code, but of course it then cannot conform to the protocol.
Is there a way to make a Swift class a subclass of NSObject and conform to an Objective-C protocol, without exposing it when importing Swift files in Objective-C?
You can create a private object that actually conforms ASAuthorizationControllerDelegate, and forward message to LoginCoordinator. It's ugly, but can solve the quetion.
class LoginCoordinator {
private lazy var authDelegate: LoginCordinatorASAuthorizationControllerDelegate {
LoginCordinatorASAuthorizationControllerDelegate(coordinator: self)
}
}
//MARK: functions from ASAuthorizationControllerDelegate, but not conforms it
extension LoginCoordinator {
private func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization....)
}
private class LoginCordinatorASAuthorizationControllerDelegate: NSObject, ASAuthorizationControllerDelegate {
weak var coordinator: LoginCoordinator!
init(coordinator: LoginCoordinator)
}
You can also make your LoginCoordinator a nested class which is not able to expose to Objective-C.
enum Login {
class Coordinator: NSObject, ASAuthorizationControllerDelegate {...}
}