2015年12月17日木曜日
レガシーな Objective-C プロジェクトを Swift なプロジェクトに変換する
ここで言うレガシーなObjective-Cプロジェクトの定義とは
こんな由緒正しいiOSのプロジェクトを未だにメンテしている人もなかなかいないのかと思いますが、もしいらっしゃいましたらそんな方のためにSwiftなプロジェクトに変換していく方法をメモしておきます。
clang moduleを有効にしてdyldを使えるようにする設定
Swift化されるターゲットごとにブリッジングヘッダを作成
Releaseビルド以外に対して最適化レベル設定
が自動的に行われるので後はせいぜいブリッジングヘッダに使ってるObjective-Cのヘッダを書き込む程度ですみます。
たったのこれだけでプロジェクトの根っこがSwiftな状態になります。簡単ですね。
まずUIApplicationについてはInfo.plistの
UIWindowについてはMain.storyboardを使わなければ勝手にUIWindowを差し込まれることはなくなるので問題ないのですが、それでは困ると言う場合は
- iOS 7時代 (Xcode 5) より前に作成されたプロジェクトである
- Swiftのコードを一行も含んでいない
IOS_DEPLOYMENT_TARGETが8.0よりも小さい (7.xをサポートしている)
こんな由緒正しいiOSのプロジェクトを未だにメンテしている人もなかなかいないのかと思いますが、もしいらっしゃいましたらそんな方のためにSwiftなプロジェクトに変換していく方法をメモしておきます。
■前提条件
まずIOS_DEPLOYMENT_TARGETを8.0以上にしましょう。IOS_DEPLOYMENT_TARGETを8.0以上にすることでdynamic frameworkおよびclang moduleが使えるようになるため、Objective-CとSwiftの間の垣根が非常に低くなります。■ケース1: 根っこはObjective-Cのまま、Swiftのファイルを追加
普通にSwiftのファイルを追加したらXcodeが上手いことしてくれます。具体的にはclang moduleを有効にしてdyldを使えるようにする設定
CLANG_ENABLE_MODULES = YES;LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";Swift化されるターゲットごとにブリッジングヘッダを作成
SWIFT_OBJC_BRIDGING_HEADER = "myapp/myapp-Bridging-Header.h";Releaseビルド以外に対して最適化レベル設定
SWIFT_OPTIMIZATION_LEVEL = "-Onone";が自動的に行われるので後はせいぜいブリッジングヘッダに使ってるObjective-Cのヘッダを書き込む程度ですみます。
■ケース2: 根っこからSwiftにする
「根っこからSwift」とは要するにmain.m(C言語のmain関数)を持たないアプリにしたいということですが、これも実は思ったより簡単にできます。- AppDelegate.swiftファイルを作成する
- 既存のObjective-Cで書かれたAppDelegateを継承て新しくswiftなAppDelegateを作成して、
@UIApplicationMainアノテーションを付ける - main.mを消す
たったのこれだけでプロジェクトの根っこがSwiftな状態になります。簡単ですね。
■さらにSwift化をすすめる
とりあえずAppDelegate.swiftを作ることで根っこはSwiftになるのですが、せっかくだからAppDelegateをまるごとSwiftにしてしまいたいものです。ここでもし、- UIWindowをAppDelegate内部でカスタマイズしている
- UIApplicationをmain.mでカスタマイズしている
まずUIApplicationについてはInfo.plistの
NSPrincipalClassを変更することで任意のカスタムクラスに差し替える事が可能です。参考はこちら: http://stackoverflow.com/questions/31642956/how-to-detect-all-touches-in-swift-2 UIWindowについてはMain.storyboardを使わなければ勝手にUIWindowを差し込まれることはなくなるので問題ないのですが、それでは困ると言う場合は
awakeFromNibとかを実装してその中で@UIApplicationMain
class MyAppDelegate: NSObject, UIResponder, UIApplicationDelegate {
var window: UIWindow?
override func awakeFromNib() {
super.awakeFromNib()
guard let defaultWindow = self.window else {
fatalError("Something is wrong")
}
let window = MySuperDuperWindow()
window.rootViewController = defaultWindow.rootViewController
self.window = window
}
}
とかすれば動くと思います。多分。