Summary: Problems with C++ header imports when creating a Swift package containing a C++ module and an Obj-C module.
There is no Swift code involved in the modules or the "clients" here - this is just about using Swift Package Manager to create and use the modules. I have often used C++ code from Objective-C clients (and from Objective-C wrappers used by Swift code) in the past using frameworks.
I am trying to use the Swift Package Manager to create a package that contains both C++ and Objective-C APIs. I have a Swift package, with working unit tests, structured as follows: MyPackageCPP
is a C++ implementation; MyPackage
is an objective-C wrapper around that.
The Package.swift file for this is
import PackageDescription
let package = Package(
name: "MyPackage",
platforms: [.iOS(.v13)],
products: [
.library(name: "MyPackageCPP", targets: ["MyPackageCPP"]),
.library(name: "MyPackage", targets: ["MyPackage"]),
],
targets: [
.target(
name: "MyPackageCPP",
path: "Sources/MyPackageCPP",
publicHeadersPath: "include"
),
.target(
name: "MyPackage",
dependencies: ["MyPackageCPP"],
path: "Sources/MyPackage",
exclude: [
"Info.plist",
],
publicHeadersPath: "include"
),
.testTarget( // C++ tests
name: "MyPackageCPPTests",
dependencies: ["MyPackageCPP"]
),
.testTarget( // Obj-C wrapper tests
name: "MyPackageTests",
dependencies: ["MyPackage"]
)
],
cxxLanguageStandard: .gnucxx1z
)
And the directory structure is
MyPackage
|
.- Sources
| |
| .- MyPackage
| | |
| . .- Classes (implementation files for Objective-C wrappers)
| | |
| | .- include
| | MyClass.h (Obj-C header)
| |
| .- MyPackageCPP
| |
| .- Classes (C++ Implementation files)
| |
| .- include
| MyClassCPP.h (C++ header)
| MyPointClass.h (Simple C-header defining a custom struct)
|
.- Tests
|
.- MyPackageCPPTests
| MyPackageCPPTests.mm (XCTestCase-based unit tests for C++ API)
|
.- MyPackageTests
MyPackageTests.mm (XCTestCase-based unit tests for pure Obj-C API)
The key header files:
MyPointClass.h (meant to define a simple C type usable by both the C++ class and its Obj-C wrapper):
struct MyPoint {
double x;
double y;
};
MyClassCPP.h:
#pragma once
#include "MyPointClass.h"
#include <string>
#include <memory>
class MyClassCPP {
public:
MyClassCPP() = delete;
MyClassCPP(const std::string &info);
~MyClassCPP();
bool initialized();
MyPoint modifyPoint(MyPoint point);
private:
struct Impl;
std::unique_ptr <struct Impl> _pImpl;
};
MyClass.h (Obj-C wrapper class)
#import <UIKit/UIKit.h>
#import "MyPointClass.h"
NS_ASSUME_NONNULL_BEGIN
// iOS Platform specific implementation
@interface MyClass : NSObject
-(instancetype)initWithInfoInAString:(NSString*)someInfoInAString;
-(MyPoint)modifyPoint:(MyPoint)point;
@property (atomic, readonly) BOOL initialized;
@end
NS_ASSUME_NONNULL_END
The problem
I've been having problems with the includes. The unit tests work as they are now, but if I change the Obj-C test file from MyPackageTests.mm
to MyPackageTests.m
, I get a build failure 'string' not found.
I understand that Objective-C++ (*.mm) is needed to correctly build objective-C files that use C++, but the direct include chain for MyPackageTests does not include any C++. Rather, it appears from the error that, when building module the Obj-C MyPackage
, all of the header files from the C++ package MyPackageCPP
are included, even though only the C header MyPointClass.h
is explicitly included by the Obj-C MyPackage (via the #import "MyClass.h"
) (The clue being "In file included from <module-includes>
")
I get the same build error when I try to include the Obj-C module into a simple Objective-C test app MyApp
that imports the Obj-C wrapper MyPackage
:
While building module 'MyPackage' imported from /Users/{user}/TestCode/MyPackage/Tests/MyPackageTests/MyPackageTests.m:3:
While building module 'MyPackageCPP' imported from /Users/{user}/TestCode/MyPackage/Sources/MyPackage/include/MyClass.h:3:
In file included from <module-includes>:1: /Users/{user}/TestCode/MyPackage/Sources/MyPackageCPP/include/MyClassCPP.h:5:10: fatal error: 'string' file not found
#include <string>
The question
Are all of the headers in the C++ package necessarily included in the Obj-C wrapper build? (I have tried adding MyClassCPP.h
to the excludes for target MyPackage, to no avail.) Or is there a way around this that will allow me to include only the pure C-headers from the C++ package, when building the Obj-C wrapper MyPackage
(while keeping the C++header available for C++ clients)?