iOS 用 Static ライブラリを C で作って Swift で使う
まえがき
C 言語で書いたオリジナルライブラリを、Swift の iOS アプリから呼び出す方法についてです。
C++ の方が書きやすいかとは思いますが、調べた限りでは色々と面倒そうだったので、シンプルにできそうな C を使うことにしました。
まあ信号処理とかだと、C でも C++ でもそう大きくは変わらないと思うので・・。
分かれば簡単なのですが、そのものの情報が見つからず苦労したので書いておきます。
ライブラリソースコード作成
・Mac の Command Line Tool としてプロジェクト生成
まずは Xcode でライブラリのソースコードを作ります。
プロダクト名は例えば myLib とします。
Create New Project → macOS → Command Line Tool
Product Name:myLib
Language:C
// myLib.c
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
int32_t myFunction(bool flag);
int32_t myFunction(bool flag) {
if (flag) {
printf("Hello from myFunction!\n");
} else {
printf("Goodbye from myFunction!\n");
}
return (int32_t)flag + 1;
}
ライブラリソースコード例
通常の C 言語による開発~デバッグを行っておきます。
必要であれば、デバッグ用のライブラリ呼び出し C コードも一緒に書きます。
ここまではどう作っても構いません。必要なのはソースコードのみです。
Mac 特有のライブラリ等を使わない限り、Windows 上で開発してもよいでしょう。
一部定義ファイルの違い等はありますが、Windows 上でデバッグした後、Mac でエラーだけ対処すれば問題ないかと思います。
他で機能デバッグ済みであれば、 Static ライブラリとして作っても構いません。(run してもビルドのみで実行されないだけ?のようですが)
Create New Project → macOS → Library
Product Name:myLib
Framework:None
Type:Static
実際、今回仕事用に開発した信号処理ライブラリは、Windows でデバッグまでを行い、Mac では Static ライブラリとしてプロジェクト作成をしました。
ライブラリコンパイル&アーカイブ
ライブラリのソースコードが完成したら、iOS 用ライブラリを生成します。
IDE でコンパイルするとリンクがうまくいかなかったので、コマンドラインツールを使います。
・ライブラリソースがあるフォルダーを Finder で開く
Xcode からであれば、プロジェクトビューのソースコード上で
右クリック(control+クリック) → "Show in Finder"
で開けます。
・ターミナルで SDK の場所を確認
ターミナルは Finder をカラム表示等にし、フォルダ上で
右クリック → "フォルダに新規ターミナル"
で開けます。
メニューに項目がない場合は、
システム設定 → キーボード → キーボードのショートカット
→ サービス → ファイルとフォルダ 「フォルダに新規ターミナル」
の項目をチェックしておきます。
xcrun --show-sdk-path --sdk iphoneos
xcrun を実行し、表示されるパスを使います。
iOS17.0 では以下のパスでした。
"/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.0.sdk"
バージョンによって変わります。
xcrun: error: SDK "iphoneos" cannot be located
が出る場合は、
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer/
としてから xcrum します。
-isysroot “SDKパス” を指定してコンパイルします。
gcc -c -arch arm64 -isysroot "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.0.sdk" myLib.c -o myLib.o
.a ファイルにアーカイブします。
ar rcs libmyLib.a myLib.o
これで、iOS アプリから呼び出せるライブラリファイル、libmyLib.a ができました。
Swift 呼び出し側
ライブラリの作成が終わったら、呼び出し側を作ります。
・iOS アプリプロジェクト作成
普通に、iOS アプリのプロジェクトを作成します。
プロジェクト名:SwiftTest
・ライブラリをプロジェクトに追加
.a ファイルをプロジェクトにドロップし、追加するプロジェクトをチェックします。
自動的に Link Binary With Libraries 設定に追加されるので後で確認します。
・Swift 用ブリッジヘッダー作成
ヘッダーファイルを作成し、プロジェクトに追加します。
どう作っても良いですが、例えばプロジェクトビューで
右クリック → “New File”
で BridgingHeader.h として作成します。
// BridgingHeader.h
#ifndef BridgingHeader_h
#define BridgingHeader_h
// 必要なヘッダーをインクルード
#include <stdint.h>
#include <stdbool.h>
extern int32_t myFunction(bool flag);
#endif /* BridgingHeader_h */
他で作ってプロジェクトにドロップしても構いません。
・Bridging Header 設定
ヘッダーファイルを指定します。
TARGETS Build Settings → Objective-C Bridging Header
に BridgingHeader.h と入れます。
サーチで「bridge」と入れてしまうと出てこないので注意しましょう。
「bridg」もしくは「bridging」です。
Library/ 等フォルダ分けして置きたい場合は 上記 Bridging Header ファイル設定も Library/BridgingHeader.h にします。
・ライブラリの確認
Build Phases → Link Binary With Libraries に libmyLib.a が入っているか確認します。
自動的に入るはずですが、入っていなければ入力します。
・Swift ソース例
アプリ仕様
-ボタンをタッチで表示が "Hello" 、"Goodbye" に切り替わる
-ボタンの状態(Hello:true 、Goodbye:false)を引数としてライブラリ呼び出し
-ライブラリからは Hello:2、Goodbye:1 が返ってくる
-その返り値によって、ボタンの下にテキスト表示
// ContentView.swift
import SwiftUI
struct ContentView: View {
// @Stateでトグルの状態を管理
@State private var isHelloState = true
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
.padding()
Button(action: {
// トグルの状態を反転
isHelloState.toggle()
print("Button Clicked. Flag is \(isHelloState ? "True" : "False")")
}) {
Text(isHelloState ? "Hello" : "Goodbye")
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
// 条件分岐でViewを返す
if let message = getMessage() {
message
}
}
}
// myFunctionの結果に応じたViewを返す
private func getMessage() -> Text? {
let ret = myFunction(isHelloState)
print("return from myFunc : \(ret)") // デバッグ用表示
switch ret {
case 1:
return Text("Goodbye from myFunc")
case 2:
return Text("Hello from myFunc")
default:
return Text("No Reply from myFunc")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
実機デバッグで動くことを確認します。
SwiftUI 中では、メソッドやクロージャー内以外では print 文が使えずエラーになります。
そのため、getMessage() を呼び出す形にしてその中でデバッグ用に print してます。
print 入れると動かなくなるとか、C っぽいですね。(違)
let _ = Self._printChanges()
辺りも使えるのかな?
あとで勉強します。(u_u)
アプリ動作例
ボタンをタッチする度にボタンのテキストが変わり、ライブラリの返り値によってその下のテキストも変わります。
・Console 出力(デバッグ出力)
デバッグ出力には、ライブラリ内の printf の結果も表示されます。
Hello from myFunction!
return from myFunc : 2
Button Clicked. Flag is False
Goodbye from myFunction!
return from myFunc : 1
Button Clicked. Flag is True
Hello from myFunction!
return from myFunc : 2
あとがき
ライブラリ生成は、IDE 上で行う方法がわからずコマンドラインを使いました。
IDE 上で完結する方法があれば教えてください。m(._.)m
さて、今年の記事 Up は多分これが最後になるかと思います。
皆さん、今年もお世話になりました。
良いお年をお迎えください! (^-^)ノ
タイトル画像モデル:綾夏
The title image was created using Adobe Generative Fill based on this picture.
この記事が気に入ったらサポートをしてみませんか?