swiftcを使ったUIKitコードのコンパイル: フレームワーク問題の解決方法
Swiftのプログラムをコンパイルする際には、swiftc
というコマンドラインツールがよく利用されます。swiftc
は非常に強力なツールであり、多様なコンパイルオプションや診断機能を提供しています。しかし、UIKit
のように特定のフレームワークを利用する場合には、特別な設定が必要になることがあります。
問題の背景
SwiftUI
を使用した場合、大抵は問題なくコンパイルできます。しかし、UIKit
のコードをコンパイルするときにエラーが発生することがあります。
以下はmain.swift
として実装されたシンプルなUIViewController
のサンプルコードです。
import UIKit
final class ViewController: UIViewController {
private var isShow = false
override func viewDidLoad() {
super.viewDidLoad()
}
private func toggleFunction() {
isShow.toggle()
}
private static func staticFunction() {
// do something
}
}
このコードはiOSアプリケーションの一部として非常にありそうなものです。しかし、このコードを単体でswiftc
を使ってターミナル上でコンパイルしようとすると、次のようなエラーが発生します。
コンパイルを行うコマンド
$ swiftc main.swift -emit-sil -o sil.swift
発生するエラー
error: no such module 'UIKit'
問題の解決方法
この問題の根本的な原因は、swiftc
がデフォルトではmacOS
のためのコンパイルを行っているため、UIKit
を認識できないからです。UIKit
のフレームワークを認識させるには、-target
や-sdk
、そして-F
といったフラグを使用して、適切なターゲット指定とSDKのパスをコマンドに追加する必要があります。
具体的には、次のコマンドを実行することでエラーを解消することができます。
swiftc -target arm64-apple-ios17.0 -sdk $(xcrun --sdk iphoneos --show-sdk-path) -F $(xcrun --sdk iphoneos --show-sdk-platform-path)/Developer/Library/Frameworks main.swift -emit-sil -o sil.swift
コマンドの詳細
-
-target arm64-apple-ios17.0
- コンパイルのターゲットをiOSの
arm64
アーキテクチャに設定します。17.0
の部分はiOSのバージョンに応じて変更可能です。
- コンパイルのターゲットをiOSの
-
-sdk $(xcrun --sdk iphoneos --show-sdk-path)
- 使用するSDKのパスを指定します。この部分で実際のiOS SDKのパスが動的に取得されます。
-
-F $(xcrun --sdk iphoneos --show-sdk-platform-path)/Developer/Library/Frameworks
- iOSのフレームワークのパスを指定します。これにより、
swiftc
はUIKit
のようなiOS専用のフレームワークを正しく認識できます。
- iOSのフレームワークのパスを指定します。これにより、
-
-emit-sil -o sil.swift
-
sil
を出力し、結果をsil.swift
ファイルに保存します。sil
を出力する必要がない場合は削除しても問題ありません。
-
まとめ
Swiftのコマンドラインツールswiftc
を使用し、UIKit
を含んだコードをコンパイルする際には、ターゲットやSDK、フレームワークのパスを適切に設定することが必要です。上記のコマンドを使用することで、この問題を解決することができます。
開発環境
- Swift compiler version info:
Apple Swift version 5.9 (swiftlang-5.9.0.128.108 clang-1500.0.40.1)
- Xcode version info:
Xcode 15.0 Build version 15A240d (Release Candidate)
余談
上記のコマンドで生成されたsil.swift
を見ると、UIViewController
が暗黙的にMainActor
であることがわかります。以下にコードの一部を示します。
@objc @_inheritsConvenienceInitializers @MainActor final class ViewController : UIViewController {
@_hasStorage @_hasInitialValue @MainActor private final var isShow: Bool { get set }
@MainActor override final func viewDidLoad()
@MainActor private final func toggleFunction()
@MainActor private static func staticFunction()
@MainActor override dynamic init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)
@MainActor required dynamic init?(coder: NSCoder)
@objc deinit
}
Discussion
SIL: Swift Intermediate Language (SIL)
【iOS】Xcodeのビルドの仕組みを知り、ビルド時間を短縮する方法を探る - Qiita