Resolving Swift.h and Bridging-Header.h circular r

2019-05-30 20:21发布

问题:

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?

回答1:

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)!
    }
}