2015年12月22日

iOS

Obj-C → Swift コンバータをオープンソースで公開しました!

  • このエントリーをはてなブックマークに追加

Yahoo! JAPAN Tech Advent Calendar 2015の22日目の記事です。一覧はこちら

どうも、ヤフーの佐野( @taketo1024 )です。先日引っ越しをしまして、張り切って自分でタンスを運んだりして今とても筋肉痛です。

皆さんは Objective-C から Swift への移行は進んでいますか?弊社ではまだ Obj-C で書かれているプロジェクトは多くあります。世に出ている iOS アプリも多くはまだ Obj-C で作られているんじゃないかと思います。

Swift もオープンソース化され、この先その進化のスピードはさらに増してくるはずです。そこでチーム内で開発を進めていた Obj-C → Swift コンバータ を大幅に改良しオープンソースで公開することにしました!この記事ではその導入と活用の方法を説明します。

まずは使ってみて!

demo.gif

objc2swift.yahoo-labs.jp にアクセスして Obj-C コードを入力してみてください。上の動画のようにライブで Swift コードが出力されます。

コンバータは以下の機能をサポートしています:

  • @interface@implementation から単一の Swift クラスを生成。
  • 親クラスやプロトコルの指定、カテゴリ拡張にも対応。
  • プロパティの変換、実装部分に getter / setter がある場合も対応。
  • メッセージ送信をメソッドコールに変換。
  • [[MyThing alloc] initWithThing:...]MyThing(thing: ...) に変換。
  • NSIntegerIntNSLogprint など対応する型や関数の変換。
  • その他いろいろ!

導入方法

ソースコードは WEB版 も含め、全て GitHub で公開しています。ビルドは Gradle で一発です。

$ git clone https://github.com/yahoojapan/objc2swift.git
$ cd objc2swift
$ gradle jar

build/libs/ 下に jar ファイルが作られるので、 alias を作って入力ファイルを指定すれば変換後のファイルが出力されます。

$ alias objc2swift='java -jar /path/to/objc2swift/build/libs/objc2swift-1.0.jar'
$ objc2swift src/test/resources/sample.*

出力:

class MyClass {
    func sayHello() {
        print("Hello Swift, Goodbye Obj-C!")
    }
}

WEB版 は Play Framework を使っているので、未インストールの場合はインストールした上で、サーバを立ち上げるだけです:

$ brew install typesafe-activator
$ cd web/
$ activator run

ブラウザから http://localhost:9000 にアクセスすれば上と同じものが見られるはずです!

便利な使い方

お手元の Obj-C ソースコードを変換し、それを Xcode で開いてみると…たくさんエラーが表示されると思います。これらの多くは id 型 と Optional 型に関係するものです。一つ一つ ?. / if let / guard let を活用しながら潰して行ってください。

この辺りはまだまだ自動化できる余地はあると思いますが、「objc2swift が Swift 移行を全てやってくれる」とは期待しないでください。 Obj-C でできたことはだいたい Swift でもできますが、Swift にしかない型推論や Optional 型、プロトコル指向や関数型言語などの新しいパラダイムもあり、これらは積極的に使っていくべきものだからです。

objc2swift はあくまで「人間がやるべきでない機械的な書き換え作業」のために使い、 「Swift らしい良いコードを書く」部分はエンジニアの手でやっていきましょう。

まとめ: objc2swift を活用した Obj-C → Swift 移行手順

  1. 既存の .h / .m ファイルを変換して置き換える
  2. エラーを潰してビルドが通るようにする
  3. Swift の新しい機能やパラダイムを活用して良いコードに書き換えていく

(14日目の記事「Objective-Cで書かれた5年もののiOSアプリを徐々にSwiftへ置き換えている話」も是非ご参考ください)

開発の背景とメンバーの声

objc2swift は 4月からスタートした新規アプリ開発プロジェクトのサブプロジェクトとして開発されました。メインプロジェクトの iOS 版は Swift で開発しようということになったのですが、メンバーの iOS アプリ開発経験が少なかったこともあり、

  • Swift 100% でゼロから開発したい
  • 過去の資産はできるだけ活用したい
  • でも単純な書き換え作業はやりたくない

ということで、先にコンバータを作ろうということになりました(プロジェクトの序盤は企画がメインで開発の手が空いていたということもあり)。開発の序盤ではコンバータは大いに活用され、無駄な工数の削減に貢献できたと思います。

以下はアンケートによるメンバーからの声です:

  • Swiftだったらどう書くのかがすぐにわかり、効率的に学ぶことができた。
  • 大量のソースを扱う場合やチーム開発では特に、書き換え方が統一されるため非常に開発> 効率が上がると感じました。
  • リアルタイムで変換してくれる機能があってその場でもコードを書けるのが良かった、これがなかったら手作業で同じことをしていたのかと思うとぞっとする。

Swift 経験者がいないプロジェクトにおいては移行のハードルが下がるということが実証できたかなと思います。皆さんのチームでも重い腰をあげる一助になれば幸いです!

objc2swift の実装について

最後に objc2swift の実装について簡単に説明します。 objc2swift はオープンソースのパーサジェネレータ ANTLR をベースに Scala で開発されています。

ANTLR の文法ファイルは BNF 風の記法で、言語の構造が記述されています。例えば Obj-C のクラス宣言は:

class_interface:
  '@interface'
  class_name (':' superclass_name)?
  protocol_reference_list?
  instance_variables?
  interface_declaration_list?
  '@end'
  ;

@interface の後にクラス名が来て、スーパークラスの指定がある場合があって…とまぁそのまんまです。この文法ファイルから ANTLR がパーサを自動生成してくれるので、コンバータではパースされた構文木に対してどのような処理をするかを記述しています:

override def visitClassInterface(ctx: ClassInterfaceContext): String = {
  val head = classDeclarationHead(ctx.className(), ctx.superclassName(), ctx.protocolReferenceList())
  val body = { ... }

  s"""$head {
     |${indent(body)}
     |}""".stripMargin
}

これで @interface MyClass ... @endclass MyClass { ... } に書きかわる訳です。

(ちなみに ANTLR は Scala のコード出力に対応していなかったので、このプロジェクト用に自前で Target を追加しました。これも完成度が上がったら公開しようかと思います。)

まとめ

objc2swift の機能と活用法、そして簡単な実装の紹介でした。ソースコードは GitHub で公開しているので、改善要望やバグなどあれば気軽に Issue を投げてください。Pull-Request もお待ちしてます!

この先の iOS アプリ開発では Obj-C が姿を消し Swift に変わっていくことは確実なので、年末の大掃除…とはいかなくても、できるだけ早いうちからキャッチアップしておきましょう。

それでは、良いお年を!

objc2swift-web: http://objc2swift.yahoo-labs.jp
GitHub: https://github.com/yahoojapan/objc2swift

Yahoo! JAPANでは情報技術を駆使して人々や社会の課題を一緒に解決していける方を募集しています。詳しくは採用情報をご覧ください。

  • このエントリーをはてなブックマークに追加