Shiraji's Blog

How to Contribute R.swift (for Japanese Android Engineer)

This entry is for Japanese speaker. Those who are interested to contribute R.swift, please refer to this doc

これはiOS Advent Calendar 2016の12/3の記事です。

Swiftでprintlnを書いてハマるレベルのエンジニアが一週間(実稼働時間 約8時間)でR.swiftにコントリビュートしたお話。(だったけど、メンテナさんが別プロジェクト始めちゃってまだマージされていない。-> この記事の翌日2016/12/04にマージされましたー!)

Swift開発している人にとっては常識な話が多数だと思いますが、Android開発やKotlin触っていて、Swiftもやってみたい!とかSwiftのライブラリ開発してみたい!という人向けです。R.swiftのコントリビュート方法として紹介していますが、他のライブラリも同じ感じなのではないかなーと思ってます。

想定読者

  • Android開発やKotlin触っていいて、iOSの開発も興味がある
  • Swiftのライブラリにコントリビュートしてみたいけど、したことがない

申し訳ないですが、iOS/Swift開発経験が長い人は想定外です。

自己紹介

Kotlinが好きなAndroidエンジニアです。

  • Swift力: printlnとか書いて、コンパイルエラーになって、なんでや?って悩むくらい
  • Xcode力: 新規ファイル作成は任せろ!
  • その他: cocoapodは知ってた。ObjCは書いたことあったけど、ブランクが2年ほどある。

iOS開発は素人に毛が生えた程度のレベルの人間ということです。

経緯

  • Swift3の勉強したい。
  • あ、そういえば、こんな記事が・・・
  • やるか!

プロジェクト選定

プルリク駆動開発のルールは以下です。

  • GitHubのスターが500以上。出来れば5000以上
  • 開発が活発
  • Issueがラベルなどで管理されている
  • 放置されているpull requestが少ない
  • GitHubで完結している
  • 使ったことあるなしは気にしない

これに則って、勉強できそうな星多いライブラリを探してみました。

名前 ライブラリ概要 検討した理由 検討結果
Himotoki タイプセーフなJSONデコードライブラリ メンテナーが日本人のikesyoさんという安心感から。
(恐れ多くて関わったことがないのですが・・・)
issueなし!(スゲェ・・・
Kingfisher Imageのダウンロード・キャッシングライブラリ なんか名前がかっこよかった 開発活発○
ラベリングがない☓
Alamofire Swift版HTTPネットワーク Swiftやるならこれでしょという短絡的な発想 開発活発○
ラベリングあり○
メインテナー多そう○
HTTP周りの知識必須っぽい△
R.swift コンパイルタイムでコード生成
Androidで言うRクラスを使えるようにするライブラリ
自分がAndroid出身者だから 開発活発○
ラベリングがしっかり○
メインテナーが複数いるっぽい○
自分が持っている知識でいけるっぽい○

ということで今回はR.swiftを選択しました。

実際に勉強出来た事

今回のコントリビュートで以下の項目を勉強しました。

  • ライブラリのビルド方法
  • サンプルアプリのビルド方法
  • Swiftの文法(optional/guard/コレクション処理など)
  • Cライブラリ/Cコードの導入方法
  • リフレクション?(Mirror)
  • XCTestの書き方

なかなか濃い一週間でした。

ライブラリのビルド方法

ここをrswiftに変える。

change_to_rswift_scheme.png

change_scheme.png

Runではなく、BuildでOK

build_rswift.png

サンプルアプリのビルド方法

ここをResourceAppに変える。

change_to_rswift_scheme2.png

ライブラリのコード内にprint()を書けばrswift.logというところにログが出力されるように設定されています。これを利用して、変更を確認していきます。

Swiftの文法

自分は本を読んで勉強するというタイプではなく、try-errorで実際に触って勉強するタイプです。

Swiftの文法も書きながら覚えていきましたが、ちょくちょくこれどうやればいいの?っていうのにぶつかりました。特にKotlinのsmartcastの知識が邪魔してきました。。。if let guardなどは文法が結構特殊なので最後まで慣れませんでした。あと、一番の問題はSwift3を調べたいのですが、大量にSwift2などの古い日本語ドキュメントが出てきます。iOS開発者辛いなーという感じでした。OptionalもコンセプトはKotlinと似ているのですが・・・。この辺りでKotlinとSwiftが似ていると言っていた人を罵りたくなってました。

コレクション処理に関してはRubyやJava8、Kotlinなど他の言語でもよくあるものなので、そこまで苦しまなかったです。

Cライブラリ/Cコードの導入方法

残念ながら今回採用はしなかったのですが、Cコードの導入を試みました。

Androidであれば、NDKを入れて、Cコードとjnlを用意してーと本当に多くのことをやらないとCコードを利用することは出来ません。iOS開発のこれは強みで、比較的簡単に利用出来ます。

導入方法としては

  1. .h/.cファイルをプロジェクトに追加します。今回はwildmatch.h/wildmatch.c
  2. rswift-Bridging-Header.hファイルを作成し、利用したい.hをimportします。#import "wildmatch.h"
  3. Swiftでwildmatch.hにあるメソッドを呼び出します

こんなことで利用出来てしまうんか!と驚愕しました。Xcodeのコンテントアシストにも即時反映されるし、驚きっぱなしでした。

リフレクション?(Mirror)

SwiftにもJavaのリフレクションがありました。Mirrorというクラスを利用します。実はこれ使い方が間違っていたのかうまく動きませんでした。

1
2
let imageMirror = Mirror(reflecting: R.image.self)
print(imageMirror.children.count) // show 0.

ちなみにこのコードであれば動きます。

1
2
let imageMirror = Mirror(reflecting: R.image.settings)
print(imageMirror.children.count) // show children count.

たぶんselfとかのせいだろうなーと思いましたが、深追いはせず。

XCTestの書き方

ざっくりですが、JUnit3とほぼ同じです。

  • setup/tearDownメソッドが存在している
  • testprefixをそれぞれのテストメソッドにつける

BuildしたところにTestという選択肢があるので、それを選らんで実行。Java開発に慣れているなら特に問題なかったです。

ある程度開発した後に気づいたので、今回は利用しなかったのですが、XCTestでのTDD開発も可能です。たぶんそちらのほうが推奨されているのではないでしょうか。

苦戦したところ

メンテナさんにも指摘を受けたのですが、やっぱりところどころSwiftらしくない書き方をしてしまっているようです。たぶんKotlinの癖が出ているのではないかと。まだSwift力が低すぎてどこのことを言っているのか不明でした。

また、Swiftのif letguardには結構苦戦しました。例えば今回こんな感じで、ignoreFileという変数を初期化するコードを書きました。

1
2
3
4
5
  if let rswiftignoreURL = callInformation.rswiftignoreURL {
    ignoreFile = try IgnoreFile(ignoreFileURL: rswiftignoreURL) // Exceptionキャッチは別途している
  } else {
    ignoreFile = IgnoreFile()
  }

let rswiftignoreURL

1
2
3
if let rswiftignoreURL = callInformation.rswiftignoreURL {
    ignoreFile = try IgnoreFile(ignoreFileURL: rswiftignoreURL) // Exceptionキャッチは別途している
}

このスコープ内で使えることに違和感がありました。

Kotlinの場合は?.letを利用した場合、lambdaの{}内で変数定義します。(上記Swiftコードはelseもあるので再現するには、?.letではだめだけど。あとitもあるけど、それは省略。)

1
2
3
val ignoreFile = callInformation.rswiftignoreURL?.let { ignoreFileURL ->
    IgnoreFile(ignoreFileURL: ignoreFileURL)
}

最後に

メインテナさんがPR投げてから、昇華していこうぜスタイルの自分にとって理想的な人でした。彼?のおかげで、わかる点までやってみて結果を貼って、質問するスタイルで勉強しつつコードを完成させることが出来ました。

みんな使ってね!