Continuity is The Father of Success

Androidアプリとかゲームとか。毎日続けてるものについて。

FirebaseUIのFlutterプラグインをメンテしている話

blog.dr1009.com

去年こんなブログを書いたのだけれど、細々とメンテナンスを続けている。

github.com

1年前はFlutterで個人アプリを作ろうかと思っていたのだけれど、家に篭りっきりだとそういう気分も落ち気味で、今ではメンテナンスするだけに。 作ってよかったことといえば、FirebaseUIのweb対応をしようとFirebaseAuthのweb版をひたすら読んだことで、なんとなくFirebase Authのリクエスト方法に詳しくなったことぐらい。 なおpub.devを眺めてみると、同じようなことをしているライブラリはあるものの更新はとまりがちであったりする。変に大変だものね、わかるわかる。

FirebaseUIのFlutter版については、今のAndroid/iOSSDKを呼び出す方式は事前の策ではあるものの、本当はDartフルスクラッチで書いてもらうべきなんだよなーと感じていたりする。 というのも、Andorid/iOSSDKに依存しているままだとmacwindowsに対応するのは難しく、マルチプラットフォームとしてのFlutterの良さが出ないからだ。 せっかくDartでFlutter用にコードを書くのだから、ライブラリ側もDartで各プラットフォームに依存しないように作るのが良いな、というのがこの1年の知見です。

チャレンジしたflutter webむけの拡張の話。2021年1月時点で成功していない。 これはAndroidiOSの場合MethodChannelを通じて行った先で「新しい画面」を呼び出すのは比較的用意なのだけれども、webの場合そうでもないのが一番の原因だったりする。 またFirebaseUIでログインした情報を、FirebaseAuthのSDKから取得するみたいなことをしているので(Dartの世界で触っているFirebaseのインスタンスと、Java/Obj-Cの世界で触っているFirebaseのインスタンスが同一である状態を利用している)、そういったことがwebでできるのかすらよくわかっていないのもあったりする。 FlutterはAndroid/iOSの世界とべったりして作られていた2019年までの世界と、真のマルチプラットフォームをやっていくぞという気持ちで生まれ変わった2020年以降の世界をどう繋いでいくかが、これから大変なんでしょうね。

flutter webのHTMLElementが全くわからないので、この辺りのサンプルコードが増えないことにはなんともならない予感。 あと、pubspec.yml の中でJSライブラリのロードを定義できるようになったりしないですかね……。 現状、マルチプラットフォーム対応する時にDartのライブラリを使わなければならないのだけれど、これって本当はJSの資産(欲を言えばTSの資産)を使える方がいいと思うんですよ……。 dart:js ライブラリでなんとかしようとしたけれど、サンプルコードも少ないし、何より dart:js を使いこなせるのはJSのライブラリを作った人たちになる(印象がある)ので、Dartの世界に色々と持ち込むのが難しすぎると思うんですよね……。

pub.dev

なお、今はFirebaseUI-iOSの10系が出たので取り込んでみたところ、FB SDKの内部でheaderファイルが2重にインポートされてビルドができないと怒られてしまったので、ライブラリの更新が入るのをひたすら待っています。 Flutter、難しいなぁ。

2020年のおしごと

12月25日に仕事を納めたことにしたので、一年間を振り返ってみる。

おしごと

3行まとめ

  • 2月以降ずーっと在宅でお仕事、出社したの5回ぐらい
  • AndroidアプリとiOSアプリとFlutterアプリ書いてCI周りごちゃごちゃやってた
  • プロジェクトの進捗とか仕様の取りまとめみたいなことをするようになった

仕事場が家になった

もともと、会社としてリモートワークを出社が難しい日(台風の日とか)にすることはあったのだけれど、まさか毎日家で仕事するとは。

家で仕事をするにあたって、ちょっとばかし困ったことは椅子でした。 もともとそんなに広い部屋に住んではおらず、新規に椅子と机を追加することができなかったのが痛手に。 地味にMacBook Pro 15inchを机の上に広げると、スペースが足りなくなるのですが、これは慣れたら大丈夫でした。最近はエミュレーターで開発してても問題がほぼないので、電源さえ確保できればなんとでも。

リモートワークをしていると、流石にコミュニケーション頻度は減るなと感じています。 Androidチーム(3名)は週1回、雑談込みの定例とテキストチャットでワイワイやっているのでなんとかなっているのですが、チームが違ってしまったりするとどうにも。 やっぱり、昼休みに一緒にラーメン食べに行けないのは痛いですねぇ……。

備考:また行きたいお店たち

tabelog.com

tabelog.com

tabelog.com

どうしても仕事と日常のON/OFFがつきにくくなっているのは感じています。 仕事をする前か仕事終わりにフィットボクシングとかリングフィットとかをするとか、仮眠を挟むとか、そういったことをするようになりました。

2021年もこの感じでなんとかやり過ごしたい。

AndroidiOSとFlutter

Android(Kotlin)とiOS(Swift)、Flutter(Dart)をメインで書いている一年でした。 各リポジトリを並べつつ、去年と比較してみると、安定して一年間コードが書けていたように見えます。 iOSがちょっと控えめかな……。

Android(2019) Android(2020) iOS Flutter
f:id:D_R_1009:20201231141617p:plain
f:id:D_R_1009:20201231141222p:plain
f:id:D_R_1009:20201231141243p:plain
f:id:D_R_1009:20201231141300p:plain

合間でFirebase Functionsとかfastlaneとかymlとかいじってたので、今までで一番色々な言語を触っていた年のような気がしています。 Dartを書いている途中にKotlinのレビューをするとか、fastlaneを書いている途中にTSとSwiftのレビューするとか、そういうことを9月以降はやっていました。 慣れてくると、レビューぐらいならなんとかなりますね。流石にコード書く時には気持ちから切り替えないとアカン。

来年もFlutterをメインにしつつ、Paging 3がbetaになったらAndroidを書きたいなーとか思っています。 iOSはApplicationDelegateをSwiftに書き直した後にやりたかった、画面遷移周りの整理に取り組みたいなと思っています。 それぞれに集中できるよう、ちゃんと時間を管理していかなければ。

コードを書かないタイプのお仕事

1つのプロジェクトで進捗の管理、3ヶ月ほど開発アイテムの優先度付ミーティングの進行をやったりしていました。 なんとなーくでやっていたのですが、予定通りに開発できたり、進捗できたので良かったのです。

2019年までは一応AndroidiOSアプリに閉じた世界で難易度を考えれば良かったのですが、そろそろプラットフォームの縛りなく考えることも発生してきたな、という感じです。 サーバーサイドがRuby on Railsだったりするので、話を理解するためにその辺りの情報を集めるようになりました。 なんとなくで話していることが理解できているので、方向性は合っているはず。

なんとなーくやることを列挙してみたり、順番を整理してみたり、先送りする事項と今決めることを分けてみたり。 大学生の時にやってたような気がするなーと思いながら取り組んでいました。 帳尻合わせるのだけは得意なので、案外向いているのかもしれない。

その他

一年を通してみると、7月ごろからコロナのストレスでだいぶインプットに割ける余力がだいぶ減っていたように思います。 特にAtCoderは継続できなかったことが悔やまれます。

blog.dr1009.com

FlutterとFirebaseのドキュメントを読んだり、TSでDanger-JS向けのAndroidLintのPluginを書いてみたりしていたのですが、なんとなく方向性が迷子でした。

blog.dr1009.com

来年は計算機科学の勉強をちゃんとやろう。 おしまい。

GraphQLにPOSTをしてみる

GraphQLのリクエストをapollo-androidとRetrofitでそれぞれ実装してみました。

github.com

なのだけれど、実装中にGitHubのGraphQLドキュメントを読んでたら大半書いてあったので、多分こっちを隅から隅まで読んでもらう方がいいブログです。 やっぱり公式ドキュメントとかデファクトスタンダードのライブラリドキュメントを読むの大事。

docs.github.com

GraphQL

graphql.org

2020年のAndroid/iOSアプリ開発だと、長くメンテしているアプリだとなかなか置き換えることができませんが、GraphQLがきてます。TwitterGitHubも、どちらも下記のようなブログ出しているあたりにキテる感をみてます。*1

blog.twitter.com

github.blog

もちろんみんな大好きFacebook Loginを導入すると、GraphQLのリクエストをすることになります。 もしかすると、意図せず「あのレスポンスからキー名を指定して必要なパラメーターを取得しているアレ」と見ているやつかもしれません。

developers.facebook.com

きっかけ

GraphQLをアプリに導入しようとすると、大体次の記事にたどり着きます。

employment.en-japan.com

実際、筆者のプロダクトでも、最初のGraphQLエンドポイントの追加から1年近くたってようやく専用クライアントのApolloを導入したのでした。それまでは、単にJSONをPOSTしてJSON responseを受け取るAPI endpointがひとつ増えただけという運用でした。

自分の環境の場合、導入当初はGraphQLに順々に移行していこう、という話で始まったので最初からApolloを導入しました。

github.com

github.com

しかし、諸事情ありGraphQLで運用されるAPIが増やせないまま1年半ほど経ってしまいました。 Apollo-AndroidApollo-iOSは開発が非常に早く、また急激に進んでいくため、メンテナンスやバージョンアップ時のコストが高いライブラリの印象があります。 このこともあり、一時的に脱Apolloをすることとしました。

Apolloは、元々GraphQLをスキーマによるクラス定義程度しか利用していなかったため、簡単に行うことができました。ApolloSQLを利用したキャッシュなどをガッツリ使っている場合には、簡単には行かないと思うので、要件的にラッキーだったと思います。 ただこの呼び替えを実装するにあたり「GraphQLのエンドポイントにRetrofit/URLRequestを利用してリクエストを送る」実装の例が見当たらず、100%手探りの対応をすることになりました。

そんなわけで、やったことの復習がてらサンプルアプリを作りました。

やったこと

GitHub APIApollo-AndroidとRetrofitから同じ(この表現が難しい)レスポンスを取得するリクエストを投げるサンプルです。*2

GraphQLRepository

GraphQLをApollo-Androidを用いて呼び出しています。

class GraphQLRepository @Inject constructor(
    private val client: ApolloClient
) {
    suspend fun repositories() = client
        .query(RepositoriesQuery())
        .await()
        .data?.viewer?.repositories?.nodes
        .orEmpty()
        .filterNotNull()
}

これと

query Repositories {
  viewer {
    repositories(first: 20) {
      nodes {
        ...RepositoryFragment
      }
    }
  }
}

fragment RepositoryFragment on Repository {
  id
  name
  url
  description
  createdAt
}

のクエリの組み合わせです。 アクセストークンを生成した「自分の」リポジトリを上から20取得します。

なお、Apolloでは Repositories のようにクエリに名前をつける必要があるため query Repositories となります。 ここで自動生成されたコードがどうなるかは……ちょっと長くなるのでcloneして実行してみていただければ。

RetrofitRepository

呼び出しの処理をひとまとめにすると、下記のようになっています。

class RetrofitRepository @Inject constructor(
    private val service: RetrofitService
) {
    suspend fun repositories() = service.repositories(
        GraphQLBody(
            query = "query { viewer { repositories(first: 20) { nodes { ...RepositoryFragment } } } } fragment RepositoryFragment on Repository { id name url description createdAt }",
            variables = "{}"
        )
    ).data.viewer.repositories.nodes
}
interface RetrofitService {

    @POST(value = "graphql")
    suspend fun repositories(@Body body: GraphQLBody): RepositoryResponse
}
@JsonClass(generateAdapter = true)
data class GraphQLBody(
    val query: String,
    val variables: String
)

こちらは先述のクエリからクエリに付けられた名前を削除したものとなります。 GraphiQLあたりで作成したクエリをそのまま貼り付けるとこうなる感じですね。

fragment として定義したRepositoryFragmentをどうやって受け取るかというと、下記のようなJSONをパースするコードが必要になります。

@JsonClass(generateAdapter = true)
data class RepositoryResponse(
    val data: RepositoryData
)

@JsonClass(generateAdapter = true)
data class RepositoryData(
    val viewer: RepositoryViewer
)

@JsonClass(generateAdapter = true)
data class RepositoryViewer(
    val repositories: RepositoryNodes
)

@JsonClass(generateAdapter = true)
data class RepositoryNodes(
    val nodes: List<RepositoryFragment>
)

@JsonClass(generateAdapter = true)
data class RepositoryFragment(
    val id: String,
    val name: String,
    val url: String,
    val description: String?,
    val createdAt: String,
)

シンプルではあるのですが、毎回書くとちょっと嫌な気持ちになる量なことが伺えるかなと思っています。

簡単なまとめ

GraphQLで多数のリクエストを送るような処理、もしくは頻繁にデータ構造を変えたいケースではApolloなどのライブラリを利用したくなることが伺えるかなと思っています。 依存するライブラリを削減したかったり、一部からGraphQLを導入する際にはリポジトリを参考にしてもらえれば。

なお、サンプルアプリを動かすにはRepositoryへのアクセス権限を持つPersonal Access Tokenが必要になります。 GitHubのDeveloper Settingsから生成してみてください。

*1:個人的にはgRPCにもっと期待してます

*2:この文章書いてたら、別に作らなくてもよかったのではという気もしてくる