I'm trying to convert an Objective-C app to swift.
I'd like to import classes in aurioTouch to Swift app, so I created the following Bridging-Header file:
#import "AudioController.h"
but I received following errors in DCRejectionFilter.h, BufferManager.h, FFTHelper.h:
Unknown type name 'class'; did you mean 'Class'?
Expected ';' after top level declarator
and also in AudioController.h:
Unknown type name 'BufferManager'
Unknown type name 'DCRejectionFilter'
Of course I use .mm instead of .m, but this does not work.
update
The simple swift project just including aurioTouch Library (with Obj-C and C++) is as follows:
https://github.com/pika-shi/aurioTouch-Sample
The present answer shows you how to solve the Bridging Header #import, and is a step-by-step tutorial on how to create an Objective-C wrapper object.
.mm does not mean Swift
.mm does not mean Objective-C either
It means Objective-C++, and merely renaming a file to .mm offers no improvement. Notice that you are still including the same .h files, and those are where the problem starts. These .h reference C++ classes, and they must be wrapped.
Wrap the C++ in Objective-C
The file AudioController.h is not an Objective-C file: it includes BufferManager.h which is a C++ file, and compilation stops right there.
You need to create a true wrapper, say AudioControllerBridge which .h is in Objective-C, and .mm can, in turn, make references to C++:
.h
Absolutely, positively no C++ allowed, explicit, included, or else.
#import <Foundation/Foundation.h>
#interface AudioControllerBridge : NSObject
// ...
#end
.mm
Objective-C++ tolerates all the C++ you need, as long as it is not exposed in the interface.
#import "AudioControllerBridge.h"
#import "AudioController.h"
#implementation AudioControllerBridge
// ...
#end
Of course, you could simply modify the AudioController.h directly, but we will consider this bad practice: for the rest of this answer, we will assume that you are attempting to integrate aurioTouch as-is, with exactly 0 lines of code changed.
In the implementation of AudioControllerBridge, you can now instantiate the AudioController, and import all the C++ files you need for proper compilation, something you could not do in the .h. Remember that the .h you are exposing to Swift in Briding-Header must be a pure Objective-C interface.
// Bridging Header
#import "AudioControllerBridge.h"
ARC
You will soon see that you need to download CoreAudio/PublicUtility because some files, like CADebugPrintf are simply missing from the example, and somehow will not build in your new project, at least in DEBUG mode.
If you made it so far, you will then find out that you will get a dozen deprecated warnings, which you can ignore for now, and half as much ARC errors in AudioController.mm. Fix with -fno-objc-arc Compiler Flag:
If you made it so far (props), and have added:
Accelerate.framework
AudioToolbox.framework
AVFoundation.framework
to your target in Build Phases and compiled, you will find that it builds and links.
Wrapping it up
It took me 1h 47mins to reach that point (proof below). The next step is of course to actually put the wrapper code in AudioControllerBridge, so that it returns (wraps):
BufferManager* _bufferManager;
DCRejectionFilter* _dcRejectionFilter;
- (BufferManager*) getBufferManagerInstance;
which are the 3 non-Objective-C elements in that class.
It may be cleaner to to wrap BufferManager and DCRejectionFilter as well, so that they can be used freely in Swift. I will let that decision to the reader.
Demo
Just for the record, all the instructions above lead to a successful compilation of
let ac = AudioControllerBridge()
as seen in this screenshot. It reveals all the files needed, and show a successful build on Xcode 7 for iPhone 6 on iOS 9.
Related
This is an interview.
There are some answers existing:
Uses #Class in .h file is enough, and using #import "" in .m file .
In header files referenced header file will at compile time is completely known file header information for all interface introduced, such as imported medical in A.H was future if import in the SubA.h A.h was, implicitly imported medical and increase unnecessary compilation, cohesion is low;
In the implementation file header files, the compiler will only need to know the type of interface information, will reduce the compile time, and the degree of coupling between the classes will be reduced.
Can and how does one ask about this issue and What is the definitely guidelines ?
The (maybe simplified) version goes like this:
If you import the header/interface file, it is like including the whole other file at that place. So typically all of your interface code is known at that place. When you are working with some classes/objects, the compiler needs to know what they are and how they are called exactly: what arguments/parameters do the methods need, what do the methods return? To answer these questions, the header files are usually included in the implmentation (.m) file.
As the implementation imports its own header file, you can often put the other imports just there. But when do you need the import in the header file? When you are declaring your interface, you often mention other types/classes, i.e. you say #proerty (strong) SomeClass *element;. Now the compiler needs to know something about SomeClass, but not all. If the compiler knows that it is another class, that will be ok at this time. In the header file, no more information is needed, so the "forward declaration" of #class SomeClass may suffice. Importing the complete header file for your other class works as well.
So where's the downside of import? There's mainly two arguments: compile time and import cycles. Consider the little example - whenever SomeClass.h changes, everything that includes it must be recompiled as well. This easily cascades through the complete code. Also, obviously, any code that imports your header file will import all their imports etc. The cycles arguments works like this. A.h imports B.h, B.h imports A.h. Depending on what's in them, one should be known before the other...
(I'd like to mention that the problems are a bit more relaxed with latest compilers but the basic argumentation still holds).
Some more notes: The #class forward declaration may be needed when you declare two types that relate to each other, i.e. a protocol that uses some class which you declare just below:
#class SomeClass;
#protocol SomeProtocol
-(void)doSomethingWith:(SomeClass *)object;
#end;
#interface SomeClass
#end
Forward declaration doesn't work for protocols the class implements or parent classes.
So I tried to import aurioTouch into my app and I keep getting this error saying
"unknown type name 'class' did you mean "Class"?
My bridging header file:
#import "AudioController.h"
#import "BufferManager.h"
#import "FFTHelper.h"
#import "DCRejectionFilter.h"
I tried changing this to .mm for all these files but it doesn't solve my issue. Anyone have any ideas why this is happening?
To mix C++ and Objective-C you need to use the .mm extension. If, however, your class is only C++ (and only includes C/C++ headers) then you can use the normal .cpp extension.
.mm
A source file with this extension can contain C++ code in addition to Objective-C and C code. This extension should be used only if you
actually refer to C++ classes or features from your Objective-C code.
I had the same issue, and I could solve by the following answer:
Import aurioTouch Library with Swift
In short, AudioController.h is not an Obj-C file because it imports C++ files (BufferManager.h, FFTHelper.h, and DCRejectionFilter.h). So you cannot bridge it to Swift just like that.
You have to create a pure Obj-C file wrapping AudioController.h.
I've got a project using the Swift generated Bridging Header and have the project set up correctly (no spaces in Names, Uses Modules, pre-declaring Swift classes in my .mm file, Deleting derived data, doing a clean rebuild etc...). The Bridging Header is being generated fine, but the automatically generated -Swift.h has errors in it. What's even worse, those errors are on the generated (in project creation - Swift) versions of AppDelegate and ViewController which would normally compile fine. The errors in the -Swift.h are:
#interface AppDelegate : UIResponder <UIApplicationDelegate>
>> Cannot find interface declaration for 'UIResponder', superclass
of 'AppDelegate'
>> Cannot find protocol declaration for 'UIApplicationDelegate'
#interface ViewController : UIViewController
>> Cannot find interface declaration for 'UIViewController', superclass
of 'ViewController'
I've searched Stack Overflow and the net and can't find any answer that addresses this particular issue. Has anyone else had this? Or, is there a way I can mark my AppDelegate and ViewController classes so Xcode doesn't try and create Objective-C stubs for those Swift classes as I don't actually need them?
Just add
#import <UIKit/UIKit.h>
before
#import <YourProject-Swift.h>
in .mm file. for me it helps.
I had same problem and discovered that..
-Swift.h only work best with Objective-C, not Objective-C++ (.mm)
So I use the Objective-C class to connect with the Swift class. Also You can always load your Objective-C++ class in your Objective-C class.
Try to import UIKit/UIKit.h into bridging header file.
Swift doesn’t play very well with Objective-C++. Since I need to interface with C++, I tend to write an Objective-C++ class MyClass_CPP containing just the interface with C++, then an Objective-C subclass MyClass that can play nicely with Swift.
I'm running into an issue where my Swift code compiles fine but then the generated -Swift.h file has an error in it...
Here is an example of what I'm doing:
class MyScene : CCLayer {
var ctrl : CCControlSlider?
}
This compiles just fine in the swift code and I can see the methods on the ctrl object just fine as well. I have the following in my bridge header:
#import "cocos2d.h"
#import "CCControlSlider.h"
This works perfectly fine in other classes that use other libraries which work correctly. Also note that I can use this CCControlSlider class in my objective-c classes with no issues at all as well.
Here is what happens on the generated -Swift.h file:
SWIFT_CLASS("_TtC15MyProject10MyScene")
#interface MyScene : CCLayer
#property (nonatomic) CCControlSlider * ctrl;
#end
The property has the error "Unknown type name "CCControlSlider" and if it's used in a method then it gives the error "Expected a type".
This works just fine using other classes but for some reason this one class gives this compiler error only in the generated header file and only when used from Swift.
I guess what I'm wondering is, am I doing something wrong or is this just a bug??
While incorporating a set of Objective C files into my mostly Swift-based project I suddenly started getting a lot of these errors on them. Then I realized though the file had:
#import <Foundation/Foundation.h>
It did not have:
#import <UIKit/UIKit.h>
and once I added that line the errors started resolving. Cleaning afterwards always helps. Good luck
NOTE: With newer versions of Swift imports are much easier to type and are done like so:
import UIKit
If you don't want to worry about the order of imports everywhere you import your ProjectName-Swift.h file, try this:
Create a file named ProjectName-Swift-Fixed.h with the following contents
// ProjectName-Swift-Fixed.h
#class CCControlSlider;
// (Add other imports or forward declarations here)
#import "ProjectName-Swift.h"
Second, throughout the rest of your codebase, replace #import "ProjectName-Swift.h" with #import "ProjectName-Swift-Fixed.h"
This approach has two benefits over copying and pasting a list of #import statements above each place you use Swift classes in Objective-C code:
You won't have to worry about the order of imports, except in the ProjectName-Swift-Fixed.h file
When you add new Objective-C classes to your Swift code, you won't have to add new #import statements in every Objective-C file that uses Swift code
The answer given here is the simplest approach: https://stackoverflow.com/a/24216718/341994
Basically, somewhere in your Objective-C code you are importing the automatically generated -Swift.h header. In that same code, before that #import line, insert #import "CCControlSlider.h". The order of these two #import statements is crucial!
The fact that this Objective-C class may not need CCControlSlider is irrelevant (though if it does, that's a bonus). The important thing is the order. We want to expose the namespace to CCControlSlider before exposing the namespace to the automatically generated -Swift.h header.
OC code has a global call # import "projectName - swift. H", and "the projectName - Bridging - Header. H" and calls the OC code.Is equivalent to the parent class call subclasses, subclasses and calls the superclass.To make the "projectName - Bridging - Header. H" calls the oc class don't call # import "projectName - swift. H.
i used following code showing error that file not found.
#import <SwiftDemo/ViewController-swift.h>
even ViewController.swift file is present in project, help me to sort out problem.
I assume you are trying to use Swift code from Objective-C. Is your product name "ViewController"? If not, then you are not importing the correct header. You should use the Xcode generated header ProductModuleName-Swift.h (replacing ProductModuleName with your specific product name).
In simplest terms, the ProductModuleName is just the name of your application. Example: my app is Application so this header is Application-Swift.h
Also, this header will connect ALL of your Swift to Obj-C, it is not file specific.
Please refer to this immensely helpful documentation piece on Swift/Obj-C interoperability.
You should import a synthetic file generated by the compiler called "<YourProductName>-Swift.h".
I think that this file is generated on the fly, but you can still inspect what it expands to by e.g. creating a dummy Objective-C file that contains just this:
extern int ____BEGIN_SWIFT_IMPORTS____; // dummy marker declaration
#import "<YourProductName>-Swift.h"
(you should of course substitute <YourProductName>)
and then by running the preprocessor on it (Product → Perform Action → Preprocess). All the output after the dummy marker declaration will come from the generated file. It's useful to see how Swift code marked with #objc gets translated to Objective-C declarations.