简体   繁体   中英

Add Swift protocol conformance to Objective-C header and make it public

I've read along here and here about conforming to Swift protocols in Objective-C headers, and I'm not quite getting the behaviour I want - I'm also looking for a better understanding of how this all works. Here's my scenario.

I have a protocol in Swift:

PersonalDetailsProtocol.swift

@objc
protocol PersonalDetailsProtocol {
    func doSomeWork()
}

I then have an Objective-C class with a header and implementation file

RegistrationPersonalDetails.h

@protocol PersonalDetailsProtocol; // Forward declare my Swift protocol

@interface RegistrationPersonalDetails : NSObject <PersonalDetailsProtocol>

@end

RegistrationPersonalDetails.m

#import "RegistrationPersonalDetails.h"

@implementation RegistrationPersonalDetails

- (void)doSomeWork {
    NSLog(@"Working...");
}
@end

At this point everything compiles, although there is a warning in the RegistrationPersonalDetails.h file stating Cannot find protocol definition for 'PersonalDetailsProtocol' . Other than that warning, the issue I'm facing is I can't publicly call the doSomeWork method on an instance of RegistrationPersonalDetails .

The call site in Swift would look something like:

let personalDetails = RegistrationPersonalDetails()
personalDetails.doSomeWork()

but I get an error stating:

Value of type 'RegistrationPersonalDetails' has no member 'doSomeWork'

I get that the method isn't public, because it's not declared in the header file. But I didn't think it should have to be as long as the protocol conformance is public ie declared in the header file.

Can anyone point me on the right path here and offer an explanation? Or is this even possible? I can obviously rewrite the protocol in ObjC, but I just always try to add new code as Swift.

In pure objective-c, you cannot make a class conform to a protocol without importing it in the header. To make the use of a protocol private, the conformance shouldn't be declared in the header. It remains possible to call such a method using a cast however, but it should be done with caution because it's a little bit unsafe:

if ([anInstance respondToSelector:@selector(myProtocolMethod)])
    [(id<MyProtocol>)anInstance myProtocolMethod];

I'm not familiar with Swift, but I think you can do the same this way (or something close to it):

 if let conformingInstance = anInstance as? MyProtocol
     conformingInstance.myProtocolMethod

EDIT: To complete my first assertion, forward declarations can still be used in the header when you need to declare a method receiving or returning an instance conforming to that protocol:

@SomeProtocol;

// This is not possible
@interface MyClass : NSObject <SomeProtocol>         

// But this is possible
@property (nonatomic) id<SomeProtocol> someProperty;
-(void) someMethod:(id<SomeProtocol>)object; 
@end

In this document Apple clearly said that:

Forward declarations of Swift classes and protocols can be used only as types for method and property declarations.

So it seems that the rule is the same whatever the protocol is an Objective-c protocol or a Swift protocol.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM