I am working in a large existing objective-c codebase, writing unit tests at the moment. The project uses instance variables widely.
I wrote a little method to grab the ivar for something. It worked in another unit test in the same project, but isn't working in this case.
Code:
-(id)getObjectForIvarNamed:(NSString *)ivarNameString
{
const char *ivarName = [ivarNameString UTF8String];
Ivar ivarValue = class_getInstanceVariable([textFieldOverlay class], ivarName);
id objectAtIvar = object_getIvar(textFieldOverlay, ivarValue);
return objectAtIvar;
}
The compiler complains at the line starting Ivar ivarValue with the following error:
"Declaration of 'Ivar' must be imported from module 'ObjectiveC.runtime' before it is required"
A web search for this error code comes up with zero hits. Not sure why it works in another file, the headers all look the same between the two XCUnitTest classes.
Never fails, found the answer as soon as I had typed the question. But, I hadn't posted yet. Because this error was impossible to find in a web search, I figured I should post it, so there is an instance out there. The project's class I was testing in the working test class imports the objective-c runtime header, I thought to check the tested class after I typed the question up. I thought it was a header issue but didn't see it in the imported class. Learning moment.
To import the objective-c runtime header and eradicate this warning, add #import <objc/runtime.h> to the header. Done, and done.
Related
I'm trying to make a category for a class that gets defined in a source file I don't have access to, namely RunnerViewController.
The two important files here are iPad_RunnerAppDelegate.h and FilesBrowser.mm. I do not have access to the header file's corresponding source file.
iPad_RunnerAppDelegate.h contains a forward declaration to the RunnerViewController class, and can reference that class with no difficulties.
However, if I try to #include "iPad_RunnerAppDelegate.h" in FilesBrowser.mm and then try to create a category in the same file, it doesn't recognise the class name.
Despite this error appearing, I can still use the RunnerViewController class inside FilesBrowser.mm, I just can't make categories for it.
What's happening here, and how would I fix it?
I've had to do this same thing but it was a long time ago. The problem is that without some indication of where to apply the category, your code cannot work alone. What you need is info to the compiler to let it know where it's going to insert the category.
In FilesBrowser.mm, you will tell it by adding this BEFORE "iPad_RunnerAppDelegate.h":
#interface RunnerViewController: UIViewController // or whatever it actually subclasses
#end
Now the compiler knows that its going to insert the category against an Objective C class named RunnerViewController.
But - you're not completely done. Now you need to insure that the runtime / loader do the class loading and category insertions at the proper time. This is where my knowledge gets a bit fuzzy, so it may not turn out to be a problem at all.
What I believe needs to occur is for the RunnerViewController class to get loaded first, then later at some point the category you wrote applied to it (but this may not be necessary). So if RunnerViewController is statically linked into your app (using a .a archive), then you're good for sure. But if that class is in a dylib loaded dynamically, and your category is statically linked - well that might be a problem. I just don't know how long Apple waits to try and deal with the categories (which by the way the -ObjC link flag is for.
In any case try the code above first and just see what happens. If RunnerViewController is dynamically loaded, and you get some hard runtime error, there is a workaround where you would make your category go into a module/framework that is also dynamically linked, but gets linked after RunnerViewController.
In the end I believe you have a good chance of making this happen.
I'm facing the following problem :
I've declared an protocol in a Swift file and I want to implement it in Objective-C.
I added the #objc annotation to my protocol so Xcode can generate a definition inside my (Project)-Swift.h file and it works fine.
However I cannot import the (Project)-Swift.h file in any of my objc headers since (I suppose) it is generated after those headers has been processed. It is not a big deal because I can simply use this workaround in my objective header file :
#protocol MySwiftProtocolName;
It compiles and works just fine but it will also throws a warning for each class that implements my swift protocol :
Cannot find protocol definition for MySwiftProtocolName
So my question is : Is it possible to force Xcode to generate the (Project)-Swift.h file before it starts processing the regular .h files ?
Thanks
You should be able to add #import "(Project)-Swift.h" into the file where you do implementing this swift protocol. Some times Xcode syntax gets funny and not autocomplete this header name, but in compile time it should work.
If it's not could you please provide more context of what is protocol looks like, as it may be not commutable with Objective-C and so it's not get generated at all.
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.
I have a build error when trying to subclass a custom Objective-C class (a subclass of UIViewController) in Swift.
When I try to subclass in Swift, I get the build errors in the picture below. All of them relate to the use of the word class as an argument in the OCMapper library (where I've opened an issue as well).
Some more notes:
In the project, I both import and use Objective-C code in the Swift code and import and use Swift code in the Objective-C code.
I import the compiled Module-Swift.h only in .m and .mm files and forward declare classes that I need in .h files.
I've attempted to create a Module-Swift-Fixed.h class where I forward declare and/or import the custom Objective-C class headers (as recommended here), but that hasn't made a difference.
Has anyone seen anything like this before or have a solution?
I have as yet not been able to trace where in the language spec this is documented, but I suspect you have come across the same problem that I recently faced in objective-c since moving to Xcode 6.4.
I had a message (method) defined as follows
- (BOOL)canProcessClass:(Class) class {
return [class isSubclassOfClass:[NSSet class]];
}
with the same compile error as you mentioned Expected identifier. The fix was simple - just rename the the class argument to something like classToProcess. Which would give you the following
- (BOOL)canProcessClass:(Class) classToProcess {
return [classToProcess isSubclassOfClass:[NSSet classToProcess]];
}
Hence just rename the arguments in your Swift code to not use the (key)word class and you should be fine.
If anyone can point me to the language spec that documents this I would really appreciate it. As far as I'm aware you shouldn't use Class, but I haven't able to find anything about class except the obvious that it is a message (method) available on classes.
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.