C++ライブラリをiOSプロジェクトに追加する方法について
XCode12.4でC++ライブラリをiOSアプリで使う方法について説明します。色々試してみて、IDEの支援を受けられる等の観点から落ち着いた方法としては、Swift Package Manager(以下SPM)を使った方法です。
CPMはバイナリとしてxcframework形式をサポートするので、まずC++ライブラリをxcframeworkとしてビルドします。C++ライブラリの構造は以下のような簡単なものです。
.
├── include
│ └── native_add.h
└── native_add.cpp
#include "include/native_add.h"
int32_t native_add(int32_t a, int32_t b) {
return a + b;
#pragma once
#include<stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
int32_t native_add(int32_t a, int32_t b);
#ifdef __cplusplus
}
これをiOS甩にビルドするために、build
ディレクトリを作ってそこにCMakeLists.txt
などを用意します。
.
├── build
│ ├── bin
│ ├── build.bash
│ └── ios
│ ├── CMakeLists.txt
│ └── ios.toolchain.cmake
├── include
│ └── native_add.h
└── native_add.cpp
cmake_minimum_required(VERSION 3.15)
project(native_add)
set(CMAKE_CXX_STANDARD 14)
FILE(GLOB SRC ../../*.cpp)
add_library(native_add STATIC ${SRC})
#!/bin/bash
cmake -Sios -G Xcode ./ios -B bin/ios_x86_64 -DPLATFORM=SIMULATOR64 -DCMAKE_TOOLCHAIN_FILE=ios.toolchain.cmake
cmake --build ./bin/ios_x86_64
cmake -Sios -G Xcode ./ios -B bin/ios_arm64 -DPLATFORM=OS64 -DCMAKE_TOOLCHAIN_FILE=ios.toolchain.cmake
cmake --build ./bin/ios_arm64
xcodebuild -create-xcframework \
-library bin/ios_arm64/Debug-iphoneos/libnative_add.a -headers ../include \
-library bin/ios_x86_64/Debug-iphonesimulator/libnative_add.a -headers ../include \
-output libnative_add.xcframework
ios.toolchain.cmake
は次のように取得します。
curl -OL https://raw.githubusercontent.com/leetal/ios-cmake/master/ios.toolchain.cmake
この状態で次のようにbuild.bash
を実行するとxcframework形式のライブラリlibnative_add.xcframework
が出来ます。
$ cd path/to/project/build
$ ./build.bash
C++の方は準備ができたのでSPM形式のパッケージを作ります。XCodeのFile > New > Swift Package...
を選択して作ります。ここでのパッケージ名はSPMSample
にします。
パッケージが出来たので、SPMSample.swift
を削除して次のような構成にします。SPMはターミナルからディレクトリを作ったり、ファイルを追加してもXCodeに反映されるようなので、ターミナルで作っていきます。
.
├── Frameworks
│ └── libnative_add.xcframework
├── Package.swift
├── README.md
├── Sources
│ └── SPMSample
│ ├── dummy.c
│ └── include
│ ├── module.modulemap
│ ├── native_add.h
│ └── shim.h
└── Tests
...
libnative_add.xcframework
は先程ビルドしたもので、Sources/SPMSample/include
のnative_add.h
はC++プロジェクトから持ってきたものです。dummy.c
はSPM的に必要なだけで空ファイルです。module.modulemap
はC言語(Objective-C)をモジュール化するもので、これを用意するとimport文でSwiftから取り込めるようになります。中身はこんな感じです。
module SPMSample {
umbrella header "shim.h"
export *
link "native_add"
}
shim.h
はヘッダーファイルをまとめるものだと思います(よく分かってない)。なのでnative_add.h
をインクルードしておきます。
#ifndef shim_h
#define shim_h
#include <native_add.h>
#endif /* shim_h */
こうした状態で、Package.swift
を次のように書いておけばSPMSample
はlibnative_add.a
とリンクされます。
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "SPMSample",
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "SPMSample",
targets: ["SPMSample"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.binaryTarget(
name: "libnative_add",
path: "Frameworks/libnative_add.xcframework"),
.target(
name: "SPMSample",
dependencies: ["libnative_add"],
// 今回のコード例では不要ですが、C++の標準ライブラリを使うときはlibc++をリンクする必要があります。
// linkerSettings: [.linkedLibrary("c++"),
]),
/*
.testTarget(
name: "SPMSampleTests",
dependencies: ["SPMSample"]),
*/
]
)
テストコードはこのままだとエラーになるので、とりあえずコメントアウトしてます。以上で、SPMが出来ました。SPMはgitリポジトリにタグを付けておかないとアプリから使えないので、タグだけ適当に付けておきます(プロジェクトを作ったときにgitプロジェクトも作られた前提です)。
$ cd /path/to/project
$ git add .
$ git commit -m "Mod."
$ git tag 0.0.1
最後にこのライブラリをiOSアプリから読んで終わりにします。iOSアプリはXCodeからFile > New > Project...
を選んでiOSのAppを選択します。今回はSwiftUIにもチェックをいれています。プロジェクトが出来たらFile > Swift Packages > Add Package Dependency...
を選択して、プロジェクトまでのパスをfile://~
で指定します。
追加出来たら、SPMSample
をインポートしてnative_add
を呼び出しを追加します。
import SwiftUI
+ import SPMSample
struct ContentView: View {
var body: some View {
+ Text("23 + 24 = \(SPMSample.native_add(23, 24))")
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
ビルドしてシミュレーター、実機で次のように表示されたら成功です。
Discussion