Objective-C
Xcode
iOS
Swift

Objective-C → Swift コンバータ3種を比較(Swiftify, iSwift, objc2swift)

More than 3 years have passed since last update.

Swiftが発表されて以来早2年、なんだかんだと言い訳しつつその後も大量のObjective-Cコードを生産し続けてまいりました。そんなカルマを精算すべく、よく使いまわしているコードはSwift化 しておこう、と思うのですが、どうしても「既に動いているコードを書き直す」というのは、他の緊急性の高いタスクに割りこまれてしまいがち。。

そんなわけで、ObjC → Swiftに 自動変換 してくれるコンバートツールを試してみようと思い立った次第です。


コンバータを探してみる

「確かヤフーさんが昨年コンバータをオープンソースで出してたなー」とググってみると、けっこう他にもいろいろとコンバータや変換方法が見つかりました。


Swiftify

https://objectivec2swift.com/#/converter/code

オンラインで、コードブロック/ファイル/プロジェクトの単位で変換 できます。

Pricing というタブがあるので見てみたところ、無料でできるのは、


  • コードブロック単位での変換

  • 1回のコードサイズが10kBまで

のようです。

swiftify_pricing.jpg


iSwift

http://iswift.org/

オンライン版、ダウンロード版(Macアプリ)の2種類があるようです。オンライン版は、コードブロック単位での変換のみ。

iswift_online.jpg

ダウンロード版は試用期間1日で、その後は有料($14.99)となるようです。


objc2swift

https://github.com/yahoojapan/objc2swift

昨年12月にヤフーさんが オープンソースで公開 したコンバータ。

手軽に試せるオンライン版もあります。

objc2swift.jpg

ソースコードを clone してきて gradle でビルドすれば、

$ git clone https://github.com/yahoojapan/objc2swift.git

$ cd objc2swift
$ gradle jar

こんな感じでコマンドラインからも変換できるようです。

$ objc2swift src/test/resources/sample.*


Objective-C to Swift Converter

http://objc2swift.me/

こちらも国産、デジタルサーカス @tomzoh さんによる変換ツール。

メソッドコール単位での変換に対応しているようです。


objc2swift.meのバックエンドは文字列処理ベースの地味~で泥臭いプログラムだし、前述のとおりメソッドコールにしか対応していないので、いつか上記プロジェクトをバックエンドにしたみたいです。


(上記記述を踏まえ、今回は試しませんでした)


その他、コード変換に関する参考記事


コンバートしてみる

今回Swiftへの自動変換対象として用いたコードは、以下のリポジトリにある TTMCaptureManager というクラス。

AVFoundation の動画の録画処理をラップしたクラスで、

self.captureManager = [[TTMCaptureManager alloc] initWithPreviewView:self.view];

[self.captureManager startRecording];

これだけで録画処理が書けて、フレームレートも簡単に変えられるようにしてあるので、たまーに引っ張り出してきて使ってます。

・・・が、2013年にプライベートでざっと書いたもので、なにぶん汚い。1ファイルで600行もある。リファクタリングしたいけど、どうせならSwiftで書きなおしてからSwiftyにリファクタリングしたい

そんなわけでこのクラスを各コンバータにかけてみました。


Swiftify

使ってみて気付きましたが、サインアップ(無料)しないと1kBまでしかコンバート結果を表示してくれないようです。

で、サインアップして、変換してみた結果がこちら。

swiftify_result.jpg

一番上にこう書かれています。


swift

// The output below is limited by 10 KB.

// Upgrade your plan to remove this limitation.

変換結果の良し悪し以前に、無料プランでの上限10kBに引っかかってしまったのでした。長いコードこそ変換したいのに。。

変換の手間を考えると月15ドルは決して高いものではありませんが、無料で使える他のコンバータとの比較になるので、ここを実力とします。(正直なところ、変換されたコードを見ても、インデントが崩れてたりして、ちょっと不穏な感じが否めませんでした。。)


iSwift

TTMCaptureManager.m を貼ると・・・エラー。


swift

/*

Unexpected : <

Expected :
'NULLABLE', 'NONNULL', const, volatile, 'NONNULL_', 'NULLABLE_', '+', '-', typedef, extern, static, auto, register, 'BLOCK___', 'WEAK___', void, char, short, int, long, float, double, Class, id, SEL, BOOL, signed, unsigned, #type, 'USER_TYPE_NAME', struct, union, '{', enum, 'NS_ENUM', 'NS_OPTIONS', @end, @property, 'COMMENT_MULTI', 'COMMENT_SINGLE'
*/


こういう無名カテゴリでのプロトコルへの適合がiSwift的にはダメみたいです。


TTMCaptureManager.m

@interface TTMCaptureManager () <AVCaptureFileOutputRecordingDelegate, AVCaptureAudioDataOutputSampleBufferDelegate, AVCaptureVideoDataOutputSampleBufferDelegate>


ではヘッダはどうだろう、と TTMCaptureManager.h を貼ってみると・・・エラー


swift

/*

Unexpected : @

Expected :
'NULLABLE', 'NONNULL', const, volatile, 'NONNULL_', 'NULLABLE_', '*', typedef, extern, static, auto, register, 'BLOCK___', 'WEAK___', void, char, short, int, long, float, double, Class, id, SEL, BOOL, signed, unsigned, #type, 'USER_TYPE_NAME', struct, union, #identifier, enum, 'NS_ENUM', '(', 'NS_OPTIONS', @implementation, @interface, @class, #ifdef, #ifndef, #ident, #include, #undef, #import, #define, #pragma, #if, 'ELIF__', #else, #endif, 'COMMENT_MULTI', 'COMMENT_SINGLE'
*/


@import がダメみたいです。


TTMCaptureManager.h

@import CoreMedia;


これならどうだ、とヘッダの @interface 部分だけ貼ってみたところ、


TTMCaptureManager.h

@interface TTMCaptureManager : NSObject

@property (nonatomic, assign) id<TTMCaptureManagerDelegate> delegate;
@property (nonatomic, readonly) BOOL isRecording;
@property (nonatomic, copy) void (^onBuffer)(CMSampleBufferRef sampleBuffer);

- (instancetype)initWithPreviewView:(UIView *)previewView
preferredCameraType:(CameraType)cameraType
outputMode:(OutputMode)outputMode;
- (void)toggleContentsGravity;
- (void)resetFormat;
- (void)switchFormatWithDesiredFPS:(CGFloat)desiredFPS;
- (void)startRecording;
- (void)stopRecording;
- (void)updateOrientationWithPreviewView:(UIView *)previewView;

@end


やはりエラー・・・


swift

/*

Unexpected : #identifier ('CMSampleBufferRef')

Expected :
'NULLABLE', 'NONNULL', const, volatile, 'NONNULL_', 'NULLABLE_', typedef, extern, static, auto, register, 'BLOCK___', 'WEAK___', void, char, short, int, long, float, double, Class, id, SEL, BOOL, signed, unsigned, #type, 'USER_TYPE_NAME', struct, union, enum, 'NS_ENUM', ')', 'NS_OPTIONS'
*/


ちなみに試用期間1日のダウンロード版(Macアプリ)の方で試してみても、同様の結果でした。


objc2swift

手っ取り早く変換結果を見れるweb版で試しました。

最初、TTMCaptureManager.m のコードを貼ってみたところ、コンバート結果が出ない。。

まさか、これもダメなのか・・・とあきらめかけたところ、

@interface MyClass

- (void)sayHello;

@end

@implementation MyClass

- (void)sayHello{
NSLog(@"Hello Swift, Goodbye Obj-C!");
}

@end

デフォルトで入っているこのサンプルコードはちゃんと変換されている模様。

どうやら、ヘッダの内容 @interface と実装ファイルの内容 @implementation をセットで 渡さないといけないようです。

というわけでヘッダの内容をコピペしつつ、import とかの余計な部分を削除したところ、

ばっちりコンバートされました。生成されたコードをざっと見ても、private とかも判別されていて、なかなか良さげ。そのままビルド成功、というわけにはさすがにいきませんが、これだけやってくれればかなり捗りそうです。


まとめ

3種類の Objective-C → Swift コンバータを試しました。



  • Swiftify: 無料プランでは10kB制限により1クラス全体を変換できず


  • iSwift: エラーでまったく変換されず


  • objc2swift: なかなか良さげ

というわけで、唯一まともに動いたのは objc2swift だけで、しかも完全無料でオープンソース。現状では ObjC → Swift コンバータは objc2swift 一択 という結果になってしまいました。

が、今回は1クラス試しただけだし、得意不得意とかもあるのかもしれません。今後もいろいろなソースコードで試してみたいと思います。