37
votes

I'm trying to work on a mixed Swift and ObjectiveC project with no luck.

My project is made of 6 targets:

  • App
  • Core
  • CoreTest
  • Summary
  • WatchKit Extension
  • WatchKit App

I've been able to add a Swift class that has one target membership (App) and uses ObjectiveC code passing from the App-Bridging-Header.h The "ObjectiveC into Swift code" paradigm works like a charm.

Instead, I'm struggling with the "Swift into ObjectiveC code" paradigm. In the specific case I need to use a new Swift class inside an ObjectiveC .m file that is member of four targets.

What I've been doing so far is this:

  1. Add a new Swift file to the Core project specifying the four memberships (App, Core, Summary, WatchKit Ext)
  2. XCode asks to create -Bridging-Header.h for three projects Core, Summary and WatchKit Ext (App-Bridging-Header.h already exists since it was used previously), and I consent (don't know why it creates those files inside the same group folder where I add my Swift class but nevermind)
  3. I create the Swift class adding the @objc key before class
  4. I go check if Objective-C Generated Interface Header Name is set for all modules, and yes it is
  5. I go check if Defines Module is set to No in all modules and set to Yes in the main project, and yes it is
  6. I write down the imports for the -Swift.h files for all the four targets
  7. Inside my Objective-C class I try to use my new Swift class, apparently it seems to work (even autocompletion)
  8. I try to build the project but it fails saying that the -Swift.h file is not found

I've followed the Mix and Match guide of Apple and many SO threads including this one that seems apparently to address the same problem of mine but it doesn't work.

Also have seen this and this but didn't work.

Have tried also to Delete Derived Data as well as cleaning the whole project several times with no luck.

EDIT 1 : More details

This is the declaration of my Swift class import RealmSwift

@objc class MyClass: Object {}

This is the snippet of usage of the Swift class inside ObjectiveC

MyClass *app = [[MyClass alloc] init];

If I avoid using #import ...-Swift.h the build fails saying that I'm making use of an Undeclared idenfier "MyClass" and this is sound.

If I do use #import ...-Swift.h files it says that module X cannot find module Y and so on.

EDIT 2 : Swift module name

By looking at the SWIFT_OBJC_INTERFACE_HEADER_NAME property of each target I've seen that it is built using this syntax $(SWIFT_MODULE_NAME)-Swift.h do I need to change SWIFT_MODULE_NAME to MODULE_NAME?

EDIT 3 : Imports

This is the set of imports for Swift headers in .m ObjectiveC file. I'm using the module names specified in relative Build Settings properties. Notice that one has '_' character since WatchKit Extension target has a space in the target name.

#import "ProjectName_App-Swift.h"
#import "ProjectName_Core-Swift.h"
#import "ProjectName_Summary-Swift.h"
#import "ProjectName_WatchKit_Extension-Swift.h"

EDIT 4 : Cross visibility of -Swift.h files

I've tryed the following:

  • Added a Swift class only in target App
  • XCode created the Bridging Header
  • Inserted #import "ProjectName_App-Swift.h" in ObjectiveC file used only by App
  • Made use of Swift class inside this file
  • It compiles! And I'm able to use the Swift class

When the Swift class is defined for multiple targets and ObjectiveC file is used by those multiple targets it doesn't work. The build errors are like this: Target X -> "ProjectName_TargetY-Swift.h" file not found. Seems like a target cannot see other targets -Swift.h files.

What am I doing wrong? Thanks for your help

10
Can you edit the way you import the Swift generated header?Sealos
@Sealos added imports to original questionLucioB
I believe you're missing the project name in the header import. Would you mind checking that? Like this: "Project_name_WatchKit_Extension-Swift.h" Also, you could check which module the file is in doing SomeClass.classForKeyedUnarchiver in a swift file and getting that.Sealos
I believe what you say it's true, you can't import other target swift imports into a different target. What you need to do is add the Swift file to the target membership (Like this: i.stack.imgur.com/v3Z0A.jpg). That should solve your issue!Sealos
@Sealos Thank you, I've found the problem it was related to what I've found out in edit 4, I've tried first your solution but it didn't work. However thank you for helping meLucioB

10 Answers

60
votes

The assumption I made in Edit 4 turned out to be the correct one. If a project has multiple targets the "-Swift.h" files cannot be imported all in one .m ObjectiveC file.

So there is one solution that must be adopted and it is to change the SWIFT_OBJC_INTERFACE_HEADER_NAME build setting and making it the same across different targets. To do so change the instruction that generates this property from $(SWIFT_MODULE_NAME)-Swift.h to $(PROJECT_NAME)-Swift.h as explained here

It is also possible to set the Product Module Name setting in Build Settings to be the same across your modules (I set it to $(PROJECT_NAME)), so that the -Swift.h file that is generated has the same name across all modules. This eliminates the need for adding/checking preprocessor macros.

After doing this Clean Build Folder by pressing Alt and going into Product menu. Since name of header is shared among targets now it can be imported once in the .m ObjectiveC file and all targets can benefit from Swift classes.

If after building it still shows the error, ensure that the header can be reached from XCode by Cmd clicking on its name. It should open a file that contains code similar to this:

SWIFT_CLASS("_TtC27ProjectName_Summary11MyClass")
@interface MyClass : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

If need to ensure that those headers are being generated open a terminal and use this command

find ~/Library/Developer/Xcode/DerivedData -name "*Swift.h"

You should see one header for each target

Another issue that happened to me after those changes is that it started giving errors on ObjectiveC code that I didn't touch. The problem was due to the position of the import, as reported here:

Exactly where at the top of a .m file you #import the hidden bridging header can make a difference. The usual sign of trouble is that you get an “Unknown type name” compile error, where the unknown type is a class declared in Objective-C. The solution is to #import the .h file containing the declaration for the unknown type in your Objective-C files as well, before you #import the hidden bridging header. Having to do this can be an annoyance, especially if the Objective-C file in question has no need to know about this class, but it resolves the issue and allows compilation to proceed.

At the very end the code compiles and runs on device and simulator!

13
votes

This is not a solution for the question but since many might end up on this page looking for the problem that the <ModuleName>-Swift.h file is not found (just like me) I'd like to add one more possible reason:

When there is a compiler issue in a swift file the header might not get generated by Xcode and thus the file is missing.

At least in my case the <ModuleName>-Swift.h not found was gone once I fixed the compile error in the Swift file.

8
votes

The solution for me was to import the generated swift header via <> syntax:

#import <OtherTargetWithSwiftClass/OtherTargetWithSwiftClass-Swift.h>
3
votes

I had similar issue and almost ended up spending a whole day figuring out the issue while following suggestions from fellow developers on StackOverflow and few other sources.

I was upgrading from Xcode 7.3.1 to Xcode 8.2.1 and Swift 2.2 to Swift 3.0.1.
In my case, I figured out the PROJECT_NAME-Swift.h file didn't get created at:

~/Library/Developer/Xcode/DerivedData/PRODUCT-NAME-hdkrxixrxbiorwesffpqvefxsrzl/Build/Intermediates/PRODUCT-NAME.build/Debug/PRODUCT-NAME.build/Objects-normal/x86_64/PRODUCT-NAME-Swift.h.

I guess it somehow tries to refer to this header file from the above path and hence the error. I manually copied it. Didn't do clean build after this. This resolved my issue and the swift header file started getting created for further compilation and builds.

2
votes

In my case, I declared import statement into .h file

Then I change

#import "ProjectName_App-Swift.h"

into implementation file(.m)

Clean and restart Xcode Working

1
votes

For my case, it runs fine in Xcode 8 but files with

#import "Project-Swift.h"

has missing imports as described by LucioB

Using Xcode 9 removes those errors

1
votes

I ran into this issue in a project which I had an iOS Application and a SubModule Framework,

The issue occurred when I changed framework build configuration to RELEASE instead

1
votes

If none of the suggested solutions on this page work, it might be good to check the configurations in your framework target. If you create custom configurations in your main applications, you want to have the same configurations present also in your framework targets. If these aren't matching it might result in Xcode not being able to find the correct build settings and therefore the right header files.

0
votes

In my case, I have a workspace with an iOS app subproject, a framework subproject, and a CocoaPods Pods subproject. The "ModuleName-Swift.h file not found" error occurred in my framework subproject because the file hadn't yet been created by the Pods subproject.

I'm not sure if there's a way to tell Xcode about dependencies between subprojects, but I simply re-ordered them in the left-side Project Navigator:

  1. Pods
  2. framework
  3. iOS app

Worked great after that!

0
votes

I develop in a modularized app, the -Swift.h was the ModuleName-Swift.h

I deleted derived data, cleaned build folder, restarted my mac, but what solved was:

pod cache clean ModuleName

and after

run pod update ModuleName