Why is a specific Swift class not visible to Objective-C? - objective-c

All
Xcode 10.3 and Swift 5.0
I have a complex project with 3 targets ("Name", "Name Test" and "Name Local")
I have set all interoperability headers between Swift and Objective-C, including the Name-Bridging-Header.h, the Objc Preprocessor Macros to import the Name-Swift.h, Name_Test-Swift.h or Name_Local-Swift.h selectively
#ifdef TEST
#import "Name_Test-Swift.h"
#elif LOCAL
#import "Name_Local-Swift.h"
#else
#import "Name-Swift.h"
#endif
I have imported several Swift classes that I use frequently and everything works fine.
Now....
I added a new Swift class. This new class is subclass of NSObject
class NewClass: UIView { }
But using the new class in a Objective-C class, Xcode can't find this new class, with these errors:
# import "Name-Swift.h" is not recognized.
NewClass *item = [NewClass alloc]; - Unknown type name 'NewClass'
I checked the following:
- The new class is added to all Targets and is verified in the Build-Phases->Compile-Sources section
My other Swift classes don't have public or #objc identifiers, and everything works OK..... So I still I tried using them in the new class (#objc and public in the class definition), with no success
With only adding NewClass *item = [NewClass alloc]; in any Objective-C class makes Xcode to stop recognizing "Name-Swift.h"
Any idea what is missing?

After some testing, I found that I need to import the Name-Swift.h file in the .h file where I want to declare my object.
My project defines the #import Name-Swift.h in the Name-Prefix.pch
#ifdef TEST
#import "Name_Test-Swift.h"
#elif LOCAL
#import "Name_Local-Swift.h"
#else
#import "Name-Swift.h"
#endif
The #import declaration in the .pch file allows the Swift class to be accesible anywhere from the .m file of my Objective-C class, but not from the .h
I am not sure if that is the expected behavior... I will do a deep dive to the Documentation.

Related

getting Swift Object from Objective C class using another Swift File

I have been trying with no success the following structure:
ClassA.swift
class ClassA:NSObject{
var varA = ""
}
then I have a ClassB.h and ClassB.m (Objective c) and I am not able to define "Project-Swift.h" in the .h file so i import it into the .m
#interface ClassB()
#property ClassA *myClassA;
#end
and
#synthesize myClassA = theAClass;
The problem comes when I try
class ClassC:NSObject{
let theClassAFromC = ClassB.myAClass
}
I get an error message Value of type 'ClassB' has no member 'theClassA'
Add your objc header to bridging header
In your objc header use #class notation for your swift class, e.g. #class ClassA;, instead of import "Project-Swift.h" file. You can import that one in your objc implementation file.
Don't forget that you cannot access non-static property of objc class the way you're declaring. Instead, initialize your ClassB object in your swift class and access it's property when needed. For instantiating the ClassA object you can use dependency injection in ClassB
As part of the convenience, use #objc declaration for your Swift classes accesible to objc runtime
try to search with keyword "Bridging-Header" and then add
#import "ClassB.h"
That does not work in my case.
adding ClassB.h in my Bridge File makes properties from .h Visible But not the the properties in my .m file. If i add Class.m In my header file then Module-Swift.h is not found

Protocol declared in Objective-C Framework is not visible in Swift

As the title says I have trouble using a protocol declared in Objective-C framework in a Swift app. I will try to describe the situation in detail below.
I have a framework that has mixed Objective-C and Swift code.
This is similar to what I have in the file with protocol definition.
// MyLibrary.h
#protocol MyDelegate <NSObject>;
#interface ClassThatUsesMyDelegate
#property (weak, nonatomic, nullable) id<MyDelegate>delegate;
#end
#protocol MyDelegate
- (void)aMethod;
#end
Objective-C code is wrapped in a module as such:
// module.modulemap
module MyObjCLibrary {
header "MyLibrary.h"
export *
}
Then I have Swift code that uses the Objective-C code via import MyObjCLibrary where all of the declared above in MyLibrary.h is visible and usable.
The umbrella header of the framework has #import "MyLibrary.h". Which should expose all the code from MyLibrary.h to anyone who uses my framework.
Here is where the problem starts. When I embed this framework in my test app I can use any Swift code from my library as well as ClassThatUsesMyDelegate from MyLibrary.h. Howerver, when I try to use the protocol MyDelegate I get a compiler error that this type has not been declared. Example of usage below:
// MyViewController.swift
import UIKit
import MyFramework
class MyViewController: UIViewController {
...
}
extension MyViewController: MyDelegate { // Error: Use of undeclared type 'MyDelegate'
func aMethod() {
...
}
}
EDIT: This seems to be the case only with protocols, everything else like varibles, classes, enums etc are accessible.

Combinating Swift & Objective C files

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.

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.

example Objective-C project calling Objective-C++ code

Anyone know of an example project of a Objective-C project calling Objective-C++ code.
I've read all the stackoverflow q's about getting one to call the other but no luck.
Would help if I had a code sample that worked.
The Obj-C++ I have is a lib .a and some c++ headers. Thats all.
Here's example project I made, it's actually easy to undestand, you can just rename your classes from .m to .mm or in project settings set "Compile sources as" to Objective-C++. I have had some trouble with C++ headers which didn't have C++ implementation as well - changing settings to compile everything as Objective-C++ helped.
Here's the code https://github.com/libec/StackOverflow/tree/master/03-Obj-C%2B%2B
Here's a contrived example. Without more information about what you're trying to do, or the errors you're receiving, it will be impossible to guide you much further. Remember that any code that calls C++/Objective-C++ code or imports a header that includes C++-isms must be compiled as Objective-C++ (use the .mm extension and Xcode will automatically do the right thing).
/*Objcpp.h
**********/
#interface MyClass : NSObject
{}
- (void)myMethod;
#end
/*Objcpp.mm
***********/
#import "Objcpp.h"
#implementation MyClass
- (void)myMethod {
//some c++ and/or objective-c calls
}
#end
/*myobjc.h
***********/
#interface MyObjCClass : NSObject
{}
- (void)someMethod;
#end
/*myobjc.mm
***********/
#import "myObjCClass.h"
#import "Objcpp.h"
#implementation MyObjCClass
- (void)someMethod {
MyClass *o = [[MyClass alloc] init];
[o myMethod];
}
#end