20191205のAndroidに関する記事は13件です。

【Firebase Analytics】Universal Links/App Linksでアプリを起動した回数が知りたい

はじめに

※仕様が変更される場合もあるので、必ず公式のマニュアルを参照しましょう!

導入:Universal Links/App Linksとは

本記事ではあまり詳細に説明しませんが、ざっくり説明すると、Webとアプリ(iOS,Android)に対応しているサービスにおいて、Webの特定のURLにアクセスすると、下記のような動作をする仕組みのことです。

  • アプリがインストールされている場合→アプリが起動する
  • アプリがインストールされていない場合→Webページが開かれる

iOSではUniversal Links, AndroidではApp Linksと呼ばれています。
実装方法などは公式のドキュメントをご覧ください。
Universal Links - Apple Developer
Android アプリリンクの処理  |  Android Developers

結論:Universal Links/App Linksでアプリを起動したとき、firebase_campaignというイベントが自動計測される

いきなり結論ですが、Firebase Analyticsにおいて、Universal Links/App Linksでモバイルアプリを起動したときのイベントは、firebase_campaign というイベント名で計測されます。

実は公式のドキュメントをちゃんと読むとしっかり書いてあります。

ユニバーサル リンクは、utm パラメータ(utm_source、utm_medium、utm_campaign)を含めるように設定することができます。関連データは [アトリビューション] レポートに表示されます。

ユニバーサル リンクの例:

http://my.app.link?utm_campaign=myappcampaign&utm_source=google&utm_medium=cpc

ユーザーがリンクをクリックすると、firebase_campaign イベントが上記のパラメータとともに記録され、キャンペーン関連のイベントについては次のデータが表示されます。

キャンペーン: myappcampaign
参照元: google
メディア: cpc
https://support.google.com/firebase/answer/6317518?hl=ja

上記はUniversal Linksについてですが、App Linksについても同様のイベント名で集計されています。

Android App Links は、utm パラメータ(utm_source、utm_medium、utm_campaign)を含めるように設定することができます。関連データは [アトリビューション] レポートに表示されます。

Android App Link の例

http://example.com/gizmos?utm_campaign=myappcampaign&utm_source=google&utm_medium=cpc

ユーザーがリンクをクリックすると、firebase_campaign イベントが上記のパラメータとともに記録され、キャンペーン関連のイベントについては次のデータが表示されます。

キャンペーン: myappcampaign
参照元: google
メディア: cpc

このfirebase_campaignイベントは、Firebase Analytics側で自動的に集計されるイベントのひとつのようなので、Firebase Analyticsが導入されていればモバイルアプリ側で特別になにか実装をしなければならないということはありません。

ちなみにfirebase_campaignイベントで自動で記録されるパラメーターは source、medium、campaign、term、content、gclid、aclid、cp1、anid だそうです。
https://support.google.com/firebase/answer/7061705?hl=ja

firebase_campaignイベントの内容はどこから確認できるのか?

それでは、このイベントによって集計された値はどこから確認できるのでしょうか? ドキュメントには関連データは [アトリビューション] レポートに表示されます。と記載されています。

Eventsレポートからは確認できない

サイドバーにある[Events]をクリックすると集計された各イベントの回数やパラメータ等を確認できるのですが、ここにはfirebase_campaignという名前のイベントは表示されていませんでした。

スクリーンショット 2019-12-05 21.30.03.png
※実際の数値等は隠しています

Conversionsレポートでコンバージョンイベントに紐づく参照元として確認できた

サイドバーにある[Conversions]をクリックすると、コンバージョンイベントのレポート結果を閲覧できます。

例えばデフォルトでコンバージョンイベントとして用意されている first_open(アプリ初回起動)イベントを選択すると、画面下部あたりにそのコンバージョンイベントの参照元が表示されています。
この「参照元」パラメータが、firebase_campaignイベントで集計されたutm_sourceの値だと思われます(違っていたら申し訳ありません…)

スクリーンショット 2019-12-05 21.37.44.png
※実際の数値等は隠しています

その他のFirebaseコンソール画面を調べてみたのですが、これ以外の画面でfirebase_campaignイベントの計測結果を確認できる箇所が見つかりませんでした :scream:

この画面が[アトリビューション] レポートなのでしょうか?どの画面が該当の画面なのか、筆者にはわかりませんでした…。もしご存じの方がいれば、コメント等で教えていただけると大変喜びます :pray:

しかし、この画面からは純粋な「モバイルアプリを起動した回数」を特定することができません…。

BigQuery経由でfirebase_campaignイベントを集計してみた

いろいろ調べてみたのですが、Firebaseコンソール画面上でfirebase_campaignイベントの内容を確認する方法がわかりませんでした :cry:
ですが、BigQueryからFirebase Analyticsの生データを分析できるので、そこから結果を確認することができました。

Firebase Analyticsで集計されたイベントの生データは、BigQuery経由でアクセスすることができます。
具体的な連携方法等は公式のドキュメントや他の方のQiitaの記事で確認していただければと思います。
Firebase の BigQuery Export - Firebase ヘルプ

BigQueryで、前日のUniversal Links/App Links経由でモバイルアプリを起動したイベント回数を、プラットフォーム(iOS, Android)と参照元等の組み合わせで表示するクエリは下記のようになりました。
ぜひみなさんのプロジェクトでも参考にしてください :bow:

SELECT
  platform,
  (SELECT value.string_value FROM UNNEST(event_params) AS x WHERE x.key = "source") AS app_source,
  (SELECT value.string_value FROM UNNEST(event_params) AS x WHERE x.key = "medium") AS medium,
  (SELECT value.string_value FROM UNNEST(event_params) AS x WHERE x.key = "campaign") AS campaign,
  COUNT(DISTINCT user_pseudo_id) as users,
  COUNT(user_pseudo_id) as eventCount
FROM
  `{your_project}.analytics_{your_id}.events_*`,
WHERE
  PARSE_DATE("%Y%m%d", event_date) = DATE(DATETIME_ADD(CURRENT_DATETIME, INTERVAL -1 DAY))
AND
  _TABLE_SUFFIX = FORMAT_DATE("%Y%m%d", DATE(DATETIME_ADD(CURRENT_DATETIME, INTERVAL -1 DAY)))
AND
  event_name  = 'firebase_campaign'
GROUP BY 
  platform,app_source,medium,campaign
ORDER BY users desc

所感:独自のイベントを実装するのもアリかも? :pencil:

  • 本記事はFirebase Analyticsをモバイルアプリに導入した際に、Universal Links/App Linksでアプリを起動したときの自動計測イベント firebase_campaignの分析方法について紹介しました
  • firebase_campaignイベントはFirebaseコンソール画面からだと確認しづらい?かも?
  • それならばいっそ独自のカスタムイベントとして、Universal Links/App Linksでアプリを起動したときにFirebase Analyticsの集計イベントを発火させるように実装したほうが、後々集計しやすいのではないかと思いました
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flavorってなんなのか

Flavor(味付け)?

みなさんFlavor使ってますか
意外と使ってないアプリも多かったりするんじゃないですかね

今日はFlavorのよくある使い方を例に、少しだけアプリに味付けをしてみたいと思います

とりあえずAndroidStudioで適当なプロジェクトつくります
URLとか出したいじゃないですか。ふつう。
ということで、Configファイルに記載してあるURLを出してみます。
スクリーンショット 2019-12-05 19.55.50.png
スクリーンショット 2019-12-05 19.56.02.png

こんな感じで出ましたね。はい。
スクリーンショット 0400665f-20d3-3f89-c5a0-2a03dc4fe929.png

・・・URLとかってテスト用と本番用で変えたい場合がほとんどですよね。。。

わけてみる

本番用をhttps://www.yahoole.com
テスト用をhttps://www.test.yahoole.com

って感じにわけたいです。

ProjectStructureを起動してBuildVariantsからFlavorsを追加しましょう

Dimensionをつくる

Flavorを登録するためには、まずDimensionというものを登録する必要があります
これはFlavorをいろいろなことに使えるようにするためのものです

でもFlavorがなんなのかがいまいちわかってないのに、Dimensionの話をされても困りますよね
ここはおまじないのように「default」というDimensionでも作ってお茶を濁すことにしましょう
スクリーンショット 2019-12-05 20.45.21.png
スクリーンショット 2019-12-05 20.45.40.png

Flavorをつくる

Dimensionが登録できたので、Flavorを登録することができるようになりました
さっそく
本番:production
テスト:development
というFlavorを作ってみましょう
スクリーンショット 2019-12-05 20.50.04.png
スクリーンショット 2019-12-05 20.50.33.png

はい、2つできました

Flavorはたくさんのことができるんですが、今回は本当にシンプルにURLを変えるということだけやってみます

Flavorごとの入れ物をつくる

まずはprojectのウィンドウをAndroidからProject Filesに変えましょう
スクリーンショット 2019-12-05 20.52.52.png

次に、Configファイルの入っている、「main.java.com.gagaaplication」というフォルダ階層のmainの部分をdevelopmentに変えたフォルダ階層を作成します。
スクリーンショット 2019-12-05 20.53.16.png

スクリーンショット 2019-12-05 20.59.08.png

次に、Configファイルを今作った、「development.java.com.gagaaplication」の下に移動させます
スクリーンショット 2019-12-05 21.00.14.png

続いて、「development.java.com.gagaaplication」のdevelopmentをproductionに変えたものをコピペして作ります
スクリーンショット 2019-12-05 21.02.54.png

これで、main、development、productionという3つのおなじようなフォルダ構成ができました

Configの中身を変える

ConfigのURLを先程の本番用とテスト用にかきかえます
スクリーンショット 2019-12-05 21.06.13.png

これで、productionフォルダ配下のConfigの中身とdevelopmentフォルダ配下のConfigの中身が異なるものになりました

実行してみる

FlavorはBuildValiantsウィンドウから変更できます。
スクリーンショット 2019-12-05 21.08.09.png
「DebugとかReleaseとかついてるやんけ!わからんわ!」となってしまった方はとりあえずDebugが付いてるのを選びましょう
このDebugとかReleaseというのは「Build Type」というものなんですが、これについてはまた今度説明することにして、ここではだまってDebugを選びましょう

ではFlavor「development」(developmentDebug)を選んでRun!
スクリーンショット 0400665f-20d3-3f89-c5a0-2a03dc4fe929.png

続いてFlavor「production」(productionDebug)を選んでRun!
スクリーンショット 0400665f-20d3-3f89-c5a0-2a03dc4fe929.png

どうでしょう?
見事にURLの中身が変わったでしょうか?

まとめ

こんな感じでFlavorを使うと簡単になにかの値を切り替えることができます
今回はURLでしたが、クラス自体の中身を変えてmock用のFlavorを作ったり、SDKのバージョンを切り替えてテストするのに使用できたりと、あなた好みの味付けでビルドをすることができるようになります

ちなみに、アプリが大方出来上がってきてからFlavorで切り分けしようとすると変えたい情報が散らばってしまっていたりしてけっこう大変なので、Flavorはアプリの開発初期段階で作成してしまうのをおすすめします

では、すてきなAndoridライフを!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Androidアプリ開発をしているときに思ったことや気づいたこと、感じたこと

 こんにちは。最近寒くなってきましたねー。僕は未だにジャンバーを着るタイミングがつかめませんw

アプリ開発をしている人しか分からないネタですw

  • できるだけネットに頼りたくない
  • 思うようなことができたらうれしい
  • PCとノートの使い分け
  • 次作るアプリの案が出てくる

できるだけネットに頼りたくない

 初めてアプリを作る際や新しくプログラミング言語を学ぶときに僕は必ず参考書を買います。その中で必ずと言っていいほど参考書だけでは分からないところが出てきます。例えば、タイマー機能を実装したいという時はネットに頼ります。そういうのではなくて、自分の力で何とかなりそう、考え方を変えれば実現できそうという時にネットを頼りたくはないと感じます。それでも分からない!となるとインターネットに頼るんですが、やっぱり悔しいですね。僕の中ではネットは最終手段です。と言っても、しょっちゅう利用するんですが(笑)

思うようなことができたらうれしい

 誰しもが経験あると思います。こうなるだろうと自分で考えてコードを打ち、Shift+F10を押す。うまくいくと僕は「よっしゃー!」と声に出し更にはガッツポーズまで添えます(笑)。

PCとノートの使い分け

 僕の場合はPCは設計書や確定している事項などしっかりしたものを残すために使い、ノートはその時に思いついたことや複雑な処理を考えるときなどメモ程度に扱います。どちらも共通して言えることは書く前に日付を記入するところです。

次作るアプリの案が出てくる

 常に頭の中でいろいろ考えているのですが、まだアプリが完成していないのに次はこのアプリ作ろうとか考えてしまいます(笑)。今考えているのはゲームセンターでゲットした景品の写真を保存するメモ帳的なアプリを考えています。僕自身よくゲームセンターに行くので、その時に取った景品やいくら使ったのか、どういう種類のUFOキャッチャーなのかなど保存しておきたいなーと思ったからです。まぁまだ日記アプリが終わってからですねー。

以上が僕がアプリ開発をしていて思ったこと、気づいたこと、感じたことでした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

社員旅行アプリをドメイン駆動設計で開発してみた軌跡

こんにちは。ナビタイムジャパンの地図フレームワークエンジニアときどきAndroidエンジニア、3代目ゆうです。

これは NAVITIME JAPAN Advent Calendar 2019 9日目の記事です。

この記事ではナビタイム社員旅行用のしおりアプリをDDD(ドメイン駆動設計)で作ってみた軌跡についてお話したいと思います。

はじめに

ナビタイムジャパンでは毎年秋に社員旅行を開催しています。最近では社員旅行のある会社は珍しいみたいですが、ふだん仕事で関わることが殆どの同僚と非日常な雰囲気で交流できるのは中々に楽しく、私は気に入っています。
それになんといっても会社のお金で美味い飯、美味い酒、いい温泉である。

しかして我らエンジニア、ただ消費するだけの旅行じゃ面白くない。せっかくならば社員旅行にかこつけて何か楽しい開発をしようじゃないかと、有志のアプリエンジニア数人が集まり勝手に始めた企画が今回お話する社員旅行アプリです。
旅のしおり(行程表)や各自の新幹線座席表、旅館の部屋番号などなど、必要な情報をまとめた一つのアプリを作っちゃおうという企画でした。

社員旅行アプリの目的、要件

そんな些細なきっかけから始まった社員旅行アプリプロジェクトですが、大まかな目的として下記を設定しました。

開発の目的
部署を超えて沢山の人達とアプリを作りたい、交流したい
新しい技術を使いたい、挑戦したい
今後の業務で参考になる設計・実装のアプリにしたい
アプリの要件
社員旅行での”困った”を解決するアプリにする
Android/iOSのネイティブアプリにする

本記事では「今後の業務で参考になる設計・実装のアプリにしたい」という目的からDDDを採用した経緯および実践してみたドメインモデル化の過程について書いていきたいと思います。
実際はよりアプリ実装に近い設計(アーキテクチャ)の話や、それに合わせたフレームワークライブラリの選定なども同時に行いましたが、話が大きくなりすぎてしまうため割愛します。

ドメイン駆動設計の採用

今回、有志メンバー同士で話し合い、以下の理由からDDDを導入することにしました。

チーム単位でのドメイン駆動設計を経験したい

一つ目の理由にして、最大の理由がこちらです。

今回の参加メンバーの習熟度としては、いわゆるDDD本1やIDDD本2の読了者から、「DDD?なにそれ?」な人まで幅広くいました(ちなみに、私は「DDD?なにそれ?」側の人間です)。中には個人のアプリ開発でDDDを実践しているという人や、普段の業務でドメインを意識した設計をしているという人もいましたが、チームとして導入した経験のある人はいませんでした。

新規サービスの立ち上げなどでもない限り、業務で突然導入するにはハードルが高いのが実情だと思います。しかし今回の社員旅行アプリは、「新規開発」で「規模もそこまで大きくなく」、「趣味半分」なので練習台として扱うには丁度いいのでは?という意見から実現することとなりました。

Android/iOSエンジニア間で用語の認識齟齬などを防ぎたい

前項のとおり、DDDの浸透が十分とは言えない開発現場において、身近な課題としてよく挙がるのがこちらです。

ナビタイムのアプリはその多くがAndroid/iOSの両OSについてネイティブアプリとして開発し、基本的には同じ機能を持つように作ります。
しかし、多くの場合それぞれ別の開発者が担当することになるため、機能の仕様検討までは一緒にやることが多くとも、コードレベルまで同調して開発ということはあまりやっていません。
その結果、同じ機能、同じ概念を指した言葉であってもコード上の名前が異なっているという事例は珍しくありませんでした。

今回、社員旅行アプリはAndroid/iOSの両OSについてネイティブアプリとして開発するという要件がありました。
また、「沢山の人達で作りたい、交流の場にしたい」という想いから、社内に広くコントリビュータを募り、不特定多数の開発者で作ることも予定していました。その結果として懸念される前述のようなドメインモデルの認識齟齬を防ぐためにも、より厳密な形のDDDが効果的なのではという狙いも導入を後押ししました。

ドメイン駆動設計の実践

DDDを実践していくにあたり、お手本としてDroidKaigi2019のカンファレンスアプリおよびその設計について解説したあんざいゆき氏の講演を参考にしました。

対象ドメインの確認

まず、ドメインモデルを考える上で、対象となるドメインそのものを決定しなくてはなりません。今回は特に大きな議論もなく、対象ドメインは以下の通りとして決定しました。

  • 2019年度ナビタイム社員旅行

ユビキタス言語の洗い出し

例年、社員旅行が近づくと総務の方から旅のしおり(PDFファイル)や各個人の旅行コース、新幹線座席などが一覧になったスプレッドシートが展開されます3。そのため、まずはそれらの資料からひたすらにユビキタス言語となりうる単語についてざっくり書き出していくという作業をはじめました。この時点では、あいまいな表現の重複や実際にアプリとして使うかどうかといった事は一旦置いておいて、読み進めながら目についた単語を書き出していきました。

書き出した一例を以下に引用します。

部署 氏名 性別 往路乗車駅 往路新幹線座席番号 コース別観光 1日目バス号車 2日目バス号車 部屋割り ...

さて、一先ずの書き出しが終わったところで、次はそれらの単語をユビキタス言語として整理することとしました。
書き出した一覧から、あいまいな表現や意味合いが重複する単語を統一し、共通言語としての日本語とコードに落とし込むための英語の2言語で定義していくこととしました。

ここまでで、ユビキタス言語として定義した一例を以下に引用します。

個人情報【user】 氏名【name】 性別【gender】 部署【division】 1日目【day1】 2日目【day2】 乗車新幹線【shinkansen】 バス【bus】 観光コース【tripCourse】 部屋番号【roomNumber】 ...

ドメインモデル化

必要な項目名、単語名がユビキタス言語として出揃ったため、いよいよドメインモデル化を行います。
まず、より大きなくくりとなる概念から、それを構成する要素が何か整理することとしました。
ここでは、「個人情報」を例にとって解説していきます。

まず、「個人情報」には「氏名」、「性別」、「部署」が含まれるだろうという事が直感的に分かります。また、今回の対象ドメインは「2019年度ナビタイム社員旅行」であるため、その人が乗車する「新幹線」や「バス」、旅行中の「観光コース」や「部屋番号」も紐付いていいはずです。

実際には、この結論まで紆余曲折経つつ議論を行ったのですが、最終的に以下のような「個人情報」モデルが出来上がりました。

  • 個人情報【user】
    • 部署【division】
    • 氏名【name】
    • 性別【gender】
    • 社員属性【employeeAttribute】
    • 1日目【day1】
      • 乗車新幹線【shinkansen】
      • バス【bus】
      • 配布ドリンク【drink】
        • お茶【tea】
        • ビール【beer】
      • 観光コース【tripCourse】
    • 2日目【day2】
      • 乗車新幹線【shinkansen】
      • バス【bus】
      • 観光コース【tripCourse】
    • 部屋番号【roomNumber】

さらに、ドメインモデルを考える上で重要な要素として、そのモデルが値オブジェクトなのかエンティティなのかを判断します。
改めて上記「個人情報」モデルを見返してみると、仮にこれらの値が一致していたとしても必ずしも同一の個人であるとは限らない事が分かります。逆に、「部署」や「観光コース」といった属性値が変わったとしても、その個人であることは変わらないため、「個人情報」は同一性によって区別されるもの、すなわちエンティティであると言えます。

エンティティを表現するためには、同一性を一意に識別できるIDが必要になってきます。
IDを付与するといっても、実態として何を使うかは考える必要があります。今回は対象ドメインである「2019年度ナビタイム社員旅行」における「個人情報」を一意に識別するためには社用メールアドレスの@以前が利用できるため、その文字列を利用することとしました。

同様に、以降も全ての概念についてモデル化を行っていきました。

ドメインモデル実装

最後に実装の話に少しだけ触れていきたいと思います。
前項までで洗い出せたドメインモデルを実際のコードに落とし込む作業となります。
なお、実際はAndroid/iOSそれぞれをkotlin/Swiftで実装したわけですが、大まかな考え方は共通な箇所となっているためAndroid(kotlin)を例にとって解説します4

まず、実際に出来上がった「個人情報」のモデルクラスを御覧ください。

User.kt
data class User(
    val id: UserId,
    val division: Division,
    val name: String,
    val gender: Gender,
    val employeeAttribute: EmployeeAttribute,
    val day1: Day1?,
    val day2: Day2?,
    val roomNumber: String?
) : Serializable


inline class UserId(val value: String) : Serializable

先程記載したドメインモデルそのままになっていることがわかると思います。
ドメインモデルは基本的に data class または inline class として定義し、選択肢が限られている場合( GenderDivision など)は enum class として定義する形としました。
また、クラス名やプロパティ名にはユビキタス言語を使うようにしました。

実際には、ここからドメインモデルレイヤー以外のUIや各種レイヤーの設計、実装があるのですが、DDDの実践としてはここまでとしたいと思います。

大変だったところ、議論の白熱したところ

前項までのように実践してみて、難しさを感じたポイントや議論の白熱したところがありましたので、その一部を紹介させていただきます。

表記ゆれ多い問題

これはユビキタス言語の洗い出し過程で発生しました。
今回は主に旅のしおりを使って用語の抽出を行ったわけですが、それ自体はあくまでヒト向けの読み物であるため表記ゆれや曖昧な語が多数出現しました(e.g. 旅館, 宿舎, ホテル)。
こういった表記ゆれや曖昧な語を直感的にまとめてしまう事は簡単ですが、今回は一つ一つ愚直に「どういう意味合いを含むのか」「どちらがより適切か」といった事を話し合うようにしました。
思っていた以上に時間はかかってしまいましたが、その分メンバー間での用語に対する認識齟齬は殆ど無くなったため、やはり大事なフェーズだったと思います。

UIとモデルの関係性

ドメインモデルを考える上で意外と大変だったことが「UIの事を考えがち」になってしまったことです。今回集まったメンバーは全員アプリエンジニアだったのですが、弊社のアプリ開発手法としては画面ベースのプロトタイピングから各画面の仕様や機能を考える事が多いため、ふだんの慣習からドメインモデルもUI基準になってしまうという問題がありました。
すなわち、「この画面でこの情報を一緒に出したいから、同じモデルに入るべきではないか?」といった発想です。ドメインモデルがUIの事を意識するべきでないという認識は持っていましたが、分かっていてもついつい画面ファーストに考えてしまうことは少なからずありました。

そこで、今回は議論が行き詰まってきた場合には敢えていろんなパターンの画面を仮想的に考えて、その中で共通のモデルを使う場合にどこでも違和感がないようにすることで普遍的なドメインモデルを探るという方針を取りました。
結果として、実装フェーズに入ってから画面デザインの変更が起きてもドメインモデルに対する修正は発生せずに済んだ事もあったので有効な手段かもしれません。

新幹線は物か概念か

今回の例の中で最もDDDらしく、最も難しかった議論がこちらです。
旅行の行き帰りに乗る新幹線に関する情報について、当初は物理的な新幹線の車両としてモデル化しようと試みました。つまり新幹線とは、複数の車両を持ち、車両の中には無数の座席を持ち、各座席に着座する個人が紐づくものであると考えたのです。
しかし、この考え方だと実際にデータクラスに落とし込んだ時に大量の座席プロパティを持つなどあまり現実的なデータ構造ではなくなってしまいます。さらに、その新幹線の停車駅や発着時刻といった情報はまた別のモデルとして切り出すことになり、必要以上に複雑になってしまう事が分かりました。

その後の議論で、今回必要な新幹線の情報とは、結局のところ「個人がどの新幹線のどの座席に乗るのか、何時に出発し何時に到着するのか」だけだということに気付きました。最終的に、それらの情報を持った値オブジェクト「乗車新幹線」という概念としてモデル化することで解決しました。
ドメインモデルの決定には、対象とするドメインにおいてその用語がどのような文脈で語られているのかを考慮しないと、適切なモデリングにはならないという良い例になったと思います。

まとめ

社員旅行アプリという題材を元にDDDを実践してみた軌跡についてお話させていただきました。

私自身、「DDD?なにそれ?」な人だったわけですが、いざ勉強しつつ実践してみて本記事が書ける程度の理解を得ることが出来ました。
言うまでもなく分かっていたつもりではありましたが、改めてちゃんと設計することの難しさと大切さに気付けるいい機会になりました。

きっかけこそ軽い気持ちで始まった社員旅行アプリプロジェクトでしたが、最終的に新卒1年目も含む30名(!)近くものエンジニア、デザイナが関わってくれました。
それだけの人数で一つのプロダクトを作れたこと、さらにそこでドメイン駆動設計を体系的に実践出来た事は大きな意味を持つのでは無いかなと思っています!

参考

  1. ドメイン駆動設計/エリック・エヴァンス
  2. 実践ドメイン駆動設計/ヴァーン・ヴァーノン
  3. DroidKaigi 2019 で「LiveData と Coroutines で 実装する DDD の戦術的設計」について話してきました。- Y.A.M の 雑記帳

  1. 参考文献1:ドメイン駆動設計/エリック・エヴァンス 

  2. 参考文献2:実践ドメイン駆動設計/ヴァーン・ヴァーノン 

  3. なお、この時点ではまだ今年の社員旅行情報は不明だったため昨年の資料をベースに実施 

  4. 私がAndroidしか分からないから、という理由も含む 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Android / iOS アプリのテスト配布サービスあれこれ(DeployGate、Firebase App Distribution、Bitrise)

この記事は くふうカンパニー Advent Calendar 2019、5日目の記事です。

くふうグループの Da Vinci Studio では、さまざまな Android / iOS アプリの開発やサポートを行なっています。
そしてふだんから複数のサービスを使ってテスト配布しているので、それぞれのサービスについての感想と使用例を書きます。

1. DeployGate
2. Firebase App Distribution
3. Bitrise(の配布サービス)

1 と 2 は fastlane を使ってアップロードしています。3 は Bitrise のデプロイステップを使っています。
また、最後におまけで Bitrise で Firebase App Distribution についても書いています。

DeployGate

https://deploygate.com/

感想

  • 日本語で説明されていてわかりやすい
  • テスター向けの説明も日本語でサポートしやすい
  • iOS でデバイスを追加するとき、UDID 取得から登録、再配布までの手順が簡単(dg コマンドラインツール が便利)
  • 同じ配布ページでアップデート版を続けて配布できる(テスターに見てほしい特定のバージョンを開発者が指定できる)
  • アップロード先に複数の配布ページを指定できるので、開発者用とテスター用で出し分けられる1
  • テスターがアプリをインストールしたときなどに通知を受信できる(サポートするときに便利)
  • 料金はグループ開発向けプランだと最低でも 10,000円 / 月(税込)、それでも使っていきたい

使用例: fastlane を使うとこんな感じ

Fastfile でレーンを設定
  build_app(...)
  deploygate(
      api_token: ENV["DEPLOYGATE_API_TOKEN"],
      user: ENV["DEPLOYGATE_USER"],
      ipa: "./davincistudio.ipa",
      message: "メッセージ",
      disable_notify: true
  )

詳しくは、https://docs.deploygate.com/docs/fastlane をご覧ください。

ENV["DEPLOYGATE_API_TOKEN"]ENV["DEPLOYGATE_USER"].env ファイル に設定しています。

Firebase App Distribution

https://firebase.google.com/docs/app-distribution?hl=ja

感想

  • 日本語で説明されていてわかりやすい
  • テスター向けの説明は英語だが、開発者がリリースノートを工夫すれば何とかなる
  • Android / iOS アプリ開発ではたいてい Firebase を使うのでなるべく統一したい
  • 今のところ 0 円だが料金はよくわかっていない
  • まだベータ版なので今後に期待

使用例: fastlane を使うとこんな感じ

fastlane プラグインをインストール
$ fastlane add_plugin firebase_app_distribution
Firebase CLI をインストール
$ curl -sL firebase.tools | bash
Fastfile でレーンを設定
  build_app(...)
  firebase_app_distribution(
      app: ENV["FIREBASE_APP_ID"],
      release_notes: "リリースノートです。",
      groups: "dvs" # 作成したグループのエイリアス
  )

詳しくは、
Android: https://firebase.google.com/docs/app-distribution/android/distribute-fastlane
iOS: https://firebase.google.com/docs/app-distribution/ios/distribute-fastlane をご覧ください。

ENV["FIREBASE_APP_ID"].env ファイル に設定しています。

Bitrise(の配布サービス)

https://www.bitrise.io/

感想

  • Bitrise で Deploy to Bitrise.io ステップを使うとそのまま配布準備ができるので簡単
  • 日本語に対応していないのでテスターにとってはわかりづらい
  • 料金は Bitrise の利用料に含まれるので、Bitrise を使っていれば気にならない

使用例

Bitrise で Deploy to Bitrise.io ステップを設定し、テスターを Team に登録します。
(テスターに登録しなくても公開インストールページからの配布もできます。)

詳しくは、
Android: https://devcenter.bitrise.io/jp/deploy/android-deploy/deploying-android-apps/
iOS: https://devcenter.bitrise.io/jp/deploy/ios-deploy/deploying-an-ios-app-to-bitrise-io/ をご覧ください。
また iOS デバイスの登録は、 https://devcenter.bitrise.io/jp/testing/registering-a-test-device/ をご覧ください。

[おまけ] Bitrise で Firebase App Distribution

Firebase CLI をインストールするステップを追加

https://firebase.google.com/docs/cli?hl=ja#install-cli-mac-linux にて「CI/CD 環境での自動デプロイ」は自動インストールスクリプトが推奨されているので、そのとおり設定します。

$ curl -sL firebase.tools | bash

Bitrise ではこのスクリプトステップを追加するだけで実行できました。

+ curl -sL firebase.tools
+ bash
-- Checking for existing firebase-tools on PATH...
Your machine already has firebase-tools@]0;Firebase CLI7.8.1 installed. Nothing to do.
-- All done!

[最後に] つまずきそうなところ共有

Error: App Distribution could not find your app x:xxx:xxxx:xxx. Make sure to onboard your app by pressing the "Get started" button on the App Distribution page in the Firebase console: https://console.firebase.google.com/project/_/appdistribution

これはたぶん Firebase コンソールで App Distribution を「開始」していません。Firebase コンソールの App Distribution ページに移動し、アプリを選択して「開始」しましょう。
そうでない場合は、アプリ ID が違うかもしれません。Settings 全般 のマイアプリで確認して合わせましょう。

[!] Could not find action, lane or variable 'firebase_app_distribution'. Check out the documentation for more details: https://docs.fastlane.tools/actions

これはたぶん fastlane の App Distribution プラグインを追加していません。$ fastlane add_plugin firebase_app_distribution を実行して追加しましょう。

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

NavigationViewのアイテムを動的に隠す

はじめに

業務において「条件に応じてNavigationViewのアイテムを隠す」
という要件があって調べたので備忘録として残しておきます。

実装方法

BottomNavigationのアイテムの場合とは違い、Visibilityをいじるのが正解のようです。

navigationView.menu.findItem(R.id.nav_slideshow).isVisible = false

結果

Slideshowというメニューを隠してみます。

通常時

通常時は下記のようなアイテムを表示しています。
image.png

隠した時

このようにSlideshowが隠れました。
Visibilityをfalseにすると一見invisibleのように思えますが、挙動的にはgoneです。

image.png

参考

https://stackoverflow.com/questions/31954993/hide-a-navigation-drawer-menu-item-android

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MVPとMVVMについてざっくり整理

はじめに

業務では専らAndroidアプリの開発をしています。
ただ、今まではあまりアーキテクチャの学習をしっかりしてこなかった面がありました。
なので今回はAndroid開発でよく耳にするMVPとMVVMの設計方法や考え方について調べ、その内容を自分用に整理してみようと思い、この記事を書いてみました。
今回の投稿が初めてであり、この記事自体もまだざっくりとした概念部分しか書けていないので、引き続き自分で実装してみたり、テストコードを書いてみたりしていこうと考えています。機会があればそちらも随時上げていけたらと思います。
(Androidと言いつつ、Androidのコードは出てきません。。。)

ちなみに、今回参考にさせていただいた書籍はこちら

MVPとは

UIの関心ごとをModel,View,Presenterという3つの役割に分離させたアーキテクチャ
UIの複雑さを解決することを目的としている

  • Model

    • UIに関わるデータを定義しデータ構造を提供するインターフェース
  • View

    • Modelで定義したデータを表示し、イベントをPresenterへ伝える
    • 表示処理を行う受動的なインターフェース
  • Presenter

    • ModelとViewを操作する
    • Modelからデータを取得し、Viewに表示するためのロジックを担当する

mvp.png

意識したい点

・まずは何に於いても各クラスの役割を明確にすること

MVPに限らず、役割を明確にすること、そしてそれ以外のことはさせないことがとても大切。それによって各クラスが自分の役割に専念できるようになり、他のクラスが中で何をやっているかは意識しなくて済むようになる。(単一責任の原則)

・インターフェースを用いることでモックを使ったテストが可能になること

処理を適切なクラスに持たせたり分割したりすることも大切だが、インターフェースを介した作りにすることで、他のクラスに依存しないテストを行うことができるようになる。(Dependency Injection)

Googleが公開しているサンプルコードはこちら

MVVMとは

こちらもUIの分離を目的としたアーキテクチャ
Model,View,ViewModelという3つの役割に分離している
UIとビジネスロジックを関連づけるデータバインディングという仕組みを前提としている

  • Model

    • アプリケーションのドメインモデルを指す
    • データ構造のほかビジネスロジックを表現する手段も含まれる
  • View

    • ユーザーが見る構造、レイアウト、外観を定義する
    • ビジネスに関わるロジックからは分離されている
  • ViewModel

    • ModelとViewの仲介役で、表示に関わるロジックを担当する
    • Modelを操作し、Viewが使いやすい形でデータを提供する

mvvm.png

意識したい点

・MVPとの違いは、Model(データ部分)が変更されると、Viewが自動的に更新されるということ

MVPではPresenterがViewへの参照を持っているため、Presenterから明示的にViewを呼び出して更新することができるが、MVVMではViewModelがViewへの参照を持っておらず、Viewを直接更新できない。その代わり、MVVMパターンを利用する場合は、レイアウトファイルを使用してViewModelから観測可能なフィールドをUI要素にバインドすることができる。(Data Binding)

Googleが公開しているサンプルコードはこちら

所感

MVPにおけるPresenterとMVVMにおけるViewModelの役割は結構似ているなと感じました。まずは役割をMVPの3つに分け、PとVの部分をDataBindingでバインドしてやれば、とりあえずの形だけはMVVMになるのかなという気がしました。あくまで形だけですが(笑)

また、MVVMの方がDataBindingの知識が必要な分、ハードルは高めかなと思います。そういう意味でも、学習するならまずはMVPを理解してからMVVMを始めた方がいいかもしれないですね。もちろん、アーキテクチャはこの2種類だけではないので、他のやり方も学習しつつ、自分のプロジェクトに合った設計方法を見つけていけたらいいなと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

リリースビルドで「SocketException: Failed host lookup: 'xxxxxxx' (OS Error: No address associated with hostname,)」と言われたときは。

要約

  • デバッグモードでリクエスト飛ばせたのに、リリースビルドだと表題のエラーが出る
  • ちゃんと<uses-permission android:name="android.permission.INTERNET" />を付与しよう!
  • 公式ドキュメントを、見よう

事例と原因、対策

Flutterアプリでhttpなど通信を行うアプリを作っている時に気を付けたい事例です。
デバッグビルドやiOSのリリースビルドでは問題なく動いたのに、リリースビルドのAndroidのみ通信部分でエラーが起きる場合があります。

エラー内容としては以下の通りです
SocketException: Failed host lookup: 'xxxxxxx' (OS Error: No address associated with hostname,)

原因としては、インターネットに関するパーミッションが付与されていないことでした。

Android開発を経験した人にはお分かりだと思いますが、Androidではネットワークを使用するためには
<uses-permission android:name="android.permission.INTERNET" />
をAndroidManifest.xmlに付与しないといけません。

しかしながらflutterのデバッグモードでは、このパーミッションを付与せずともネットワークが使用可能になっています。
https://flutter.dev/docs/deployment/android#reviewing-the-app-manifest

Flutterから入った人、Android開発経験が久しぶりな人には、ついうっかり起きてしまうのではないでしょうか。
Flutterの公式ドキュメントは内容が豊富なので(ただし英語)しっかり活用していきたいところです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者向け】Android×KotlinでTwitter4Jを使ってみる

はじめに

この記事は初心者の我々でもかけるAndroid アドベントカレンダーの11日目の記事です。
こういったものを書くのは初めての初学者ですが、自分と同じようにアプリ開発でTwitterを使ってみたい初心者の方が、この記事を読んで少しでも役に立てていただければ幸いです。

何か本文中で誤りがありましたら、ぜひコメント等でご指摘くださいm(_ _)m

この記事の内容

【対象者】
・Android開発でTwitterAPIを使ってみたい人
・Twitter4Jで色々調べてるけど、日本語文献でKotlinのコードが全然なくて困っている人
・よくわからないけれど、非同期処理でコルーチンとやらを使ってみたい人

※APIとは?等のことは説明しないので、各自でお調べください。

【やってみること】
・Twitter4Jの基本的な使い方を学んでみる
・ベアラートークンを使ってツイートを取得してみる
・アクセストークンを使ってツイートを取得・投稿してみる
・Coroutines(コルーチン)についてちょっとだけ勉強してみる

※エラー処理は一切していません。これも読了後に各自でお調べいただければと思います。

筆者の環境は以下の通りです
・Android Studio(Version 3.5.1)
・Kotlin (Version 1.3.50-release-Studio3.5-1)
・Twitter4J (Version 4.0.7)
・Kotlin coroutines用ライブラリ(Version 1.3.2)

前準備:Twitter API キーの取得

まずはTwitterでDeveloperアカウント登録をする必要があります。やり方についてですが、以下の方がとても詳細に流れを書いてくださっているので、ぜひそちらをご参考ください。

Twitter API 登録 (アカウント申請方法) から承認されるまでの手順まとめ ※2019年8月時点の情報

私は2019年10月ごろに登録しましたが、2か月しか経っていないのでほとんど内容は同じでした。英語で記述しなければならないところはGoogle翻訳を使えば何とかなります。最近のGoogle翻訳はすごいですね!!

上記リンクの内容に補足で、もしDMを送りたいとかの場合はメニューの中のPermissionsタブを開いて権限を変更してください。
permission_fix.PNG
なお、APIキーの取得の中で「Consumer Key(APIキー)」「Consumer Secret(APIシークレットキー)」「Access Token」「Access Token Secret」の4種類が出てきます。
前者2つはTwitterAPIを使うのに必ず必要で、後者2つは投稿する系の使い方をする時に必要になります。
このアクセストークンはユーザーごとに固有で、「Twitterクライアントを自作する」みたいなことをする場合はそれを最初に取得する処理が必要になってきます。

(登録の中でもらえたアクセストークンは自分のアカウント専用のものです。例えばこれを使ってツイートすると、登録に使ったアカウントでツイートすることになります。)

Twitter4Jを導入する

TwitterAPIを使って何かする場合は、認証やらJSONやら色んな事を考えなければいけないのですが、それを全部やってくれるという神様みたいなライブラリがあります。それがTwitter4Jです!製作者様に圧倒的感謝…!

では、さっそくプロジェクトに導入していきましょう。
Gradleに以下の文を書き加えてSyncしてください。調べてみると「.jarをダウンロードしなきゃいけない」とか書いてあるものもありますが、この一文を加えるだけで大丈夫です。
また、後ほど非同期処理にコルーチンを用いるので、それも一緒に追加しておきます。

app/build.gradle
dependencies{
        //省略
        implementation 'org.twitter4j:twitter4j-core:4.0.7'
        implementation"org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2"
        implementation"org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2"
        //省略
}

これでTwitter4Jの導入は終わりです。簡単ですね!
実際に使う際にはインターネット通信を行うので、マニフェストに許可する旨を書いておきましょう。

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest>
    <application>
        <activity>
            <!-- その他省略 -->
        </activity>
    </application>
    <uses-permission android:name="android.permission.INTERNET"/>
</manifest>

実際にツイートしてみる

それでは実際にツイートを投稿してみましょう。そのためには、まず先ほど入手した各種キーをセットする必要があります。セットする方法は「プロパティファイルを使う方法」「プログラムから直接設定する方法」があるので、ここでは両方紹介しておきます。

プロパティファイルを使う方法

まず左側のプロジェクトツールウィンドウがProjectビューになっているのを確認して、app/src/mainで右クリック、New→Directoryを選択してresourcesというフォルダを作成します。
次にそのresources上で右クリックしてNew→Fileを選択、名前をtwitter4j.propertiesとして作成しましょう。
最終的にAndroidビューでこのようになっていれば大丈夫です。

フォルダ構成.PNG

そうしたら、twitter4j.propertiesを開いて中身を次のように書いてください。

twitter4j.properties
debug=true
oauth.consumerKey=XXXXXXXXXXXXXXXX
oauth.consumerSecret=XXXXXXXXXXXXXXXXXX
oauth.accessToken=XXXXXXXXXXXXXXXX
oauth.accessTokenSecret=XXXXXXXXXXXXXXXX
#それぞれ右側に各種キーをコピペする

以上で設定は完了です。それでは試しにツイートしてみましょう。いったん何も考えず、次のようにコードを打ち込んでみてください。
また、MainActivityのレイアウトファイルはTextViewとButtonを1つずつ置いたものを用意しておきましょう。

(ここの処理はもっと良い書き方がありますが、ここではlaunchを使いたい&初心者(=自分)が流れとして見やすいようにこうしています。)

MainActivity.kt
//プロパティファイルを使う場合
class MainActivity : AppCompatActivity(), CoroutineScope {

    //Coroutinesを扱うための設定(詳細は後述)
    private val job = Job()
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + job

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        button.setOnClickListener {                  //ID:buttonのボタンをクリックした際の処理
            onClick()
        }
    }

    private fun onClick() {
        launch {
            textview.text = "Now Sending"            //ここはメインスレッドで動作するのでViewの変更ができる

            async(context = Dispatchers.IO) {
                val twitter = TwitterFactory().getInstance()
                twitter.updateStatus("Test tweet from Twitter4J #twitter4j")      //ツイートの投稿
            }.await()                                //.await()で通信処理が終わるまで待機

            textview.text = "finish"
        }
    }

    override fun onDestroy() {
        job.cancel()                                 //すべてのコルーチンキャンセル用
        super.onDestroy()
    }
}

実行してボタンを押すと、TextViewの表示が変わってツイートされるはずです。実際に確認してみましょう。

post1_fix2.PNG

ツイートが投稿されました!

プログラムから直接設定する方法

この方法はプロパティファイルを作成する必要がありません。先ほど書いたコードの中のonClickを少し変更します。変更後、同様に実行してみてください。(プロパティファイルは消しておいてください。)

MainActivity.kt
//プログラムから直接設定する場合
    private fun onClick() {
        launch {
            textview.text = "Now Sending"

            async(context = Dispatchers.IO) {
                val cb = ConfigurationBuilder()
                cb.setDebugEnabled(true)
                    .setOAuthConsumerKey("XXXXXXXXXXXXXXXX")
                    .setOAuthConsumerSecret("XXXXXXXXXXXXXXXX")
                    .setOAuthAccessToken("XXXXXXXXXXXXXXXX")
                    .setOAuthAccessTokenSecret("XXXXXXXXXXXXXXXX")                //各種キーの設定

                val tf = TwitterFactory(cb.build())
                val twitter = tf.getInstance()
                twitter.updateStatus("Test2 tweet from Twitter4J  #twitter4j")    //ツイートの投稿
            }.await()

            textview.text = "finish"
        }
    }

こちらも同様に投稿できていますね!

ベアラートークンを使ってツイートを取得してみる

先ほどは自分で取得したアクセストークンを使ったため投稿ができていましたが、本来はユーザーごとに認証画面を表示し、アクセストークンを入手しなければなりません。
ただし、「ハッシュタグを検索する」「ある人のツイートだけを見てみる」といった「投稿しない系の処理」だったら、アクセストークン無しに行うことができます。
それを実現するのがベアラートークン(Bearer Token)です。

まず、先ほど作成したtwitter4j.propertiesの中身を次のように書き換えてください。

twitter4j.properties
debug=true
enableApplicationOnlyAuth=true
http.useSSL=true
oauth.consumerKey=XXXXXXXXXXXXXXXX
oauth.consumerSecret=XXXXXXXXXXXXXXXXXX

そうしたらMainActivity.ktの中のonClickを次のように書き換えます。

MainActivity.kt
//Bearer Tokenでの処理
//自分のツイートを検索してログに出力

    private fun onClick() {
        launch {
            textview.text = "Now Loading"

            async(context = Dispatchers.IO) {
                val twitter =  TwitterFactory().getInstance()
                twitter.oAuth2Token                               //ベアラートークンの取得

                var query1 = Query().query("from:(自分のアカウントの@以降)")    //指定したIDのユーザーの投稿を検索
                var result: QueryResult = twitter.search(query1)

                for (status in result.tweets){                    //取得したツイート数分ログに出力
                    Log.d("status", status.text)
                }
            }.await()

            textview.text = "finish"
        }
    }

ベアラートークンの取得は、上記の真ん中あたりにあるたった2行だけです!この2行だけでツイートの取得や検索ができるようになっています。
上では試しに自分のツイートを検索してログに出力しています。ちゃんと表示されましたか?

また、こちらもプロパティファイルを使わずコード上から設定することができます。以下のようにコードを変更してください。実行結果は上と同じになるはずです。

MainActivity.kt
//Bearer Tokenでの処理
//コード上から設定

    private fun onClick() {
        launch {
            textview.text = "Now Loading"

            async(context = Dispatchers.IO) {
                val cb = ConfigurationBuilder().apply {
                    setDebugEnabled(true)
                    setOAuthConsumerKey("XXXXXXXXXXXXXXXXXX")
                    setOAuthConsumerSecret("XXXXXXXXXXXXXXXXXX")
                    setApplicationOnlyAuthEnabled(true)
                    //setUseSSL(true)  これは不要になった?
                }

                val tf = TwitterFactory(cb.build())
                val twitter = tf.getInstance()
                twitter.oAuth2Token                       //ベアラートークンの取得

                var query1 = Query().query("from:(自分のアカウントの@以降)")
                var result: QueryResult = twitter.search(query1)

                for (status in result.tweets){            //取得したツイート数分ログに出力
                    Log.d("status", status.text)
                }
            }.await()

            textview.text = "finish"
        }
    }


アクセストークンを使ってツイートを取得・投稿してみる

とてもお手軽なベアラートークンですが、前述した「投稿不可」のほかにも「フォローしてる鍵アカウントを見ることができない」「プロフィールを変更できない」といった制限が多くあります。
もしそのような処理をしたい場合は、アクセストークンを入手する処理を入れる必要があります。ただ、これまでの処理と比べてかなりやることが多いです。順序としては以下のようになります。

1、コールバック用のアクティビティの追加
2、AndroidManifestの更新
3、Twitter DeveloperサイトのApp detailsの更新
4、各コードの変更

1、コールバック用のアクティビティの追加

アクセストークン取得の流れは、一度アプリからWebサイトにジャンプ→そこでTwitterログイン→アプリに返ってくるという感じです。その返ってくる用のアクティビティを追加します。

emptyactivity1.png
上の図のようにEmpty Activityを選択して、Activity NameCallBackActivityとして作成してください。
自動的にレイアウトファイルも作成されるはずなので、そこには適当にTextViewを2つ置いておきましょう。
ここではTextViewのIDをそれぞれtextView_tokentextView_secretとしています。

2、AndroidManifestの更新

次にAndroidManifest.xmlを開きます。上の図のようにアクティビティを作成したら、ここに新しくCallBackActivity<activity>タブが作られているはずなので、この部分を以下のようにしてください。

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest>
    <application>
        <activity android:name=".CallBackActivity" android:label="@string/app_name"
            android:launchMode="singleInstance">
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="callback"/>
            </intent-filter>
        </activity>
    <!-- その他省略 -->

詳しい説明は省略しますが、これによってWebブラウザ上で認証した後にアプリに戻り、認証情報を受け取ることができます。

3、Twitter Developerサイトでの設定

-前準備:Twitter API キーの取得-のところでTwitter Developerサイトにアプリを登録したと思います。もう一度その画面に戻り、App detailsタブを開いて右上のEditボタンをクリックしましょう。

そうしたら、その中にあるCallback URLscallback://と設定してください。

callback.PNG

先ほどAndroidManifest.xmlの中で<data android:scheme="callback"/>という一文があったと思います。これと同じになるようにCallback URLsを設定しました。
変更が終了したら忘れずにSaveボタンを押して変更を反映させておきましょう。

4、各コードの変更

ようやくコードの変更に入ります。まずはMainActivity.ktを次のように変更しましょう。
(以下のコードはこちらのサイトを大変参考にさせていただいております。)

MainActivity.kt
class MainActivity : AppCompatActivity(), CoroutineScope {

    //コルーチンを使うための準備
    private val job = Job()
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + job

    //認証オブジェクトの作成
    companion object{
        val mOauth = OAuthAuthorization(ConfigurationContext.getInstance())
        lateinit var mRequest: RequestToken
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        button.setOnClickListener {
            onClick()
        }
    }

    private fun onClick() {
        launch {
            mOauth.setOAuthConsumer("XXXXXXX(ConsumerKey)", "XXXXXXX(ConsumerSecretKey)")
            mOauth.oAuthAccessToken = null
            var uri: Uri?

            async(context = Dispatchers.IO) {
                mRequest = mOauth.getOAuthRequestToken("callback://CallBackActivity")    //コールバックの設定

                var intent = Intent(Intent.ACTION_VIEW)
                uri = Uri.parse(mRequest.authenticationURL)
                intent.data = uri
                startActivityForResult(intent, 0)            //暗黙インテントでWebブラウザを呼び出して認証
            }                                                //認証情報を受け取るためにstartActivityForResult()
        }
    }

    override fun onDestroy() {
        job.cancel()
        super.onDestroy()
    }
}

次に、先ほどCallBackActivityを作成したときに自動生成されたCallBackActivity.ktを開き、以下のようにしてください。

CallBackActivity.kt
class CallBackActivity : AppCompatActivity() , CoroutineScope{

    //コルーチンを使うための準備
    private val job = Job()
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + job

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_call_back)

        val uri = intent.data                //認証情報の受け取り
        if (uri != null && uri.toString().startsWith("callback://CallBackActivity")){
            launch {
                val mtoken = async(context = Dispatchers.IO){           //asyncは値を返せる
                    val varif = uri.getQueryParameter("oauth_verifier")
                    val token = MainActivity.mOauth.getOAuthAccessToken(MainActivity.mRequest, varif)
                    return@async token
                }.await()

                textView_token.text = mtoken.token          //ID:textView_tokenのTextViewにアクセストークンを表示
                textView_secret.text = mtoken.tokenSecret   //ID:textView_secretのTextViewにシークレットキーを表示
            }
        }
    }

    override fun onDestroy() {
        job.cancel()
        super.onDestroy()
    }
}

これで作成はすべて完了です!
実行してみると「ボタンを押す→Webページ開く→見たことあるような認証画面出てくる→ログインする→アプリに戻ってアクセストークンキーとシークレットキーが表示されている」の一連の流れが確認できると思います!
これでそれぞれのアカウントにフルアクセスできるようになりました!

あとはこの2つのキーを端末に保存しておけば、次回以降はログイン処理が必要なくなります。

以降のツイートの取得や投稿のやり方に関しては、前述したものとほとんど同じです。ぜひ自分で試してみてください。

Coroutines(コルーチン)について

先ほどまでのコードの中には、launch{...}という見慣れないものが出てきたかと思います。これはKotlin特有の機能で、これを使うことで非同期処理をとても簡単に行うことができます。

どうして非同期処理をしなければならないのか

Androidはシングルスレッドモデル(画面のビューなどの操作を1つのスレッドのみが担当する)です。アプリを実行した際にメインスレッド(UIスレッド)が生成され、画面の操作やビューの更新は全てメインスレッドで担っています。
その時に「画像のダウンロード」のような時間がかかる処理をメインスレッドで行ってしまうと、その処理が終わるまでユーザーは画面を操作できず、俗にいう「固まった」状態になってしまいます。また、その状態が続くとAndroid側からANRと呼ばれるエラーを出されてしまうこともあります。

したがって、Androidでは基本的なルールとして「インターネット通信を含む処理は別スレッドでやらなければならない」ということになっています。

コルーチンを使わない場合

通信処理は別スレッドでやる必要がありますが、例えば「ツイートを取得→画面に表示」の場合だと、通信処理に加えてビューの操作もしなければなりません。でも、ビューの操作はメインスレッド以外からはできません。めんどくさい!
スレッド間のやり取りのほかにも色々と考えなきゃいけないことが多く、初心者である我々にとっては非同期処理の実装はとても大変です。

【Android】今更ながらAsyncTaskの警告に対応した話をまとめる【開発】
【Android】 非同期処理 AsyncTaskの使い方  ←おすすめ

上記のリンクは一例ですが、初めての単語がいっぱい出てきてとても疲れますね。

余談ですが、下のリンク先の方が書かれている記事はどれもめちゃくちゃわかりやすい上に更新が最近(2019年8月頃)なので、Androidアプリ開発を始めた初心者の方はぜひ全てに目を通した方がいいと思います。

コルーチンを使う場合

コルーチンを使う場合は、先ほどまで書いていたコードそのままです。使わない場合に比べてとてもコードがすっきりしていますね!
コルーチンについては、正直私が説明するよりもよっぽど分かりやすい記事がQiitaにも多数投稿されていますので、そちらを紹介させていただきます。

入門Kotlin coroutines
逆引き Kotlin Coroutines
Androidと非同期処理 とCoroutine1.0.0
図で理解する Kotlin Coroutine

特に後ろ2つのリンク先は、本記事を書くうえでも大変参考にさせていただきました。
この記事にあるコードで「どうしてそうなるか」は全て上記リンクで説明されている(特に最後)ので、ぜひ皆さんも一度目を通していただけたらと思います。

おわりに

以上でTwitter4Jの基本的な使い方は終わりです。ほかにも画像の取得やプロフィール関連、DMなど出来ることはたくさんありますが、IDEの補完機能で出てくるやつを眺めたりググったりして勉強していただけたらと思います。

この記事では「どういうことをどんな意味でやっているの?」よりも「いいからさっさと動かしたい!」に焦点を当てていますので、詳しい説明は一切していません。
私自身、最初から理屈を学ぶよりも「とりあえず動かせるようになって、そこから意味を考える」勉強法じゃないと頭に入らないタイプなので、私と同じような人の最初の手助けができていれば幸いです。

こうした記事を書くのは初めてなので分かりやすく書けているか不安ですが、何か不明な点や間違っているところがあれば是非お教えくださいm(_ _)m

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Firebase App Distribution を使ってみた

Firebase App Distributionとは

Firebase App Distributionは開発途中のアプリケーションをテスターに配布するプラットフォームで、AndroidとiOSに対応しています。

今回はFirebaseコンソールとGradleを使用したapkファイルの配信方法を紹介します。

特徴

  • クロスプラットフォーム
    • AndroidとiOSに対応しています
  • テスターの管理
    • テストチームをグループにまとめて管理できます
    • 招待メールを作成したり招待リンクを作成できます
    • 招待した人数、招待を承認した人数、インストール数など把握できます
  • テストアプリの配信方法が豊富なのでお使いのCI環境に合わせてすぐ導入できます
    • Firebaseコンソールから配布
    • firebaseコマンドから配布
    • gradleから配布(Android)
    • fastlaneから配布
  • Crashlyticsを組み合わせるとビルドで安定性の指標を自動的に取得できます
  • 新しいアプリを配信すると準備ができたことがテスターに通知されます

Firebase コンソールを使用して配信

Firebaseコンソールを使用すると気軽にアプリを配信できます。

下準備

配信

App Distributionのページを開き利用契約を確認&チェックし開始を押します

Screen Shot 0031-12-03 at 20.45.48.png

予め用意したapkファイルをアップロードします

Screen Shot 0031-12-03 at 21.02.09.png

アップロード完了後、リリースノートを書きテスターを追加します

Screen Shot 0031-12-03 at 21.02.56.png

テスターに追加すると、招待メールがテスターに届きメールにあるリンクにアクセスするとアプリをダウンロードできます。なおFirebase App Distributionコンソール上では、招待済、承認済、ダウンロード済の数字が状況に合わせて変化するのでテスターの動向を確認できます。

Screen Shot 0031-12-03 at 21.03.52.png

以上でアプリを配信できました。しかしこれだと毎回手動でアップロードすることになるので、CI等に組み込み自動化したいところです。次にGradleを使用した配信方法を紹介します。今回はGradleを使用しましたが、Firebase CLIやfastlaneにも対応しており、使い方も大きく変わらないので、ご自身の環境に合わせ使い分けていただければと思います。

Gradle を使用して Android アプリをテスターに配布する

app/build.gradleを編集

app/build.gradleにFirebase App Distributionのスクリプトを追加します。

apply plugin: 'com.android.application'

apply plugin: 'com.google.firebase.appdistribution'

buildscript {
    repositories {
        google()
    }
    dependencies {
        classpath 'com.google.firebase:firebase-appdistribution-gradle:1.2.0'
    }
}

認証

appDistributionLoginコマンドまたはサービスアカウントを使用し認証します。

appDistributionLoginでOAuth認証

$ ./gradlew appDistributionLogin

実行すると認証用ページのURLが表示されるのでアクセスし認証します。

Please open the following address in your browser:
  https://accounts.google.com/o/oauth2/......

下記のメッセージがでれば認証完了です。

BUILD SUCCESSFUL in 49s
1 actionable task: 1 executed

Refresh tokenがでるので FIREBASE_TOKEN 環境変数にtokenをセットします。

Refresh token: hogehogehoge

export FIREBASE_TOKEN=hogehogehoge

サービスアカウントで認証したいひとへ

サービスアカウントで認証する場合はこちらをご参考ください。

https://firebase.google.com/docs/app-distribution/android/distribute-gradle#authenticate_using_a_service_account

配布プロパティを構成する

次にリリースノートとテスターを指定します。今回はシンプルに下記のような構成にしました。

 buildTypes {
        release {
            firebaseAppDistribution {
                releaseNotes="テスト配信" // リリースノート
                testers="ali@example.com, bri@example.com, cal@example.com" //テスター
            }
        }
        debug {
            firebaseAppDistribution {
                ...
            }
            testCoverageEnabled true
        }
    }

プロパティのパラメータは releaseNotesやtesters以外にも下記のようなパラメータや「デモ版」と「完全版」のプロダクトフレーバーを設定できます。詳しくは公式ドキュメントをご参考ください。

  • appId
    • FirebaseのアプリID
  • serviceCredentialsFile
    • サービスアカウントのパス
  • releaseNotes
    • リリースノートを文字で指定できます
  • releaseNotesFile
    • リリースノートが書かれたテキストファイルをパスで指定できます
  • testers
    • テスターのメールアドレスを文字で指定できます
  • testersFile
    • テスター情報が書かれたテキストファイルを指定できます

アプリを配信

app/build.gradleに追加したので app/ 配下でコマンドを実行します。
アップロードコマンドは名はパラメータやプロダクトフレーバーの設定で名称が変わるので下記で名前を確認します。

コマンドを確認
./gradlew tasks --all

Other tasks
-----------
appDistributionLogin
appDistributionUploadDebug
appDistributionUploadRelease

下記コマンドでapp-debug.apk を作成しFirebaseコンソールにアップロードします

$ ./gradlew assembleDebug appDistributionUploadDemoDebug

アップロードが成功するとテスターにアプリが配信されます

Screen Shot 0031-12-04 at 8.34.28.png

招待リンクの作成

招待リンクを作成するとテストアプリを広く通知できます

新しい招待リンクをクリック(グループやドメイン制限は特にかけることもできる)

Screen Shot 0031-12-04 at 8.35.10.png

copy linkからURLを取得し招待リンクにアクセスします

Screen Shot 0031-12-04 at 8.36.42.png

メールアドレスを入力しサインアップするとテスターに招待されます

GitHub Actionsと連携

こちらを使用すればGitHub ActionsにFirebase App Distributionを組み込みできます。

https://github.com/marketplace/actions/firebase-app-distribution

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RailsエンジニアがReactNativeエンジニアになるにあたって苦しんだところワースト3

top.png

はじめに

こんにちは、株式会社OKANで「おかんPay」というプロダクトのPdMをしながらも、自らバリバリReact Nativeで実装している かなすぎ と申します。
元々は、RailsでOKANの社内システムを構築していたサーバーサイドエンジニアだったのですが、志願してReact Nativeを使うアプリエンジニアになってから約半年が立ちました。
そこで、RailsエンジニアがReact Nativeを扱うアプリエンジニアになる時に苦しんだ箇所をまとめることで、自分と同じようにReact Nativeを新たに勉強する人の助けになればと思いながら記事を書きました。

想定読者

  • Railsエンジニアだが、これからReactNativeアプリもつくってみたいなって思っている人
  • React.js, Vue.jsなどフロントエンドフレームワークをがっつり実装したことないけど、React Nativeでアプリ作ってみたい人

RailsエンジニアがReactNativeエンジニアになるにあたって苦しんだところワースト3

RailsではJavascriptも使うことはあったのですが、MVCでいうViewのDOM操作をjQueryで軽く行うくらいの経験値であったので、ES6でがっつりロジックを書くというのも覚えることが多くて大変でした。また、Vue.jsやReact.jsなどで用いられる状態管理や、React.js特有のライフサイクルメソッドやJSX。そして、ウェブ開発では存在しないアプリ特有のDeepLinkや画面遷移の方法なども大きく異なっているので、キャッチアップに苦労しました。

ワースト1位:ES6(ES2015)

React NativeはES6で書きます。Javascriptは、ES6以降とそれより前では使える機能は大きく変わりました。現在流行りのフロントエンドのフレームワークであるAngular.js、React.js、Vue.jsなどでも、基本的にはES6以降の書き方で書くことが多いです。JavascriptはViewの見た目を動的に変える軽量な言語という認識でしたが、ES6を勉強してからは、認識が大きく変わりました。

イマドキのJavaScriptの書き方2018 - Qiita
ES2015(ES6) 入門 - Qiita

アロー関数

ES6以前では、関数を定義する時は、functionを使用していましたが、ES6以降では、アロー関数を用います。記号ばっかりで最初は慣れないかと思いますが、慣れれば、短い記述で関数を定義できるので非常に便利です。()を省略できたり、様々な記述パターンがあるので、引数の数など場合によって使いわけましょう。

// 何もしない関数
() => {}

// 引数が一つだったら「()」不要
value => {}

// ブロックないが1行しかないときは、「{}」不要
(a, b) => a + b

// オブジェクトは「()」が必要
(a, b) => ({ a: b, b: a })

// 複数行はいつも通り
(a, b) => {
  const c = a * 100;
  return {
    c,
    d: b / 100,
  };
}

アロー関数
JavaScript アロー関数を説明するよ - Qiita
アロー関数を使って効率的にJavaScriptを記述する

import / export

モジュール毎に機能を管理する時に使います。モジュールで機能を管理することによって、他コードとの依存性を少なくして保守性を高めたり、変数名の競合を防いだり、汎用性の高いモジュールをつくって同じコードを繰り返すのを防いだりできます。

import React from 'react';
import {
  View,
  Text,
  StyleSheet,
  FlatList,
  Image,
  TouchableHighlight,
  Alert,
  Linking,
} from 'react-native';

export default ItemList;
export const calories;
export function pay(){...}
export class Payment {...}

import
export
ES6のexportについて詳しく調べた - Qiita

非同期処理 async / await

hidouki.js
// 非同期のpayメソッド
const pay = async (amount) => {
  const items = await getItems();
  const totalAmount = amount + tax;
  try {
    await this._payByCreditcard(totalAmount, items);
    return true;
  catch(e) {
    console.warn(e);
    return false;
  } 
};

非同期処理には、かなり苦しめられました。Rubyは同期処理できる言語なので、コードを書いたら上から順に実行されます。しかし、Javascriptは非同期であり、たとえばAPIをコールしても、その結果を待たずに、次の行のコードの処理を進めることができてしまうのです。そこで、Promiseオブジェクトを用いて非同期処理を行い、asyncで非同期関数を定義し、awaitで非同期の結果が帰ってくるまで待つことができるのです。

JavaScriptの同期、非同期、コールバック、プロミス辺りを整理してみる - Qiita
【JavaScript入門】誰でも分かるPromiseの使い方とサンプル例まとめ! | 侍エンジニア塾ブログ(Samurai Blog) - プログラミング入門者向けサイト
async/await 入門(JavaScript) - Qiita

ワースト2位:React

React NativeではReactを使ってAndroidアプリとiOSアプリを作ることができます。したがって、React自体の状態管理であったり、JSXなどReact独特の考え方・概念をキャッチアップする必要があります。フロントでReactを使用したことがあれば問題ないのですが、RailsをAPIモードで使用していた経験もなく、SlimでViewを書いていたので、Reactの概念を理解するのも大変でした。

React Native · A framework for building native apps using React
React - ユーザインターフェース構築のための JavaScript ライブラリ

Component

ReactはComponent志向で作られます。Railsエンジニアだと、Componentとは?というところのキャッチアップから始める必要があります。Componetをざっくり説明すると、UIを独立させて再利用できるようにして、propsという任意の値を親要素から受け取り画面上に表示させるものです。この文章だけ読んでもわけが分からないと思うので、サンプルコードなどを見ながら理解すると、理解が深まると思います。

// ■ Reactのコンポーネントたち

// 【Component】
// Stateを持っていて、再レンダリングの条件を自分でチューニングしたいときに使う。
class FooComponent extends React.Component {
  render() {
    return <View />;
  }
}

// 【PureComponent】
// Stateを持っていて、再レンダリングの条件をチューニングしないときに使う。
class BarComponent extends React.PureComponent {
  render() {
    return <View />;
  }
}

// 【Functional Component】
// Stateを持たなくて良い場合に使う。
const FuncComponent = props => {
  return <View />;
};

フロントエンドのコンポーネント設計に立ち向かう - Qiita

状態管理(state)

stateとは、Componentの状態を管理するデータです。たとえば、isPayableというフラグがたっていたら支払いのロジックを動かすということをDBでフラグを管理しなくても、フロント側で管理することができるのです。Reactでは、eduxという状態管理のライブラリを使用することが多いのですが、弊社のアプリは軽量なアプリなのでunstatedというライブラリを用いて状態管理をしています。個人的には、Reduxは冗長になってしまうので、unstatedの方が直感的でわかりやすくオススメです。

[React Native] 基本を学ぶ - Stateでコンポーネントの状態を管理する | Developers.IO
コンポーネントの state - React
jamiebuilds/unstated
次の状態管理はReduxをやめてunstatedにする理由 - Qiita

props

Componentは、propsという任意の引数(正確には引数ではありません)を受け取って、値を当てはめたReact要素を返します。初めてReactを勉強した時は、stateとpropsをごっちゃにしてしまい、それぞれの役割を正確に理解できませんでしたで苦労ポイントです。

[React Native] 基本を学ぶ - Propsでコンポーネントのパラメータを設定する | Developers.IO
コンポーネントと props - React

JSX

JSXはReactのComponent内でのXML風の構文です。React.jsでは、HTMLでお馴染みの<h1>タグなどが利用できるのですが、React Nativeでは、<View><Text>といった見慣れないタグを扱います。さらに、アプリエンジニアの方は理解しやすいそうなのですが、とかとかアプリ独特のComponentを使用する必要があるので、実際に動かしてみれば、「あーこれか。アプリで使ったことある」と思うのですが、Componentの名前などを覚えるのには一苦労しました。

ActivityIndicator · React Native

class Payment extends React.Component {
  // ここがstateで状態をComponent内で管理している
  state = {
    isPayable: true,
    items: {},
    totalAmount: 0,
  };

  _onPressPayment = () => {
    { isPayable } = this.state;
    if (isPayable) {
      const result = await pay(amount);
      if (result) {
        this.setState({ isPaylable: false});
      }
    }  
  }

  render() {
    return (
      // <Text>タグや<TouchableHighLight>がJSX
    // onPress={this._onPressPayment}がprops
      <TouchableHighlight onPress={this._onPressPayment}>
        <Text>支払いをする</Text>
      </TouchableHighlight>
    )
  };
}

ライフサイクルメソッド

ここが個人的には一番ReactNativeエンジニアになって辛かったです。未だに、あれこれってComponentDidMountであってるよね?って確認したくなります。Reactでは複数回renderメソッドが走るので、どのタイミングではどんなデータを用意してなければならないといったことも考慮しなくてはならないので、奥が深いです。さらにReactHooksでの書き換えも「おかんPay」では行っているので、useEffectなどの使い方は悪戦苦闘しています。

lifecycle.js
// 色々なライフサイクルメソッドがあります。
componentDidMount()
componentDidUpdate(prevProps, prevState, snapshot)
componentWillUnmount()

【React Native】React Native ライフサイクルメソッドについて|Fresopiya
React component ライフサイクル図 - Qiita

ワースト3位:ReactNativeアプリやアプリ開発特有の概念

Web開発では存在しない概念であったり、Web開発の場合と概念が異なることがあり、Railsエンジニアの方にとっては、新しく勉強することが多いです。そこで、自分がReactNativeエンジニアになるにあたって特に苦労したことをまとめました。

React Navigationの画面遷移

画面遷移については、Railsでもある概念です。Routes.rbでルーティングを設定して、redirectやrenderを用いて画面遷移を実現します。しかし、ReactNativeなどアプリの開発では、画面を重ねていく、つまりStackしていくといったイメージの方がしっくりくると思います。RailsでWeb開発だけをしていた自分にとっては、なかなか理解するのに時間がかかるところでした。ライブラリによっても仕様は異なるのですが、弊社ではReact Navigationを使用しています。

okanpay_navigation.gif

React Navigation · Routing and navigation for your React Native apps
React Navigation 3.x チートシートつくってみた - Qiita

DeepLinkとかLinking

アプリ同士の連携をするのには、DeepLinkやLinkingというモジュールをReact Native開発では使います。アプリエンジニアにとってはお馴染みの概念なようなのですが、Railsエンジニアの自分にとっては、未知の世界でした。urlを叩いたりするとアプリを開いたりする機能はこうやって実現するのかと素直に感心した領域です。日本語のドキュメントが少ないので、調べるのも英語だったので一苦労でした。今回紹介するのは、React NavigationのDeepLinkになります。

Deep linking · React Navigation
Linking · React Native
ディープリンクをめぐる歴史とReact NativeにFirebase Dynamic Linksを導入する手順 - KitchHike Tech Blog

アプリのリリース

iOSとAndroid共にリリースを何度も経験をしているのですが、慣れれば作業なのですが、一番初めのリリースはこんなにも時間がかかるものなのかと驚愕しました。また、弊社プロダクトの「おかんPay」のReactNative版を初リリースしようとしたタイミングでAndroidの審査も始まったらしいというタイミングだったので、てんやわんやでした。さらに、Google Play ConsoleのUIや仕様はかなり頻繁に更新されてしまうので、Qiitaの記事なども最新記事を追わないないと、「そのボタンどこにあるの?」みたいなことは多発しました。初めてのアプリリリースには、余裕をもったスケジュールにすることをオススメします。

iPhoneアプリ申請やAppleの審査に関するメモ - Qiita
Androidアプリにも審査がされるようになった件 - Qiita
React NativeでiOS, Androidのストア公開のTips - Qiita

iOSとAndroidのレイアウト差分

iOSとAndroidでは、PickerやCalendarで日付を選択する時など、多くの場合で、レイアウトが異なります。ReactNativeたった1つの言語でiOSとAndroidの両方のアプリを作れることは非常に大きなメリットではあるのですが、両者を全く同じに扱って言い訳ではなく、レイアウトをPlatformによって変更する必要があります。

また、エミュレータと実機の差分もあったり、Androidでは、HuaweiやSonyといった機種依存でレイアウトが変わったり、Androidのバージョンによってもレイアウトが崩れたりします。したがって、Androidすべての機種で実機テストをすることはできないので、レンタルサービスなどを用いてテストしたりしています。

import {Platform, StyleSheet} from 'react-native';

const styles = StyleSheet.create({
  width: Platform.OS === 'ios' ? 200 : 100,
});

【React-Native】iOS, Android両対応する。 - Qiita
配備機種一覧 - 実機検証用スマホ・モバイルデバイスレンタル|テスト受託サービスのケータイラボ

最後に

いかがだったでしょうか?Railsエンジニアには馴染みのない概念が多かったと思います。
この記事だけでは、もちろん十分ではないものの、自分がここ半年で苦労した箇所を中心にまとめてあるので、何かの助けになれば幸いです。

次は、SocialDogのアプリ開発している@t0m0120さんです。
私もSocialDogは、Twitterを活用するのに利用させてもらっています。どんな記事が読めるのか楽しみです!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Android]Navigation でシンプルな画面遷移を最速で実装する

はじめに

JetpackNavigation はすごく便利ですよね。
最近ちょくちょく利用するのですが実装の仕方を忘れてしまいます。

なので忘れても Navigation を最速で実装できるようにするため、
1つの Activity で 複数のFragment を切り替えるアプリの作成方法をまとめます。

Dec-04-2019 09-02-30.gif

準備

Navigation を利用したアプリケーションを作成するための下準備をします。
次の手順で必要なライブラリやクラス、レイアウトを生成していきます。

Navigation をセットアップする

次の内容を dependencies に記述するとNavigationをインストールできます。
JavaKotlin でインストール方法が違うので注意が必要です。

build.gradle(app)
dependencies {
    def nav_version = "2.1.0"
    implementation "androidx.navigation:navigation-fragment:$nav_version"
    implementation "androidx.navigation:navigation-ui:$nav_version"

    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
}

Fragment を作成する

Navigation を使った遷移で利用する Fragment を作成していきます。
次の手順で OneFragment, TwoFragment, ThreeFragment の 3つを作成します。

  1. Package Name を右クリックし、New → Fragment → Fragment(Blank) を選択する
  2. Fragment Name任意の名称 を入力し OK を押す
  3. Fragment にいらない記述が書かれているので消す
  4. LayoutFragment に遷移させるための Button を追加する
Fragment.kt
class OneFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_one, container, false)
    }
}

image.png

layout.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".OneFragment">

    <Button
            android:id="@+id/nav_button"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="@string/hello_blank_fragment" />

</FrameLayout>

実装

Navigation を利用するには次の3つを実装する必要があります。
それらの 3 つの実装を 1 つずつ進めていきます。

No 名称 役割
1 Navigation Graphの作成 宛先などナビゲーションの情報を管理する XML ファイルを作成する
2 Nav Hostの作成 Nav HostNavigation Graph の宛先を表示するための空のコンテナのこと
Fragment を表示するのでNavHostの実装であるNavHostFragmentを作成する
3 Nav Controllerで画面を遷移させる Nav Host に表示するコンテンツをNavigation Graphを元に
切り替える処理をするNav Controllerを取得し、画面を遷移させる。

Navigation Graph の作成

宛先などのナビゲーション情報を管理するNavigation Graphを作成します。

リソースファイルの作成

Navigation Graphはどこからどこへ遷移するかをリソースファイルに記述したものです。
なのでまずはNavigation Graphのリソースファイルを作成します。

  1. res を右クリックし New → Android Resource File を選択する
  2. File namenav_graph , Resource TypeNavigation を入力する

遷移先の組み立て

作成した nav_graph を開くとNavigation Editorが表示されます。
この Navigation Editor を利用して、どこからどこへ遷移するかを決めていきます。

image.png

次の操作でどこからどこへ遷移するか設定できます。今回はOneFragment から TwoFragment
TwoFragmentからThreeFragmentThreeFragmentからOneFragmentに遷移させます。

  1. ?をクリックして OneFragment, TwoFragment, ThreeFragment を追加する
  2. OneFragmentからTwoFragment, TwoFramgentからThreeFragment
    ThreeFragmentからOneFragment に線を引く

image.png

Nav Host の作成

Navigation Graph で定義したコンテンツを表示するための空のコンテナを作成します。
Activityのレイアウトにfragmentを追加した後、
次のAttributeを追加することでNav Host Fragmentを定義できます。

Attribute Value 説明
android:name NavHostFragment どのNavHostの実装を利用するか指定できる。
今回はFragmentを表示するためのNavHostFragmentを指定する。
app:navGraph @navigation/nav_graph どのNavigation Graphを利用するか設定できる。
今回はさっき作成したNavigation Graphをここで指定する。
app:defaultNavHost true NavHostFragmentBACKボタンを無効化できる。
今回はBACKボタンでホームに戻りたくないので有効化しておく
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <fragment
            android:id="@+id/nav_host_fragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:name="androidx.navigation.fragment.NavHostFragment"
            app:defaultNavHost="true"
            app:navGraph="@navigation/nav_graph" />
</FrameLayout>

Nav Controller で画面を遷移させる

Nav Controllerを取得し、Buttonをクリックした時にFragmentを遷移させる処理を実装する。

Nav Controller を取得する

Nav Controller を取得する方法は 3つあります。
次のように FragmentView , Activity から取得する方法があります。

Fragment.findNavController()
View.findNavController()
Activity.findNavController(viewId: Int)

Nav Controller で遷移する

Nav Controller を取得したら Navigate で画面を遷移できます。
このNavigateの引数にはID指定し、どこからどこへ遷移するか指定します。
このIDですがNav Graphを設定するNavigation Editorで矢印をクリックすると確認できます。

NavController.navigate(ID)

image.png

説明したNav Controllerを取得する方法とNav Controllerで遷移する方法を組み合わせて
次のようにFragmentにコードを実装すれば画面遷移できるようになります。

Fragment.kt
class OneFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_one, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        // Fragment を使った Nav Controller の取得
        val navController = this.findNavController()
        val button = view.findViewById<Button>(R.id.nav_button)
        button.setOnClickListener {
            navController.navigate(R.id.action_oneFragment_to_twoFragment)
        }

        // View を使った Nav Controller の取得
        val button = view.findViewById<Button>(R.id.nav_button)
        button.setOnClickListener { view ->
            val navController = view.findNavController()
            navController.navigate(R.id.action_oneFragment_to_twoFragment)
        }
    }
}

動作確認

実装が完了したらアプリケーションをインストールして動作確認します。
ボタンをクリックすると次のFragmentに遷移できてますね。

Dec-04-2019 09-02-30.gif

おわりに

Navigationは複雑そうに見えますが簡単です。
これからはNavigationで簡単に早く画面遷移を実装しましょう。

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Androidひとり開発】~ガラパゴス実装に陥らないための参考資料~

~この記事は、ひとり開発 Advent Calendar 2019の提供でお送りしています~
前回の5日目は @mikkame さんの Githubの日本語で書かれてるっぽいIssueを検索するサイトつくったでした。5日目なのにmikkameさんでした。(絶対これが言いたくて・・・)
OSS活動の入り口に、言語ハードルから下げていくのは全く正しい&なかなか思いきれなかった切り口ですね。ありがたい・・・。

Androidアプリ、作ってますか

作ってますか。(エコー)
開発手法の進化、ヤバいですよね。
Androidx、ついていけてますか。
findViewById、まだ使ってますか。
根絶する方法、調べましたよね。

「ググったらなんか出来たな」って、なりますよね。
「けどコレでええんか?」とも、なりますよね。

そこからの質問先、困りますよね。
インターネットの記事、古いですよね。

こんなとき、ひとり開発、つらくなりますよね。

僕は ついてゆけるだろうか
質問先のいないJetpackのスピードに

Jetpackの進化、名前の通り加速しすぎやんけってなガハハ!1

この記事でしゃべること

「道に迷ったらGoogle Codelabsを見てくれ」
「特にAndroidビギナー各位はandroid-kotlin-fundamentalsを見てくれ」

以上です。マジで以上です。あとは好き勝手喋ります。学びたい方はさっさとリンク踏んだほうが良いですね。
読みすすめる方はこのあたりからどうぞ。それより前は筆が乗りすぎただけの導入です。

Google Codelabs

そういうわけで、Androidをやっている皆々様におかれましては、
「じゃあGoogle信じたら問題ねえだろ文句あんのか」というアンサーに異論はないものと思います。無いよね?

なのでAndroid Developersを見ます。公式ですからね。大変に妥当。
ですがしかし、我々よわよわ開発者からすると大抵実装例が物足りません。

クラスのリファレンスあたりはメソッド名なんかから意味を察せると問題解決に至ったりしますが、
「組み立て方」のようなものを知りたいとなると白旗です。そうして安易な外部記事を参照してしまいます。

そう、組み立て方。実装例。サンプルプログラムがほしい。欲を言えばチュートリアル形式でほしい。
その上で重要な部分には解説をつけて教えてほしい。自分で手を動かす要素があると殊更嬉しい。この際有料でもいい。2
そんなものがね。

あるんすよ・・・。(Google Codelabs)

AndroidのCodelabs

さて、早速先のリンクにアクセスされた方は、
大量に並んだ「見覚えのあるアイコンとよくわからん英文」を目にしたかと思います。

そのアイコン群の中にありますね。Androidも。ドロイド君が。コイツらを端からやってきゃあいいんですよ。

・・・というのはちょっとマッチョですが、まあ間違いではありません。
今回はさらに優しくオススメするものがあります。

さて、Google Codelabs内のカテゴリは、先ほど見たドロイド君のアイコンに当たる技術区分3だけではありません。
多くは過去のイベント(Android Dev Summitとか)で発表された内容なので、技術区分と並行して、イベント区分4が存在します。
このイベント区分、よくよく見ると、少し異なるものがあります。なんと学習コースとして組み立てられたものが存在します。
しかもいつもだいたい数ヶ月内に更新されています。

今回はソレについて喋ります。
Google Codelabs Android Kotlin Fundamentals Course

Android Kotlin Fundamentals Course

身につくもの

だいたいAndroid Architecture Componentsを学んでいくものと思ってよいかと思います。
https://developer.android.com/jetpack の「アーキテクチャ」の項目に沿って、紹介していきましょう。

先に言ってしまうと絶対にはじめからから順番にやった方がいいので、
それぞれ項目別にCodelabsのリンクを添えてはいるものの、これは最後の最後まで腰が重い人用みたいなモンです。

Data Binding

Codelabs : Android Kotlin Fundamentals 02.4: Data-binding basics
みんな大好きなやつです。レイアウト定義側に「この場所にはこの値を表示してな」と指定するようなやつです。

Android的には、XMLファイルに変数を記述するような形になります。
テキスト類が一番イメージしやすいですが、「変数の値がTrueの時表示、Falseの時非表示」というような使い方も可能です。
クリックリスナーなんかも指定できます。あと双方向bindingもあります。ユーザー入力がデータにすぐ反映されるやつ。

概念的な構成の概要だけ話しておくと、

  1. XMLファイル内で型とともに変数を宣言
  2. その変数を使ってXMLファイル内の表示内容を指定
  3. 処理側ではXMLファイルに対応したBindingオブジェクトから変数の値を代入

という感じです。
これの味をシメると、レイアウトはとりあえず<layout><data></data></layout>5を書くようになります。なりましょう。

Lifecycle

Codelabs : Android Kotlin Fundamentals 04.1: Lifecycles and logging
これはあまり「このライブラリを使って出来ることがあってな」という話ではないやつです。
(クラスとしては、たぶんテストの時に使うくらい・・・?かと思っています)

とはいえ単語としては「Android開発の肝」と呼ばれるブツでありまして、
他のものを使うために必要な知識、という存在ですね。基本の図くらいは知っておきましょう。

LiveData

Codelabs : Android Kotlin Fundamentals 05.2: LiveData and LiveData observers
DataBindingがヤバくなるやつです。あとRoom。6
存在としては「持ってる値の変更を監視してるクラス」です。
変更時に走る処理を登録できて、たとえばそこに画面テキストの更新を入れておいたりしたらハッピーになるわけです。
そしてAndroidにおいては、XMLにLiveDataを直接書いておくと画面更新まで自動でやってくれちゃうとかいうヤバいアハ体験ができます。
上のリンクからさっさと使ってみましょう。

    private val _piyo = MutableLiveData<Boolean>()
    val piyo: LiveData<Status>
        get() = _piyo

自分はこの宣言のしかたをメチャクチャ守って開発をしています。

Navigation

Codelabs : Android Kotlin Fundamentals 03.2: Define navigation paths
アプリ内の画面(Fragment)遷移を管理するやつです。
あとAndroid特有の左側にシュっとなるメニューとか画面上についてるボタンとかをメッチャ簡単に実装できます。

編集がほとんどGUI上で出来るのもヤバいところで、遷移の時に固定で行う処理を定義したりも出来ます。
image.png
AndroidStudio君、こんな顔もできるんですよ。僕はもうビビり散らかしましたね。

Paging

Codelabs : Android Paging
(リンク先ページ名のとおり、実はこれだけFundamentals Courseにはありませんでした)

無限スクロールとかを実装するときに、流暢なデータ読み込みを実現するやつです。
RecyclerViewにPagedListAdapterなるアダプタをアタッチして実装します。

Room

Codelabs : Android Kotlin Fundamentals 06.1: Create a Room database
アプリ内Database(SQLite)をラップするライブラリです。ありえんほど使いやすいです。

自分は普段仕事で素のSQLiteとも戦っていますが、使っている時の安心感から何から段違いですね。
定義するのは

  1. テーブル情報になるEntityデータ
  2. そのテーブルに対するクエリを持つDao(DataAccessObject)
  3. データベースオブジェクト

の3つです。
Entityはデータクラス置いといたらいいし、
Daoは呼びたいクエリごとに関数として窓口を用意する形で超わかりやすいし、
Database本体はDao紐付けたらいいだけです。

ViewModel

Codelabs : Android Kotlin Fundamentals 05.1: ViewModel and ViewModelFactory
画面(Activity,Fragment)が持ってる値を保持してくれるやつです。

要はActivityがライフサイクル的に死んでもこいつは生きててくれるという話なんですが、
これに関しては公式の画像がメチャクチャわかりやすくこのクラスの便利さを表しているので貼っておきます。
image.png
このシンプルさよ。(図中右)

WorkManager

Codelabs : Android Kotlin Fundamentals 09.2: WorkManager
バックグラウンドの処理を管理するやつです。
バックグラウンドなので当然見た目が嬉しくなるものとかではないですが、メッチャ簡単に定期処理とかを実装できます。
識別子を定義しておいて、タスクの重複なんかもしないようにいい感じにやってくれる他、
そもそものリソースの使われ方なんかも「こいつに任せといたらおk」となるので使い得です。

おまけ情報

リポジトリ

コースで使うコードはすべて単一のGithubリポジトリにまとまっています。クソ親切。

英語のおとも

Mouse Dictionary
みんな大好き魔王物語物語のwtetsuさんによる英単語一瞬で引けるマシーン。

Google 翻訳(Chrome拡張)
Codelabs中の英文は簡単に短めになっているので、文章単位でページ遷移無く翻訳できがち。

Read Aloud(自動読み上げ)
正直Codelabsではあまり使えていないんですが、メッチャいいです。

(ひとり開発AdventCalendarは)「ひとりぼっちで開発しているものを発表していくカレンダーです。」

「すみませんリリース間に合いませんでした・・・」

翌日は

@gayou さんの「mecab-koで簡易的なルールベースの韓日機械翻訳を作っている話」です。よろしくおねがいします!


  1. Android Jetpackが加速するものはそれを使う開発者の生産スピードです 

  2. なんと激ヤバいことにこれから紹介するものは無料ですが 

  3. そう呼ぶことにしました 

  4. 上に同じ 

  5. DataBindingライブラリによって、要素から始まるXMLファイルはDataBindingオブジェクトが自動生成されるようになります。 

  6. RoomからLiveData<>返すやつの便利さはマジでヤバいので是非体験して頂きたい。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む