Combinating Swift & Objective C files - objective-c

I have a bit complicated classes in swift and Objective-C combinated together:
Keypad.h:
#import "MyApp-Swift.h"
#interface Keypad : UIViewController {
...
SwiftViewController *swiftViewController; // this is written in swift
...
}
This worked well.
Then I created a new swift file:
AnotherSwiftViewController.swift
#objc class AnotherSwiftViewController: UITableViewController {
func myMethod() {
let keypad = appDelegate.getTabs().selectedViewController as! Keypad // I need get ObjC Keypad class
}
}
And I need to use there the ObjcC Keypad class.
So I added it to the MyApp-Bridging-Header.h:
MyApp-Bridging-Header.h
...
// lot of other Obj-C files imported
...
#import "Keypad.h"
...
And I get the error:
> .../MyApp-Bridging-Header.h:31:9: note: in file included from .../MyApp-Bridging-Header.h:31: #import "Keypad.h"
> .../Keypad.h:13:9: error: 'My_App-Swift.h' file not found \#import "My_App-Swift.h"
> <unknown>:0: error: failed to import bridging header '.../MyApp-Bridging-Header.h'
Any ideas?
/// EDIT:
Maybe will help:
I'm using
#import "My_App-Swift.h"
In the Keypad.h file, not in standard Keypad.m, because I have there that SwiftViewController *swiftViewController; property
Maybe it will help

You have a good point in your ///Edit.
In Keypad.h remove #import "MyApp-Swift.h" and add a forward declaration of your Swift class as follows:
...
#class SwiftViewController;
#interface Keypad : UIViewController {
...
This should do it assuming Keytab.h references SwiftViewController only by pointer and your bridging header is imported correctly otherwise.
See section Referencing a Swift Class or Protocol in an Objective-C Header in https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html.

Related

Cannot find interface declaration in myproject-swift.h

I'
m using XLPagerTabStrip pod in my project,
i have a bridging header for other purposes to integrate from swift to objective c myproject-swift.h
i cant build the project and this error always pops:
Cannot find interface declaration for
'ButtonBarPagerTabStripViewController', superclass of
'ParentViewController'
This is my Controller
import Foundation
import UIKit
import XLPagerTabStrip
class ParentViewController: ButtonBarPagerTabStripViewController {
override func viewDidLoad() {
tabStripStyle()
super.viewDidLoad()
containerView.isScrollEnabled = false
}
}
I have seen this issue everywhere posted but its not yet answered here: 'Cannot find interface declaration' in auto-generated Swift bridging header
Bugs in swift SR-805 SR-5398
You needed to import the -Swift.h for for both the framework and the app target
For Example :
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <Foundation/Foundation.h>
#import "XLPagerTabStrip-Swift.h"
#import "RealmSwift-Swift.h"
...... // Add all frameworks, subclasses, and dependance ios frameworks
#import "MyProject-Swift.h"
You can read this article How to import file header and check paths
I had error "Cannot find interface declaration for 'CLLocation', superclass of 'MYLocation'
for below code
#interface MYLocation : CLLocation // code in MyProject-Swift.h
when I just imported
#import <MyProject/MyProject-Swift.h>
after importing below both, the error is gone.
#import <CoreLocation/CoreLocation.h>
#import <MyProject/MyProject-Swift.h>

Swift class not conforming to Objective C Protocol

I have this objective c protocol as mentioned below:
#protocol Class1<NSObject>
- (void)searchBy:(Enum1)searchType;
#end
And i am trying to make a swift class conform to that protocol . The stub which Xcode generated for me is as follows:
func search(by searchType: Enum1) {
}
But Xcode gives me an error while building that the class still doesn’t conform to protocol. What am i missing here?
Do you have your Objective-C header that contains the protocol and enum in the bridging header for swift to find?
So I created a new test app, and the view controller looks as follows:
import UIKit
class ViewController: UIViewController, Class1
{
func search(by searchType: Enum1) {
print("it works")
}
override func viewDidLoad() {
super.viewDidLoad()
let class1 = self as Class1
class1.search(by: Enum1.PlayerStateOff)
}
}
And my objective-C bridging header looks like this:
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import "Foo.h"
And the ObjectiveC header Foo.h looks like this:
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, Enum1) {
PlayerStateOff,
PlayerStatePlaying,
PlayerStatePaused
};
#protocol Class1<NSObject>
- (void)searchBy:(Enum1)searchType;
#end
#interface Foo : NSObject
#end
And it all compiles, runs, and prints "it works" just fine in Xcode 9.2. So it's probably your bridging header, but double-check everything you're doing against the above.

How to make protocol from Swift visible in .h file?

I have a 3 files:
PLThreadViewController.swift
#objc protocol PLThreadViewDelegate {
func threadViewControlledWillDismiss(threadViewController: PLThreadViewController)
}
class PLThreadViewController: UIViewController {}
PLMessagesTableViewController.h
#import <UIKit/UIKit.h>
#interface PLMessagesTableViewController : UITableViewController <PLThreadViewDelegate>
#end
The problem is: Cannot find the protocol declaration for PLThreadViewDelegate
When i try to add:
#import "Module-Swift.h"
then:
Module-Swift.h file not found. The name of module is very correct. I read about spaces, underscores in the name and so on.
Did Xcode create a bridging header for you? If so, look at the naming Xcode uses there and that will clue you in on how your "-Swift.h" file is named. So if your bridging header is named My_Project-Bridging-Header.h, your auto-generated Swift header will be My_Project-Swift.h.

Swift class using Objective-C class using Swift class

I have an obj-c project to which I successfully added a new Swift class A, which is being used by some existing obj-c class B - the use of the automatically generated "MyProject-Swift.h" header worked as expected.
I also successfully added a new Swift class C that uses some existing obj-c class D - the use of the bridging header also worked as expected.
However, suppose I want to refer from my Swift class C to the existing obj-c class B (which in turn refers to the new Swift class A). In order to do that I need to import "B.h" to the bridging header. However, if I do that I get an error in class B: "'MyProject-Swift.h' file not found" (i.e., the file is no longer generated).
Am I doing something wrong or is this a kind of interaction between Swift and Objective-C that is not allowed? It looks like there is a kind of circular reference that the compiler is unable to solve.
--- EDIT ---
I'll try to make the question clearer by adding some code.
-- PREAMBLE --
I added a new Swift class to an obj-c project:
// SwiftClassA.swift
import Foundation
#objc class SwiftClassA : NSObject {
var myProperty = 0
}
The code compiles correctly and is translated into obj-c stubs in the automatically generated "MyProject-Swift.h" header like so:
// MyProject-Swift.h
...
SWIFT_CLASS("_TtC7MyProject11SwiftClassA")
#interface SwiftClassA : NSObject
#property (nonatomic) NSInteger myProperty;
- (instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
Now, one obj-c class uses SwiftClassA:
// ObjCClass.h
#import <Foundation/Foundation.h>
#import <MyProject-Swift.h>
#interface ObjCClass : NSObject
#property (nonatomic, strong) SwiftClassA *aProperty;
#property (nonatomic) int *aNumber;
#end
This also works seamlessly.
-- THE QUESTION --
Can I now create a new Swift class that refers to the obj-c class (ObjCClass) that is using the Swift class SwiftClassA?
This is what I can't do.
If I add the new Swift class:
// SwiftClassB.swift
import Foundation
#objc class SwiftClassB : NSObject {
var aPropertyOfClassB = 1
func someFunc() {
var objCObject = ObjCClass()
var theProperty = objCObject.aProperty
print("The property is \(theProperty)")
}
}
this of course won't compile because of "Use of unresolved identifier 'ObjCClass'". So I need to add that to the bridging header file:
// BridgingHeader.h
#ifndef MyProject_BridgingHeader_h
#define MyProject_BridgingHeader_h
...
#import "ObjCClass.h"
#endif
However, if I do that, the ObjCClass.h file won't compile giving a "'MyProject-Swift.h' file not found".
I've read in several places (with no example, though) that this may mean that there is a circular reference and that a forward reference using #class could solve the problem. However, I'm not sure what needs to be forward referenced and where, and all my attempts failed.
I hope the question is no longer confusing now!
This is a typical cyclical referencing problem.
Be careful to read the docs:
To avoid cyclical references, don’t import Swift into an Objective-C header file. Instead, you can forward declare a Swift class to use it in an Objective-C header. Note that you cannot subclass a Swift class in Objective-C.
So, you should use "forward declare" in .h, and #import in .m:
// ObjCClass.h
#import <Foundation/Foundation.h>
#class SwiftClassA;
#interface ObjCClass : NSObject
#property (nonatomic, strong) SwiftClassA *aProperty;
#property (nonatomic) int *aNumber;
#end
// ObjCClass.m
#import "ObjCClass.h"
#import "MyProject-Swift.h"
#implementation ObjCClass
// your code
#end

Swift protocol in Objective-C class

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!)
}
There are two common reasons for this occuring:
Getting the module name wrong, see my answer.
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.
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"
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...
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.
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()
Import delegate as like this in .h file
#protocol AnalyticProtocol;
and add this in to .swift file
#objc public protocol AnalyticProtocol {
}
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.