43
votes

I wrote SearcherProtocol in Swift and need to implement an Objective-C class FileSearcher which has to use this protocol.

So I tried this:

#import <Foundation/Foundation.h>

@interface FileSearcher : NSObject <SearcherProtocol>

// ... class content

@end

The compiler tells me

Cannot find protocol declaration for 'SearcherProtocol'

The corresponding bridged header file (modulename-Swift.h) is being imported within FileSearcher.m.

Importing SearcherProtocol into FileSearcher.h throws another compiler error: module name-swift.h file not found

Does anybody have any clue what I'm doing wrong?

I'm using Xcode 6 Beta 5.

Edit

Here is the protocol declaration in Swift:

@objc protocol SearcherProtocol
{    
    var searchNotificationTarget: SearchCompletedProtocol? { get }
    var lastSearchResults: [AnyObject] { get set }

    func search(searchParam: String, error: NSErrorPointer) -> Bool
}

And the SearchCompletedProtocol:

@objc protocol SearchCompletedProtocol
{
    func searchCompletedNotification(sender: AnyObject!)
}
7
Is the protocol declared with @objc flag?Sulthan
Yes, it is. Looking into the generated header file tells me it's there.Alex
Could you show us the protocol declaration?Sulthan
Sure. I will post it tomorrow (I'm out of office now).Alex
If anyone is finding that their Swift Delegate protocol isn't found and they're using both Objective-C and Swift, it's because you're missing the @objc flag.Will

7 Answers

35
votes

There are two common reasons for this occuring:

  1. Getting the module name wrong, see my answer.
  2. Having a circular reference - see mitrenegades answer below.

1. Get the module name right:

If both the swift protocol and and Objective C are in the same project then according to apple you should just need to make sure you get the correct module name.

For Xcode6 beta 5 you can find it under BuildSettings->Packaging->Product Module Name

A common mistake would be to think that each swift file/class gets its own file, but instead they are all put into one big one that is the name of the project.

A further mistakes are if the module name has spaces, these should be replaced with underscores.

Edit:

With your protocol I created a test project called 'Test' which compiles perfectly and it has the files:

TestObjClass.h

#import <Foundation/Foundation.h>
#import "Test-Swift.h"

@interface TestObjCClass : NSObject <SearcherProtocol>

@end

TestObjClass.m

#import "TestObjCClass.h"


@implementation TestObjCClass

@end

TestProtocol.swift

import Foundation

@objc protocol SearcherProtocol
{
    var searchNotificationTarget: SearchCompletedProtocol? { get }
    var lastSearchResults: [AnyObject] { get set }

    func search(searchParam: String, error: NSErrorPointer) -> Bool
}

@objc protocol SearchCompletedProtocol
{
    func searchCompletedNotification(sender: AnyObject!)
}

2. Avoid circular reference:

Mitrenegades answer explains this, but if your project needs to use the explicit objc class that uses the swift protocol, (rather than just using the protocol) then you will have circularity issues. The reason is that the swift protocol is defined to the swift-objc header, then to your obj-c class definition, which then goes again to the swift-objc header.

Mitrenegades solution is to use an objective-c protocol, is one way, but if you want a swift protocol, then the other would be to refactor the code so as to not use the objective-c class directly, but instead use the protocol (e.g. some protocol based factory pattern). Either way may be appropriate for your purposes.

26
votes

When you have

#import "moduleName-Swift.h" 

in the .h file that you want to be a delegate, and you have that .h file also in the bridging headers file, there's a circular reference that causes the moduleName-Swift.h to fail compilation. for @james_alvarez's test project, it's probably working because you don't need to include TestObjClass.h into the bridging header.

The best way for me to combine objc files that need to be the delegate for a class written in swift, but that also needs to be included in the bridging header so other swift files can access this objc class, is to create a separate protocol file in objc:

MyProtocol.h:

@protocol MyDelegate <NSObject>

-(void)didDoThis;
-(void)didDoThat;

@end

ViewController.h:

#import "MyProtocol.h"

@interface ViewController : UIViewController <MyDelegate>

MyProject-Bridging-Header.h

#import "MyProtocol.h"
#import "ViewController.h"
22
votes

I know this was a long time ago, but I just struggled with the same problem when adding a protocol to my Swift code, and it wasn't being added to the -Swift.h header file, hence "Cannot find protocol declaration"

The problem was my protocol wasn't marked as Public. I changed my protocol from this:

@objc protocol MyProtocol { //etc.... }

to this:

@objc public protocol MyProtocol { //etc.... }

I'm still not entirely sure why I need 'Public' but nobody else seems to, but hey it works...

1
votes

Make sure you are including the auto generated Swift header in your ObjectiveC file. It will have the same name as your project module followed by -Swift.h.

For instance if your Project Module is MyTarget, then you would use:

#import "MyTarget-Swift.h"

If you are typing in the import into your Objective C file, it will not autocomplete. You can verify that you have the correct file by Command-clicking on the header after typing it in.

0
votes

You can do the conformance part from Swift side 😁

So you have a swift protocol and want to make an Objective-C type conforms to it,

Swift Side

Add @objc to your protocol to make it visible to Objective-C world.

@objc protocol IndianCooking {
  func cookChicken()
}

Objective-C Side

In the implementation .m file you do the following:

#import "YourProject-Swift.h"
@interface Cheef ()<IndianCooking> { 
}

and in the header file .h add the method cookChicken()

-2
votes

Import delegate as like this in .h file

@protocol AnalyticProtocol;

and add this in to .swift file

@objc public protocol AnalyticProtocol {

}
-3
votes

Try adding #import "Product_Module_Name-Swift.h" to your Product_Module_Name-Prefix.pch file. That fixed it for me, plus you will now have access to your swift files from any objc file.