Kotlin で iOS アプリを作る
概要
Kotlin で iOS アプリを作成します。 開発環境には Android Studio を使用し、Intel Multi-OS Engine を使って iOS アプリに変換して実行します。
確認環境
- OS X El Capitan (10.11.6)
- Xcode 8.0
- Android Studio 2.2.2
- Multi-OS Engine Plugin 1.2.1
- Kotlin 1.0.4
- buildToolVersion 24.0.1
- compileSdkVersion 24
参考情報
- Intel® Developer Zone
- Intel Multi-OS Engine の公式文書
- GitHub - multi-os-engine/moe-samples-kotlin: Multi-OS Engine: Kotlin Samples
- Kotlin サンプル
解説
前提
Xcode, Android Studio, Kotlin Plugin はインストール済みとする。
Multi-OS Engine のインストール
Android Studio に Multi-OS Engine (MOE) Plugin をインストールする。
プロジェクトの作成
プロジェクトの作成手順は通常の Android プロジェクトと同じ。
Multi-OS Engine モジュールの作成
iOS アプリは Multi-OS Engine モジュールとして作成する。
モジュールを作成したら、実行可能な状態になっているので Run 'ios' を選択して実行する。 実機での実行は Developer Team ID が必須な様子(How to configure the Development Team id with MOE 1.2.0 - Support - Multi-OS Engine)。 Simulator では実行された。
Kotlin に変換
Multi-OS Engine モジュールのソースコードは Java で書かれている。 Java を Kotlin に変換する。
まずは、build.gradle に Kotlin の設定を加える。 以下を行うとプロジェクトと app (Android アプリ) モジュールに Kotlin の設定が追加される。
ios (Multi-OS Engine モジュール) の build.gradle にはエディタで以下を追記する。
apply plugin: 'kotlin' dependencies { ... compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" }
次に、Java のソースコードを Kotlin に変換する。
app (Android アプリ) モジュールは java フォルダを選択し Code > Convert Java File to Kotlin File
を選択すれば変換される。
ios (Multi-OS Engine モジュール) でも java フォルダを選択し Code > Convert Java File to Kotlin File
を選択すれば変換されるが、一部編集が必要。
変換前の Java ファイルと変換後の Kotlin ファイルを記載する。
Main.java
@RegisterOnStartup public class Main extends NSObject implements UIApplicationDelegate { public static void main(String[] args) { UIKit.UIApplicationMain(0, null, null, Main.class.getName()); } @Selector("alloc") public static native Main alloc(); protected Main(Pointer peer) { super(peer); } private UIWindow window; @Override public boolean applicationDidFinishLaunchingWithOptions(UIApplication application, NSDictionary launchOptions) { return true; } @Override public void setWindow(UIWindow value) { window = value; } @Override public UIWindow window() { return window; } }
Main.kt
@RegisterOnStartup class Main protected constructor(peer: Pointer) : NSObject(peer), UIApplicationDelegate { companion object { @JvmStatic fun main(args: Array<String>) { UIKit.UIApplicationMain(0, null, null, Main::class.java.name) } @Selector("alloc") @JvmStatic external fun alloc(): Main } private var window: UIWindow? = null override fun applicationDidFinishLaunchingWithOptions(application: UIApplication?, launchOptions: NSDictionary<*, *>?): Boolean { return true } override fun setWindow(value: UIWindow?) { window = value } override fun window(): UIWindow? { return window } }
alloc 関数では override
を削除し @JvmStatic external
を追加する。
window 関数の戻り値の型が UIWindow
になっているため UIWindow?
に変更する。
AppViewController.java
@org.moe.natj.general.ann.Runtime(ObjCRuntime.class) @ObjCClassName("AppViewController") @RegisterOnStartup public class AppViewController extends UIViewController { @Owned @Selector("alloc") public static native AppViewController alloc(); @Selector("init") public native AppViewController init(); protected AppViewController(Pointer peer) { super(peer); } public UILabel statusText = null; public UIButton helloButton = null; @Override public void viewDidLoad() { statusText = getLabel(); helloButton = getHelloButton(); } @Selector("statusText") @Property public native UILabel getLabel(); @Selector("helloButton") @Property public native UIButton getHelloButton(); @Selector("BtnPressedCancel_helloButton:") public void BtnPressedCancel_button(NSObject sender){ statusText.setText("Hello Intel Multi-OS Engine!"); } }
AppViewController.kt
@org.moe.natj.general.ann.Runtime(ObjCRuntime::class) @ObjCClassName("AppViewController") @RegisterOnStartup class AppViewController protected constructor(peer: Pointer) : UIViewController(peer) { @Selector("init") external override fun init(): AppViewController val statusText: UILabel get() = getStatusTextSel() val helloButton: UIButton get() = getHelloButtonSel() override fun viewDidLoad() {} @Selector("statusText") @Property external fun getStatusTextSel(): UILabel @Selector("helloButton") @Property external fun getHelloButtonSel(): UIButton @Selector("BtnPressedCancel_helloButton:") fun BtnPressedCancel_button(sender: NSObject) { statusText.setText("Hello Intel Multi-OS Engine!") } companion object { @Owned @Selector("alloc") @JvmStatic external fun alloc(): AppViewController } }
alloc 関数は override
を削除して @JvmStatic external
とする。
実装の無い Selector は external
を付ける。
関数名を getStatusText とするとプロパティの statusText と衝突してしまうため getStatusTextSel としている。
自動変換では getLabel 関数が label プロパティに変換されてしまうので修正する。
statusText, helloButton プロパティは var
から val
に変更し、get をオーバーライドしている。
Java と同様に viewDidLoad 関数が呼ばれたタイミングでフィールドにオブジェクトを保持したい場合は lateinit var
を使えば良い。
以上で変換は完了となるため、再度実行して動作することを確認する。
共通モジュールの作成
app (Android モジュール) と ios (Multi-OS Engine モジュール) で共有するモジュール (Java モジュール) を作成する。 app と同様に Kotlin に変換できる。
app と ios モジュールの依存関係に common モジュールを追加する。
以上により共通モジュールのクラスを app と ios モジュールから使用できる。