Resolving Swift.h and Bridging-Header.h circular references involving enums - objective-c

I have an Objective-C header that has to be used by a Swift class. However, this header has to use the Swift.h file for an enum declared in a Swift file. In other words, the setup is as follows:
MPViewController.h
#import "MyProject-Swift.h"
#interface MPViewController: UIViewController
#property (nonatomic, assign) MPSomeEnum theEnum;
...
#end
MyProject-Bridging-Header.h
...
#import "MPViewController.h"
...
SomeEnum.swift
#objc enum MPSomeEnum: Int {
...
}
When compiling the code, I get three errors:
'MyProject-Swift.h' file not found
Failed to emit precompiled header [Xcode DerivedData folder]/[...]/MyProject-Bridging-Header-swift_[...].pch for bridging header [Project folder]/MyProject-Bridging-Header.h
Unknown type name 'MPSomeEnum'
Am I correct to assume that this stems from the circular reference between MyProject-Swift.h and the bridging header MyProject-Bridging-Header.h? From looking at a similar question one solution is to use forward declaration. However, it doesn't seem possible to forward declare an enum, so perhaps the only way to do this is to move the enum definition to an Objective-C file altogether?

TL&DR; As you suspected, you need to either move the enum declaration to Objective-C, or migrate the class to Swift.
Forward declarations of enums is possible in Objective-C:
#property SomeEnum someProperty;
- (void)doSomethingWithEnum:(enum SomeEnum)enumValue;
However correct Cocoa enums are typedefs to NSInteger: typedef NS_ENUM(NSInteger, MyEnum), and the enum keyword doesn't hold enough information for how much space to allocate when using it, so you'll get into all kind of compiler error when you want to use declarations like this. Thus an enum declared in Swift is not forward declarable in Objective-C.
Now, if you really want to keep the enum definition in Swift, you could use a workaround, and declare it as NSInteger in Objective-C, while providing a specialized property in Swift:
// NS_REFINED_FOR_SWIFT imports this in Swift as __theEnum
#property(nonatomic, assign) NSInteger theEnum NS_REFINED_FOR_SWIFT;
extension MPViewController {
// we provide a wrapper around the Objective-C property
var theEnum: MPSomeEnum {
// this uses a forced unwrap, beware :)
return MPSomeEnum(rawValue: theEnum)!
}
}

Related

How do I access my Objective-C constants in Swift?

My project currently has a file containing many constants that I use in a variety of places throughout my codebase. I'm writing a class extension in swift and need to access some of these constants.
I have something like this defining my constants in Objective-C:
//
// AppConstants.h
//
#import <Foundation/Foundation.h>
#interface AppConstants : NSObject
extern NSString *const SOME_CONSTANT_IN_APP_CONSTANTS;
#end
I've added my AppConstants.h to the bridging header, but when I try and do:
someDictionary.objectForKey(PROPERTY_CALL_CAMPUS_SECURITY_TITLE)
I get:
Use of unresolved identifier 'PROPERTY_CALL_CAMPUS_SECURITY_TITLE'
Is there a way to access constants defined in Objective-C in my Swift code?

Swift/ObjC circular import

I am working on an existing large codebase that is predominately Objective-C but is in the process of converting to Swift.
New classes are being implemented in Swift, but some of these classes need to be accessed from existing ObjC code. In an attempt to follow both ObjC and Swift best practices, the new classes do not have a prefix, but are defined with a prefix for ObjC.
/**
Create contrived class that is named XXClassA in ObjC and ClassA in Swift.
*/
#objc(XXClassA) class ClassA: NSObject {
let foo = "bar"
}
So far this has been working great; Swift code uses ClassA() and ObjC uses [[XXClassA alloc] init]. Once all ObjC code that references XXClassA is removed, #objc(XXClassA) can also be removed from the Swift class.
Unfortunately this breaks down if you have an ObjC class reference XXClassA and then the Swift code attempts to use that new class inside of ClassA.
Say I create an ObjC class, named XXClassC and it instantiates itself using an XXClassA (which is really the Swift class ClassA)
//
// XXClassC.h
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
#class XXClassA;
#interface XXClassC : NSObject
-(instancetype)initWithA:(XXClassA *)classA;
#end
NS_ASSUME_NONNULL_END
The circular reference is now in place. If I attempt to use XXClassC from inside of the ClassA, the initializer is not available.
If I redefine ClassA as this instead, all is well again.
class XXClassA: NSObject {
let foo = "bar"
}
I understand why this is happening and the fix I have in place, however I want to continue to use this pattern of prefixed ObjC classes. Any ideas on how to avoid the circular import, but also keep the naming convention?
Full code sample here: https://gist.github.com/justAnotherDev/78483f9d94e40fd90c38
I'm having the same problem. My workaround is:
Use an objc-style prefixed name for the Swift class.
Add a typealias: typealias ClassA = XXClassA.
Always use the unprefixed name from Swift code.
With this pattern it's at least easy to remove the prefix when the last objc dependency is gone.

declare obj-c class interface that contain c++ class type ivar

Currently I am working on a cocos2d+Box2D project so I have deal with some Objective-C++ code.
And I am facing to such situation:
#import "cocos2d.h"
#import "Box2D.h"
#interface BasicNode : CCNode {
#private
ccColor3B _color;
b2Body *_body;
b2Fixture *_shape;
}
b2Body and b2Fixture are C++ class that defined in Box2D.h
It works if the implementation of BasicNode is named BasicNode.mm.
But if I have another file named Game.m that is using BasicNode and import BasicNode.h, it won't compile because .m file is Obj-C file and does not know about C++ code.
So I decided to move #import "Box2D.h" into implementation file and only keep type declaration in head file (this is exactly what header file should contain).
But how do I do it? They are C++ class type but they are actually just a pointer so I wrote some helper macro
#ifdef __cplusplus
#define CLS_DEF(clsname) class clsname
#else
#define CLS_DEF(clsname) struct clsname; typedef struct clsname clsname
#endif
CLS_DEF(b2Body);
CLS_DEF(b2Fixture);
It works, only if CLS_DEF(b2Body) is appear once only. Otherwise compiler will find multiple type declaration for a same name even they are the same. Than I have to change to
#ifdef __cplusplus
#define CLS_DEF(clsname) class clsname
#else
#define CLS_DEF(clsname) #class clsname
#endif
And it is working now.
But I don't think it is a great idea that I declare a C++ class type as an Obj-C class especially I am using ARC.
Is any better way do deal with it? And I don't really want to make something like this
#interface BasicNode : CCNode {
#private
ccColor3B _color;
#ifdef __cplusplus
b2Body *_body;
b2Fixture *_shape;
#else
void *_body;
void *_shape;
#endif
}
Edit: Also please tell me will my tweak way introduce any problem?? by making C++ class ivar looks like Obj-C class for other pure Obj-C code.
One simple solution is to rename Game.m to Game.mm.
There are a couple of ways. If you can rely on using the Objective-C 2.2 runtime's features, you can add ivars in class (category) extensions. This means you can add ivars in your class's .mm file, and keep the .h file clean of any C++ stuff.
If you need to support older versions of the runtime, there are a few ways to do it which are better than #ifdefing. In my opinion, the best way is to use the 'pimpl' idiom which is common in C++ - you forward declare an implementation struct in your header, and add an ivar which is a pointer to such a struct. In your class's implementation (.mm), you actually define that struct with all its C++ members. You then just need to allocate that implementation object in your init... method(s) with new and delete it in dealloc.
I've written up the pimpl idiom as it applies to cleanly mixing Objective-C and C++ in this article - it also shows some other potential solutions which you could consider.
With Xcode 5, you don't have to declare instance variables in the header file, you can just declare them in the implementation file. So your BasicNode header file is not "contaminated" with C++.
You can use "struct" instead of "class" in C++. The only difference is that in a class all members are private by default, while in a struct they are public by default. But you can do everything with a struct that you can do with a class. That way you can write for example
struct b2Body;
struct b2Fixture;
outside your interface, and
{ ...
struct b2Body* _body;
...
}
in your interface.

#class for typedef enum?

In one header file, I have something like:
// PasscodeInputViewController.h
typedef enum {
PasscodeInputModeOn, // set passcode
PasscodeInputModeEnter, // enter passcode
PasscodeInputModeChange, // change passcode
PasscodeInputModeOff // turn off passcode
} PasscodeInputMode;
In another header file, I declare a method that takes an argument of type PasscodeInputMode:
#import "PasscodeInputViewController.h"
- (void)presentPasscodeInputWithMode:(PasscodeInputMode)mode;
As you can see, I use #import "PasscodeInputViewController.h" as above so that PasscodeInputMode is recognized, but is there a #class equivalent for typedef enum?
No, there isn’t an equivalent.
Enumerations in Objective-C are the same as enumerations in C. Since the underlying type of an enumeration is implementation-dependent (e.g., it could be char or int), the compiler must know the complete declaration of the enumeration.
That said, a type specifier
enum nameOfEnum
without listing the enumeration members is valid C provided it appears after the type it specifies is complete, i.e., enum nameOfEnum { … } must appear beforehand in the translation unit.
In summary: There’s no forward declaration of enumerations, only backward references.
#Caleb, #Bavarious:
Most recent way (Jan, 2017) to forward declare the enum (NS_ENUM/NS_OPTION) in objective-c is to use the following:
// Forward declaration for XYZCharacterType in other header say XYZCharacter.h
typedef NS_ENUM(NSUInteger, XYZCharacterType);
// Enum declaration header: "XYZEnumType.h"
#ifndef XYZCharacterType_h
#define XYZCharacterType_h
typedef NS_ENUM(NSUInteger, XYZEnumType) {
XYZCharacterTypeNotSet,
XYZCharacterTypeAgent,
XYZCharacterTypeKiller,
};
#endif /* XYZCharacterType_h */`
Similar question Forward-declare enum in Objective-C
Forward declaration of classes is necessary to enable two classes to refer to each other. It's not uncommon to have two classes that are defined in terms of each other:
#class ClassB;
#interface ClassA : NSObject
{
ClassB *objectB;
}
#end
#interface ClassB : NSObject
{
ClassA *objectA;
}
#end
There's no way to make that compile without the forward declaration.
The same is not true of enumerations. enum just creates a set of named values... you can't include one enumeration in the definition of another. Therefore, there's never a need to forward declare an enumeration.
I think what you want is a class that has PasscodeInputMode as a property of it. That way you can be passing around an instantiated object of that class, and can set/get that property, and do other object-like-things with it (assume that's why you'd want to find a "#class equivalent"

how to return C++ pointer in objective-C++

I have the following objective-C++ header with the simple method to return this pointer.
#interface MyObj
{
MyCPPObj * cpp;
}
-(MyCPPObj *) getObj;
I have created the simple method
#implementation MyObj
-(MyCPPObj *) getObj
{
return cpp;
}
Everything seems to work until I actually try to use the object in another file
newObj = [createdMyObj getObj];
It complains: error: cannot convert 'objc_object*' to 'MyCPPObje *' in initialization.
It seems that the method is return an objective-c object, but I specifically requested a C++ pointer.
MyCPPObj is an honest C++ class:
class MyCPPObj
{
public:
int x;
}
How can I fix that?
On my 10.6.3 machine, the following combination worked without any problem: aho.h
#import <Foundation/Foundation.h>
class MyCPPObj{
};
#interface MyObj:NSObject
{
MyCPPObj * cpp;
}
-(MyCPPObj *) getObj;
#end
and aho.mm
#import <Foundation/Foundation.h>
#import "aho.h"
void foo(){
MyObj*objcObj=[[MyObj alloc] init];
MyCPPObj*cppObj=[objcObj getObj];
}
Two pitfalls you might have fallen into:
Unlike C++, a class in Objective-C which doesn't inherit from NSObject won't work. (Well, you can make it work, but you don't want that usually.) Note the line #interface MyObj:NSObject.
To use NSObject, do #import <Foundation/Foundation.h>
Don't forget to use the extension .mm for Objective-C++ files.
Most likely you have forgotten to #import the header file with the #interface into the .mm file where you use getObj.
The error states what happens, and JeremyP is right on the money. When you forget to include a header file with the prototypes of the selectors, the compiler assumes the selector returns an object of type id. Well id is a typedef to objc_object*, which is incompatible with your C++ class. To fix the error, you simply need to include your header file in the file where you called getObj.