I would like to see examples of how to do this. Apple states that explicit clang modules don't work with C++ interop. ObjC++ has simple interop with C++. Swift does not. And so I'd like to know how to setup my C++ projects to build them as clang modules.
Thanks for all the extra info.
I don’t have a solution to that specific problem yet, but I wanted to post what I’ve achieved so far, primarily to establish a baseline.
Note I was primarily working from Mixing Swift and C++. That was referenced upthread, but I’m including a link here primarily for the benefit of Future Quinn™.
Here’s what I did today:
-
Using Xcode 16.1, I created a new Swift project from the macOS > Command Line Tool template.
-
I chose File > New > File and selected the macOS > C++ File template, naming the file
QContact
. -
Xcode asked whether I wanted to create a bridging header. I agreed to that.
-
I replaced the
QContact.hpp
andQContact.cpp
file content with the code at the end of this post. As you can see, I’m an expert C++ programmer (-: -
In the target editor, I changed the C++ and Objective-C Interoperability build setting to C++ / Objective-C++.
-
I added this line to the bridging header:
#include "QContact.hpp"
-
I replaced
main.swift
with this code:func main() { let quinn = QContact("Quinn “The Eskimo!”") print(quinn.fullName()) } main()
-
I chose Product > Run. It built successfully and printed:
Mr Gumby
OK, so that’s the absolute minimum working.
For my next trick I tried moving the C++ code out into a Swift package. Why a Swift package? Well, I’ll come back to that below.
Here’s what I did:
-
I chose File > New > Package and selected the Multiplatform > Library template. I chose a Testing System of None, just to simplify things. I set the name to
QContacts
. -
I deleted
Sources/QContacts/QContacts.swift
. -
I created a
Sources/QContacts/include
directory. -
I copied
QContact.hpp
to that. -
And
QContact.cpp
toSources/QContacts
. -
In
Sources/QContacts/include
, I created a new module map using File > New, choosing the macOS > Module Map template. -
I replaced the contents with this:
module QContacts { header "QContact.h" export * }
-
I selected my Mac as the run destination and chose Product > Build. It built successfully.
-
I closed the package.
Now I imported the package into the tool project:
-
In the tool project, I remove the include in my bridging header.
-
And deleted this project’s copy of
QContact.hpp
andQContact.cpp
. -
I chose File > Add Package Dependencies, clicked the Add Local button, and selected the
QContacts
package. -
In the resulting sheet I left all the options at their default and clicked Add Package.
-
I added
import QContacts
to the top ofmain.swift
. -
I chose Build > Run. It built successfully and printed the same message as before.
Neat-o!
Now there’s a reason I select a Swift package here. Swift Package Manager has good support for the traditional Unix-y ‘static library plus a bunch o’ headers’ module. That’s less well supported in Xcode. Xcode is much happier building frameworks. I suspect it’s possible to get Xcode to do what you want, but I didn’t have time to explore that today.
OTOH, if you’re happy building a framework then there’s actually sample code for that. See the links under the Language Interoperability with C++ topic.
Remember that, with mergeable libraries, a framework build structure no longer means that you have to link to the code dynamically. You can create a framework and then merge the framework [1] into your main executable.
Alternatively, you might find that a Swift package is something you enjoy working with.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] Well, everything except a tiny stub.
#ifndef QContact_hpp
#define QContact_hpp
#include <string>
class QContact {
public:
QContact(std::string fullName);
~QContact();
std::string fullName() const;
private:
std::string _fullName;
};
#endif /* QContact_hpp */
#include "QContact.hpp"
QContact::QContact(std::string fullName) {
this->_fullName = fullName;
}
QContact::~QContact() {}
std::string QContact::fullName() const {
return this->_fullName;
}