前書き
最近Objective-CのプロダクトにSwiftを導入するようになりました。Swiftの移行の方針として、SwiftとObjective-Cの間に相互互換性を保ったまま移行しようという話でチームはまとまりましたが、落とし穴があったので共有しておきます。
というのもコンパイル時間が遅いのです。どうも差分ビルドがちゃんと働いていないようで、何が原因か調べてみました。
原因
ProductModuleName-Swift.h
が Precompiled Header でインクルードされている
通常Objective-CからSwiftのクラスを利用しようとする場合、自動生成されるヘッダー(ProductModuleName-Swift.h
)をインクルードして使用する。ちなみにこのヘッダーはSwiftで書いたクラスへのインターフェースである。
以下のようなTestClass
を定義すると
class TestClass: NSObject {
var name: String = ""
var children = [String]()
}
自動生成されるヘッダーは以下のようになっている。
ProductModuleName-Swift.h
SWIFT_CLASS("_TtC11ProductModuleName19TestClass")
@interface TestClass : NSObject
@property (nonatomic, copy) NSString * _Nonnull name;
@property (nonatomic, copy) NSArray<NSString *> * _Nonnull children;
@end
ここからが本題だが、私が開発しているプロダクトでは便利だからという理由で、ProductModuleName-Swift.h
をPrecompiled HeaderProductModuleName.pch
でインクルードしてしまっていた。
その結果Swiftのコードを書き換えると、Precompiled Headerに依存するファイル(つまりは全Objective-Cのファイル)を再コンパイルしだすという恐ろしい現象に陥ってしまっていた。
考察
今回の問題、例えば個別のbjective-Cの実装ファイルにインクルードするような話で済むような影響範囲だったら、あまり問題ではないのだが
Service.swift
Objective-CとSwiftを含めた多くの他クラスで使用する
class Service: NSObject {
}
SampleView.swift
一部のViewControllerのクラスで使用する
class SampleView: UIView {
}
例えば上のようなSwiftのクラスがあったとすると、このようにヘッダーファイルに書き出される。
ProductModuleName-Swift.h
SWIFT_CLASS("_TtC11ProductModuleName1Service")
@interface Service : NSObject
@end
SWIFT_CLASS("_TtC11ProductModuleName9SampleView")
@interface SampleView : UIView
@end
普通Swiftオンリーで書いていればSampleView
クラスを修正した際に、Service
クラスには全く影響などない。しかし、同じProductModuleName-Swift.h
に吐き出されるので、Service
クラスに依存するファイルまで全て再コンパイルしてしまう。
Using Swift with Cocoa and Objective-C (Swift 3.0.1)を見ても以下のような記述が書いてあって、個別のクラスごとヘッダーをExportするような方法は書いてないし、これって解決できない問題なんですかね?
When you import Swift code into Objective-C, you rely on an Xcode-generated header file to expose those files to Objective-C. This automatically generated file is an Objective-C header that declares the Swift interfaces in your target. It can be thought of as an umbrella header for your Swift code. The name of this header is your product module name followed by adding "-Swift.h". (You’ll learn more about the product module name later, in Naming Your Product Module.)
これが今のところ解決できない問題ならば、Swiftでモデルやサービスクラスのような上位階層のクラスをSwiftで書き直してそれをObjective-Cで利用するということが難しくなってしまう。
SwiftとObjective-Cに相互の互換性を保ったまま、開発することは難しいのだろうか?