kumamotone’s blog

iOS/Android アプリエンジニアです https://twitter.com/kumamo_tone

potatotips #52 に参加しました (iOS, Androidブログまとめ)

potatotips #52iOSブログまとめ枠で参加させていただきましたのブログです。

会場はFOLIOさんでした。

f:id:kumamotone:20180621193041j:plain

f:id:kumamotone:20180621182347p:plain

最初から FOLIOGoogle の著名なスピーカーの発表が決まっており、申し込みは大人気でした(当日の 18:30 に撮ったキャプチャ。+59 人キャンセル)。

個人的にも気合を入れて臨んだ回だったが、発表陣も異様に気合が入っており、明らかに5分ではない分量のスライドだったり、理解が困難なマニアックでハイレベルなトピックが続出するという状況が、22時ごろまでひたすら続きブログ枠の私は完全に精神が崩壊した

そんなわけで当日にまとめきれず遅くなってしまいましたが、別に溜めたからといってブログのクオリティは上がったりしないんですよね…つらい…

以下発表内容です。参加枠としては iOSブログまとめ枠でしたが Android の発表内容に関してもまとめています。

基本的に発表内容どおりですが、一部、個人的に理解しやすいように勝手に順番を並び替えたり、補足の情報を加えたりしているため、実際の発表内容と違う部分があります。気になるところや間違いなどあればご指摘ください。埋まっていない資料は公開されたのを見つけ次第反映します。

Room のできるまで 〜InvalidationTracker 編〜 / yaraki さん

Room は AndroidSQLite をラップするORマッパー。

@Dao
interface UserDao {
  @Query("SELECT * FROM User")
  fun all(): LiveData<List<User>>>
}

みたいな感じに interface で定義できる。

この User の変更の通知を受け取れるわけだが、 Room は SQLite からどうやって通知を受けているか?

  • 候補1: DAOから通知する
    • 書き込みメソッドの実装はRoomが生成しているので、自分が生成しているコードの中から通知することはできるはず
    • ☓ execSQL で直接クエリを投げたときにも対応できないし、トリガーにも対応できない
  • 候補2: SQLite拡張機能を使う
    • void *sqlite3_update_hook という、何か変更があったときに通知を受け取れる C 言語のAPIがあって、完璧じゃんとなるが、
    • Android ではAPIが開いていない
  • 実際に採用した方法: トリガーをつかう
    • 変更ログのテーブルを作っておき、各テーブルに変更があるたびに変更ログに記入されるようなトリガーを仕掛ける。トランザクション終了時にログを確認して(Roomではすべての書き込みはトランザクションで処理される)、Observerに通知する。

Livedata でテーブルに変更があったときに通知を受け取る場合、 invalidationTracker を使えば良いです

WWDCのセッションから、パスワードのAutofill、エクステンションの紹介 / kishikawakatsumi さん

speakerdeck.com

Web では iOS12 以前から使えた機能だが、パスワードの自動生成というのができるようになった。 勝手に強力なパスワードをSafariが考えてくれて、Keychainに保存しておいてくれる。また、iOS12 から、Safari を使っていれば 2段階認証で SMS で受け取ったコードを自動入力してくれる。

アプリ開発者が対応すべきことはなにか。 AccosiatedDomains というのを設定する。

Textfieldをつくって、Content Type に Username と Password を設定する。すると入力時に鍵のマークが出る。ちゃんとAccosiatedDomainsが設定されていれば、候補が鍵マークの左に出る。鍵マークをタップして、KeyChainから選んで入力することもできる。

Automatic Strong Passwords。iOS12からの機能で、サインアップをするときにOSが自動的にパスワードのサジェストをしてくれる。

Content Type を New Password にすると、これはサインアップ画面で使われるものだという認識をしてくれて、自動でパスワードを考えて強いパスワードを設定してくれる。Keyboard の type を email にすると、emailになるし、デフォルトだとユーザー名っぽい感じになる。

サーバーの方のパスワードのルールと合わない場合は、 Password Rules Validation Tool というのを使うと、 IB Password Rule (セッションではIBで設定できるよと言っていたが、今のところコードからしか設定できない)

困っていることとして、パスワードを保存してくれるタイミングが全然わからない たぶん自動で保存してくれるはずなのだが‥

Two factor auth も同じような感じでできる(関係ないSMSにも反応する)。対応コストは小さいので、対応しておくと離脱を防げて良いのでは。

KotshiからMoshi-codegenに乗り換えた経緯 / duane0728 さん

speakerdeck.com

今年の2月のpotatotips で、moshi-kotlin がリフレクション使っていて遅くて、Kotshiを使ったら早くなったという話を @magie_pooh さんがしていた 。

Moshi-codegen もそうで、リフレクションフリー。Kotshi とどちらを使えば良いのか?

  • 速度
    • 速度の検証、0.2MBのjsonのパースを20書い行った時間の平均msecを測った 大差なし
  • 安定性
    • codegen のほうにバグがあるのに気づいた nonnull な string に null が入ってしまう
    • ただ最新版 Kotshi でも同じバグが起きている
  • 将来性
    • Moshi-codegen は Moshi 本家がオーナー
    • Kotshi の issue で Kotshi の作者が deprecated になるかもと言っている

これからcodegenを導入するなら、kotshiも導入していない場合、最初からmoshi-codegenのほうが良さそう。

How to Manage Edge Gestures / TachibanaKaoru さん

speakerdeck.com

画面をなぞるときの操作には3つある。

  • Pan
  • Flick
    • HIGいわく、速くPanすること(ので実質パン)
  • Swipe
    • HIGいわく、画面をなぞること

Edge Pan Gesture というのはエッジからジェスチャーするやつ。iOS7あたりから UIScreenPanGestureRecognizer で取ることができる。

これですべての Pan Gesture を扱えることを期待したいが、実際には、画面上部や画面下部はシステムジェスチャ(通知センターが出る)が優先されてしまうという問題がある。

f:id:kumamotone:20180715182309p:plain

prefferedScreenEdgesDeferingSystemGestures というiOS11から使えるメソッドをoverride して方向を返す実装を書けば、システムジェスチャを一回ブロックすることができる(ベロみたいなのが出て確認できる感じ)。たとえばデレステ音ゲー)はこれをやっている。

HIG を読むと、やるなとは書かれている。でもゲームとかだとしょうがないよね。

Kyashで使っているTutorial Library / konifar さん

speakerdeck.com

世の中に出ているチュートリアルライブラリ下記の3点が不満で、ライブラリを作った。

不満な点と、作ったライブラリがした対応は以下の通り。

  1. ハイライト部分に角丸のものがなかった
  2. interpolatorやdurationをセットできる
  3. ハイライト部分のアニメーションしか対応していない
  4. interpolatorやdurationをセットできる
  5. テキスト部分のレイアウトをカスタマイズしにくい
  6. setMessageLayoutResId で作ったレイアウトをセットするだけ

よかったら使ってみてください。

github.com

enum as Option / r_plus さん

speakerdeck.com

Swift には Optionset という複数のフラグを管理するためのプロトコルがある。こんな感じで実装して使う。(developper.apple.com/documentation

struct ShippingOptions: OptionSet {
    let rawValue: Int

    static let nextDay    = ShippingOptions(rawValue: 1 << 0)
    static let secondDay  = ShippingOptions(rawValue: 1 << 1)
    static let priority   = ShippingOptions(rawValue: 1 << 2)
    static let standard   = ShippingOptions(rawValue: 1 << 3)

    static let express: ShippingOptions = [.nextDay, .secondDay]
    static let all: ShippingOptions = [.express, .priority, .standard]
}

Int 型の rawValue を数値ではなく、1か0のフラグを表す bit の列として使う。

使う側はlet option: ShippingOptions = [.priority, .standard] みたいな感じで定義して、 if option.contains(.priority) 判定できる。

Foundation でもよく使われている(DispatchQueueのAttributesなど)。

この OptionSet、単なるオプションを判定したいだけにしては記述が面倒ではないかと思うようになってきた。

そのような場合にはOptionSetを使う必要はくて、シンプルにenumを定義して管理すれば良い。複数のオプションの判定をしたい場合は可変長引数にすれば良い。こうすると、 OptionSet を引数にとった場合と 可変長引数の enum を引数にとった場合の実装側の書き方は一致する。

ただ、 OptionSet にあった Union の処理などができないので、そのような複雑な要件の場合には OptionSet を使ったほうが良いかも。

XML Object Mapping / hiroyuki_mori_1217 さん

speakerdeck.com

Swift4 で Codable が追加され、 JSON をモデルに簡単にマッピングできるようになった。

Java はこういうの得意で、 Android には Gson, Jackson, Moshi, SimpleXML... など色々ライブラリある。

これらのライブラリは、空のインスタンスを作って後からセットするというのが基本のロジックであるが、Kotlinでは「イミュータブル」で「非Null」なオブジェクトを扱うため、この方法は相性が悪い。

これを克服しているのが Moshi であるが、 XML には対応していない。仕方ないので、仕事で XML のマッパーを作った。

自分で作る場合には3つのアプローチが考えられる。

Reflection

f:id:kumamotone:20180715185728p:plain

型パラを受け取って内部構造を監査してオブジェクトグラフを作ってオブジェクトグラフの末端からインスタンスを作っていく。

これは楽でいいが、Reflection は遅かったりしてあまり好まれない。

APT

javac にクラス追加するチャンスがある。データクラスがあった場合に目印としてアノテーションをつけておいて自動生成する。重要なのは名前に規則性があるところ。

拡張性高めで Reflection でないのは良い。

Transform API

techblog.yahoo.co.jp

3つめはTransform APIなのだが、既存の処理を修正することができる 難読化しても影響がないが、超めんどくさい。記事を書いたのでそちらを参照してください。

作ろうぜORマッパー

GDPRについて / yoichitgy さん

GDPRとは何か?ヨーロッパに住んでいる市民の個人情報を適切に扱うための決まり。

データ取得と用途について明示的な同意を取得、情報漏えいや不正アクセスが会った場合は3日以内に通知するとか…

個人情報には、氏名住所は当然として、位置情報やクッキー、病歴など…色々ある。

違反すると全世界での売上の4%、最大25億円を支払わなければならない。EEAのユーザーが対象で、今年5月25日以降からすでに有効 こわい

Crashlytics レポートのユーザ情報、これ危険。レポートで個人情報 が見れてしまう。オプションで見れないようにできますが、注意が必要。

今年のWWDC2018から、ユーザーの行動記録を取る場合は視覚的に明示して同意を取らなければならないというのが書いてある。(2.5.14) 注意が必要。

Tips for Bitrise Android / shaunkawano さん

もともと Circle CI を使っていたのだが、問題としてメモリが足りなくて落ちる、独自のスクリプトを書かないといけないという問題などがあった 今は bitrise を使っている

メモリ不足でなやむことはなくなった、独自のシェルスクリプトに関しては fastlane で解決

tagが切られたときになにかしたい、bitrise.ioのときにはymlでやってたのだが、問題があった。

たとえばタグが切られたタイミングでGithubのドラフトリリースを作って、file-downloaderという機能を使って、KeystoreとGradleのファイルを落としてきてさもローカルで署名してるみたいにできる

fastlaneのほうにはset_github_releaseというレーンがあるのでそれでできる。今後は gradle ファイルのキャッシュなどをやりたい

bitrise Fastlane, file-downloader, おすすめ

Turi Createを試してみる / tattn さん

speakerdeck.com

Turi Create は Apple が買収した Turi という会社

CoreMLのモデルを出力できる 実装済みのアルゴリズムや調整済みパラメータを使って特定の問題を解くという思想で、Create ML の思想に近い

画風変換の学習に必要なデータは、Contant Images と Style Images

github.com

High Sierra でも動く。モデルを続きから学習される機能が現在ないので作って欲しい… 現在対応する気はなさそう

Colaboratoryでも使える (apt install で必要なものを入れられたり、GPUが無料で使えたりして良い)

Android P - Restrictions on non-SDK interfaces / operandoOS さん

speakerdeck.com

非 SDK インターフェースの制限  |  Android Developers

Android P から non-SDK へのアクセスに制限ができるようになった。

リフレクションとか使ってアクセスするようなフィールドやメソッドは、使うと落ちる場合がある。直接リフレクション、またはJNI経由でnon-SDKを使用する場合に適用される。

しかしすべてのnon-sdkがそうなるのか?と言われればそうではない。light-graylist、dark-greylist、blacklistという風に区分けされている。

light-graylist はまだ普通にアクセスできる。dark-greylist は targetSDK によってはアクセスできない。blacklist は targetSdkVersion に関係なくアクセスできない。non-SDK はそもそも使わないほうがいいが、今後はもっと気をつけましょう

見落としがちなVoiceOver対応 / jkatayama さん

speakerdeck.com

カスタマーサービスへ、「アプリ内のほとんどがVoiceOver対応していてありがとう、でも一部使いものにならないよ」という問い合わせがきた

なるほど、しかし VoiceOver なんて実装した覚えはない‥

調べてみると、 VoiceOver でほぼ対応されていて、UILabel,UIButton, TableView の Cell の内容は読み上げてくれるようだった。

しかし、UIButton で画像だけ、のような場合にはリソース名がそのまま読み上げられてしまう。

設定は簡単で、Interface Builder から、 Accessibility > Label にテキストを指定すると良い。

VoiceOver で読み上げられる内容を逐一確認するのは大変だが、 Accessibility Inspector を使うと、カーソル当てると Quick Look が見れて、読み上げられるテキストが確認できて便利である。

What’s new in Google Play Billing / ymnder さん

speakerdeck.com

Google Play Storeのアプリと、Billing Library に変更があったので変更点の紹介。

Play Storeアプリでは、定期購入コンテンツのリコメンドや、何の定期購読コンテンツを購読するか管理する画面が使いやすくなりました。

また、Deep Link によって、我々が作ったアプリから Play Store アプリの管理画面に直接遷移させることができたり、ユーザーが定期購読コンテンツを解約するときに理由を聞いたりできるようになりました。このアンケートの分析結果は Play Console で見ることができます。

Billing Library では、以下のような変更があった。

  • 定期購読のプランを月の途中で変更することができるようになった
    • proration (編注: 比例配分) modeが指定できるようになった
  • launchBillingFlow()を行ったときに、失敗時にもレスポンスを受け取れるようになった

proration mode には以下の 3 つのモードがある。

  • IMMEDIATE_WITHOUT_PRORATION
    • 追加請求なしで移行する
    • 2ドルのプランを月初めに購読開始して、月途中で3ドルにしたとしても、その月は2ドルのままの太っ腹モード。
  • IMMEDIATE_AND_CHARGE_PRORATED_PRICE
    • 日割りで追加請求を行う
    • 2ドルのプランを月初めに購読開始して、半月後3ドルにしたら、2ドルの半分の1ドルと、3ドルの半分の1.5ドルで、その月の請求は2.5ドルになる。
  • IMMEDIATE_WITH_TIME_PRORATION
    • 既に支払った分をクレジットとみなし、日割り計算で使い切ったら新しい課金サイクルで課金が開始される
    • 2ドルのプランを月初めに購読開始して、月途中で3ドルにしたら、1ヶ月未満で次のプランに切り替わる その結果、月途中から新しいプランの課金サイクルが始まったりする

少々ややこしいが、スライドに図解がある。

Swiftで高カインド多相 / inamiy さん

speakerdeck.com

型コンストラクタは「型」を引数に取り、「型」を返す。 カインドは「型の型」。 高カインドは「カインド」を引数に取り、「カインド」を返す。

Swift 4.2 現在、高カインド関数には対応していない。Kotlin では ARROW というライブラリで Functor, Applicative, Monad などの基本的な typeclassをサポートしてる。

github.com

Swiftで高カインド関数を実現するにはどうすればよいのか?

qiita.com

Qiitaに記事があるが、どれも Lightweight Higher-Kinded Polymorphism という論文が元になっている。この論文を元にライブラリ作った。

github.com

これによって Array から List への自然な変換ができたりする。

let list = [1, 2, 3].kind
               .naturalTransform {
                    List($0.value).kind
                }
               .value

list == List.cons(1, .cons(2, .cons(3, .nil)))

Pay attention to the HUGE logs / Ryo Sakaguchi さん

端末内に存在するデータをサーバーに送信する機能を開発していて、データの規模を聞くと、1000件ぐらいだった。長めのbackground jobになるなという感じ。

この機能を実装するために、I/Oで発表された WorkManager を使おうと思った。Worker を継承したクラスを実装して、インスタンスにenqueue するだけ。あとは適切なサイズにデータを加工すればOK。

しかし動かしてみると、 byte[]-> String でOOMで死んでた。forkして出力するログサイズを制限できるパッチを当てたものを使用することで回避した。StethoもRequestBodyが巨大で死んでたりしたがまだ対策できていない。

これが参考になった

in.fablic.co.jp

めっちゃでかいログには気をつけましょう。

Contributing to Swift in WWDC / kitasuke さん

WWDC、Technology Labs というのがある。OSSをあまりやってこなかったAppleだが、ここではエンジニアと話できる。

いろんなLabあるので色々質問できる Swift Open Hours に行った。2日目以降は暇そうだったので色々聞けて良かった。

ラボでは、事前に用意してきた Bug report と、それに対してこういうプルリクを出そうと思うのだが、みたいなことを話して合意を取ったりした。これがめちゃくちゃ良かった。

Swift Open Source Workshop by try! Swift San Jose でワークショップあったのでプルリク出せた。

github.com

Hey Guys, I am... / mhidaka さん

エンジニアリングマネージャーをやるという話。

マネージャー、最初はあんまり興味がなかった。エンジニアリングに集中したいという気持ちがあった

しかし組織の急拡大により、ミドルマネージャーが足りない問題があった。Androidエンジニアのマネージャーがいない。適切なアサインが難しい。技術的な評価が難しい。施策の選択と集中が難しい。

ということでエンジニアリングマネージャーをやることになった。エンジニアリングマネージャとは技術に責任を持つ職能。お試しでというエクスキューズがあるのでやっていけている。人間なのでコミュニケーション、働きやすさが大事。どうすればモチベーションが上がるのかとか教えてほしい。

現在のところは、Slack, スプレッドシートをやっている。俺に任せていけという感じ。

Hey Guys, I am an engineering manager.(タイトル)

Video decode on iOS. / noppe さん

speakerdeck.com

透過動画再生ビューライブラリを作っている。

透過動画とは何かというと、こういうやつ。(引用元: Kitsunebi/animation.gif at master · noppefoxwolf/Kitsunebi · GitHub )

f:id:kumamotone:20180623202531g:plain

2つ動画ファイル用意してあげて、マスク合成して描画している。

今日はビデオファイルのデコードについて話す。ビデオファイル、それはコンテナの集まりであるが、デコードとはメディアコンテナを再生できる状態にすること。

コーデックの違いによって圧縮率などが変わる。AVAssetReader, VTDecompression ffmpeg などの方法がある。

これらのフレームワークでは、内部でOS/端末のデコーダの実装を利用しているのだと思う。OSや端末によってサポートが異なるので注意。たとえばHEVCは一部のチップでしかハードウェアデコードをすることができない。

対応コーデックの確認をすることが今のところできないように見える。AVSessetReader を使うと対応コーデックであればコーデックを意識せずにできるので簡単。

VTDecompressionSessionはもっと低レイヤーなやつで、やや複雑だが柔軟性は高い。特徴としては以下のようになる。

  • ストリーミングでもキャプチャが取れる
  • AVAssetReaderにバグが有っても大丈夫
  • 分散してエンコードしたいときとかに便利
  • エラーチェックとかちゃんとしないといけない

VTDecompressionSessionの流れとしては

  • h264/avcを分割
    • nal file format
  • 映像が含まれるチャンクを取り出す
    • nal unit type は ISO/IEC 14496 に定義
  • VTDecompressionSession を生成
  • チャンクをSession にわたす
  • デコード済みのImageBufferがコールバックに返される

サンプルはgithubにあります

感想とか

ハイレベルなトピックが多く本当にエキサイティングだった。言わずもがな、勉強になったし素晴らしい会でした。

懇親会ではアプリ界の名立たる著名人とコンタクトを取れる貴重な機会であったが、

ブログのために情報の濁流を整理するのに疲弊していた私は、知人とばかり話し、挙げ句シラフであるにも関わらず

「もう東京で消耗しすぎたから1ヶ月ぐらい何もかも忘れて海外に行きたい」

「どうしたら楽してお金が手に入りますかね?」

といった生産性のない話題ばかり降っていた。

これらの話題に、懇親会で嫌な顔せずに応えてくださった優しい方々にこの場を借りて感謝いたします。