BASE開発チームブログ

フリーミアムなネットショップ構築サービス BASE( https://thebase.in )の開発チームによるブログです。開発メンバ積極募集中! https://www.wantedly.com/companies/base/projects

WWDC 2017に参加しました

初めまして、BASE iOS エンジニアの大木です。 6/5-6/9の日程で行われたWWDCに参加してきました。 初参加だったのですが、いつもの業務から離れてiOSの技術的な内容を聞いたり同じiOSエンジニアと議論したり大変有意義な時間を過ごせました。 発表内容は他の方やメディアがまとめていると思いますので、ここではそこにはあまり触れず雰囲気をお伝えできればと思います。

会場について

今回はSan Franciscoではなく、San JaseのMcEnery Convention Centerで開催されました。 前日から会場にチェックインできるので、夕方に現地に着き宿泊先に荷物を置いてから会場に向かいました。 もっと前から現地に行き観光に行った方もいたようで、若干羨ましかったです。

会場の外でチェックインして、バッチをもらいました。

前日に備え腹ごしらえをするために歩き回るGuys

宿泊先

Appleからメールで送られてきた予約フォームにあった会場に近いThe Fairmontというホテルに宿泊しました。 会場へはゆっくり歩いても、徒歩5-10分以内につくので大変助かりました。

www.fairmont.jp

ホテル下にMUJIも!

Tech Museumも近い!

Singleで予約してもベットは2つ!

初日

前日の腹ごしえの時に徹夜しなくても入れるという情報をもらったので、宿泊先から会場へ歩いていけることもあり、明るくなってから列に並びました。 一応朝4時ごろに目が覚めたのですが、列に並び始めたのは6時でした。

建物は朝7時くらいに開場され、スタッフに拍手で迎えられました。

入場すると朝食が振舞われていたので食べつつ、ホールが開くのを待ちました。

真ん中の一番後ろの席を確保

Keynote、Platforms State of the Unionを観た感じ、VRKit/ARKit/Core ML、App StoreのリニューアルやXcode/Foundation/Swiftの改善などアプリを作るための基礎となるものの発表が多かった気がします。

また、Keynote後のランチ中に、WWDCアプリを確認するとセッション内容が更新されていたので、Keynoteの内容についての感想やセッションどこに行くかなどについて話したりしました。

ランチボックス

セッション

自分は初日の発表を聞いて下記のことが気になったのでそれらのセッションを中心に回りました。

  • JSONマッピングのためのDecodableプロトコルって、ダーティーなJSONでも対応できるの?
  • ARKitでマーカーっていらないの? 認識性能は?
  • Core MLって端末上で動くらしいが、機械学習の学習済みモデルってどうインポートするのか? また、画像認識や自然言語処理の特定機能の処理にそれがどう関わってくるのか?
  • UIKit周りのアップデート

動画とスライド、あとサンプルコードもいくつか公開されているので、気になる方はチェックしてみると良いかと思います。

Lab

セッションとは別に、Apple社員に質問ができるLabというのもありました。 デザイン系やAppStore系のLabは予約が必要なので早起きする必要があります。 予約フォームは毎朝7時以降に専用のページにアクセスするのですが、私は残念ながら予約できませんでした。

3つほどエンジニアリング系Lab(それしか回ってない)を回ったのですが、UIKit and Collection View Labは、結構並びました。

いくつかした質問紹介すると、

Q. CollectionViewを入れ子で使うことが多いのだけど、これってパフォーマンス的に問題ないの?
A. あまりよくないね。CollectionViewは一つにするべきだよ。
このViewはレイアウトを色々カスタマイズできるので、
必要な種類のCellを用意してセクションを分けたりしながらレイアウトするべきだよ

Q. UITableViewライクなレイアウトを作成することがよくあるのだけど、
パフォーマンスの観点だとセパレータってDecorationViewで作るべきなの?
CollectionViewCellの要素として作るべきなの?
A. 今ってどっちで作ってるの? 
(CollectionViewCellの要素として作ったコードを見せながら) 全然これで問題ないよ!
Cell初期化の時に作ってるみたいだしね

あと、ApplePayボタンに関して、利用ケース別にどのボタンタイプを使うべきか質問をしにいった時に、 自分が実装したAutolayoutのコードを見て、AppleのエンジニアがHuman Interface Guidelinesを確認していました。(幅と高さが最低いくつ必要かというのを確認してたっぽい)

The Bash

最終日前日には、酒が飲めたりライブ見たり遊具ありのパーティーがありました。 会場近くの屋外広場で開催されてました。

開場直後のステージ

Fall Out Boyが演奏してました。チケット手に入れる手間もなく観られてよかったです!

Fall Out Boy on Apple Music

www.setlist.fm

トラブル

荷物検査の後、iPhoneを失くしかける

荷物検査の後、ゲートに移動したらiPhoneがないことに気づきました。 荷物検査のところで失くした場合、係員に伝えると探してくれます。 iPhoneの場合、指紋認証でアンロックして写真アプリを開いて、自分の写真を見せるように言われるので、セルフィーなど一枚残しておくと良いです。

その時、別のiPhone持ちながらiPhone失くしたって伝えたので、「持ってるじゃない」言われたりしました。

ホテルの差し込み型のカードキーでドアが開かなくなる

Fairmontホテルは、差し込み型のカードキーなんですが、滞在2日目の夜くらいから開けにくくなったのでフロントにいって交換してもらいました。 その時、ID(旅行客の場合パスポート)を見せるように言われたため、パスポートは肌身離さず持っておいた方が良いと思いました。

ホテルのWi-Fi/ポケットWi-Fiが繋がりにくくなる

WWDCの会場にいる場合は良いのですが、ネットに繋がらないと色々辛くなります。なのでプリペイドSIMを出発前か現地到着後に購入しておくと良いと思いました。 現地調達する場合、T-MobileストアとかSIMを購入できる場所がどこかにあるはずなので見つけて購入すると良いでしょう。

下のような感じで色々と辛くなります。

  • Labの予約ができなくなる
  • 宿泊先が遠いのに、Uber/Lyftが呼べない
  • はぐれる

まとめ

滞在中は、サンノゼ空港から宿泊先のホテルまで、$26と吹っかけられたり、(スルーしてLyft使いました$15.5)

下記のイベントに参加したり

www.meetup.com

スポーツバーで、偶然NBAの試合見たりしました。 Warriorsが逆転勝利して盛り上がってました! NBA | Games: Warriors at Cavaliers

色々ありましたが、WWDCに参加できて本当によかったと思います。 まだいったことがない人がいれば是非参加することをオススメします。

2017年にプロが今更PHPを学ぶための本、Webまとめ

BASE CTOの藤川です。

リブセンスさんが運営されている転職ドラフトという転職サイトで、全員のプロフィールを読んでいて薄々気がついていたことに改めて気がつかされたのですが、BASEの方でサーバサイドに使っているメインの技術はCakePHPというフレームワークでありPHPの技術なのですが、

新卒の就職先がRubyを使っていて、今、25〜27歳ぐらいになっている若手エンジニアにPHPの経験がない人が増えている!

という大きな問題にぶちあたりました。我々は転職いただく方の前職については、いくつか期待している流れがあります。決済、EC視点ではEC-cubeなどでPHPを扱っていた会社からの転職組というのが重要な人材供給源だったりするのですが、それ以外に「モバイル、スマホアプリ、最強のUX」というスタートアップ的な視点においては、モダンなスタートアップのサービスに携わっている経験は魅力的です。(別にそれだけで採用したりはしませんが)

つまり第二新卒などの若手エンジニアを採用しようとすると、仕事道具としてPHPを今更学んでいただくところから始めなくてはいけません。

最近、Railsで作られてるマストドンを個人で運営している関係で、がっつりソースコードを見ていますが、ビジネス要求に照らし合わせると、ぶっちゃけRailsもCakePHPもできることはあんまり変わらないわけです。

もちろんRailsは高い生産性を発揮する素晴らしいプロダクトで、作っていて楽しいWAFだとは思いますが、スケールしたシステムにおいて抱えている問題解決の会話において、言語やWAFの重要性は、割とどうでもよくなってくる。

つまり、起きる問題が言語の問題ではなく、アーキテクチャに起因することの方が多いので、日々の問題解決において話している内容は言語の話ではありません。

マストドンがRailsなのも、改めて考えてみれば、丁度10年前のツイッターと同じなわけで、このプロダクトは、N+1問題を抱えたタイムライン型プロダクトとして超勉強になって、あぁこうやってタイムラインは遅延してたのねということが実感できます。

ですが、Twitterのその後が脱Railsをしたことも加えると、成長するサービスにおいてフルスタックなフレームワークが寄与する範囲は多くはないかなと思うわけです。

(それは僕らの問題としてCakePHPにも全く同じことが言えるわけです。つまり僕らは僕らでCakeに依存してはいけないわけですね。やっぱり生産性の高いWAFはどこかで効率性を引き換えに利便性にロックインされてしまうので地味に脱するのは難しいんですよ。)

またクラウドの発達でAWSやGCPなどを素早く扱えることの方が重要性を増してきて、Web開発言語はどこかグルー(糊)としての役割のほうが大きくなってきました。

ということで、我々としては20代の若者に対しては、言語を学ぶことが重要なんじゃなくて、Webをとりまくアーキテクチャをしっかり知っていくことが大切なんだ!という言葉と、メルカリさんだってPHPでしょ?SlackもFacebookもPHPでしょ!?成功するサービスにPHPは少なくないんだよ!というポジショントークを交えながら、「正しいWebエンジニアのあり方」を説くわけです。

そして、その先にある、「じゃあ、2017年の段階で、PHPを学ぶにはどうしたらいいですか?」という質問に答えるための記事がこの記事です。

とりあえず、なにやら最新情報はないものかとTwitterとマストドンに投げてみました。

「うずらさんの資料」とはこれです。

■半端なPHPDisでPHPerに陰で笑われないためのPerl Monger向け最新PHP事情(5.6対応)(YAPC公式サイトの紹介)

半端なPHPDisでPHPerに陰で笑われないためのPerl Monger向け最新PHP事情(5.6対応) - YAPC::Asia Tokyo 2014

2014年のYAPCで、PHPネタでベストトーク賞を取るという伝説のプレゼンテーションですね。

その他、教えてもらったURLを貼っておきますね。

まだマストドンのtootの著作権がはっきりしてないので基本URLだけにします。creative commonsなどで再利用権を主張できるようになるといいですね!

■PHP The Right Way

ja.phptherightway.com

■短気なプログラマのためのPHPUnitクックブック https://leanpub.com/grumpy-phpunit-jp

■パーフェクトPHP(書籍)

gihyo.jp

■効率的なWebアプリケーションの作り方 ~PHPによるモダン開発入門(書籍)

https://www.amazon.co.jp/%E5%8A%B9%E7%8E%87%E7%9A%84%E3%81%AAWeb%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E4%BD%9C%E3%82%8A%E6%96%B9-~PHP%E3%81%AB%E3%82%88%E3%82%8B%E3%83%A2%E3%83%80%E3%83%B3%E9%96%8B%E7%99%BA%E5%85%A5%E9%96%80-%E5%B0%8F%E5%B7%9D-%E9%9B%84%E5%A4%A7/dp/4774150827

パーフェクトPHPを書かれた小川さんの本

■PHP公式マニュアル。

https://secure.php.net/manual/ja/index.php

ふつうに良い。おおむねこれで足りる。

■PHP公式マニュアル

https://secure.php.net/manual/ja/appendices.php

うずらさん談:「php.netは、付録という名前の本編を全部読んでいただきたい。」

■PHP入門(とほほのWeb)

http://www.tohoho-web.com/php/

■PHP The Wrong Way

http://www.phpthewrongway.com/

うずらさん談:「ひねくれた(?)人なら、the wrong wayもよむべき(?)」

道具としては若干枯れた印象も強いPHPですが、歴史も長い言語なので、それ故にモダンな開発言語で意識されるような「正しく扱うスキル」が求められます。

他にもオススメの本やWeb記事がありましたら、是非はてブで教えてください!

追記:この記事を読んでいただいた方から、おまえPHP7のやる気ないだろ!って感想をいただいたので、全然そんなことないですよ!ってことを書いておきます!

今回の話は、若い人がPHPをネガティブに捉えたまま近寄らない事態になるのを防ぐために、あえてそっち側に寄せて書いてあります。それ以上に今回、記事を書いていて思ったのは、書籍などの大きな情報発信について、2014年ぐらいを境にPHPに関する情報発信が途切れているイメージを持ったこと。

それでは「新しい人」が入ってこないのも当たり前なので、盛り上げていかないと!自分たちもPHP7.xのムーブメントは大切なことなので、情報発信も含めた技術的チャレンジをちゃんとやっていき、あわよくばPHPを使っているエンジニア業界を牽引できるようにしていきたいと思っています!そういう部分も含めて、一緒に将来を作っていっていただける方を募集しております!

www.wantedly.com

RecyclerViewでGridLayoutの余白をうまく調整するItemDecoration

こんにちは。BASEでAndroidアプリ開発をしている鈴木です。
https://twitter.com/G_devi

突然ですが、デザイナーからこんな感じにしてくれと頼まれたことはありませんか?

f:id:gendevi01:20170329172157p:plain

GridLayoutですね

一番上のヘッダー部分が全幅なうえ、Grid部分の余白を全部同じサイズに調整するのが地味に手間がかかる。

どうせならヘッダーも同じサイズの余白をつけるか、Gridの左右は半分でもいいじゃん・・・と思うのは実装者だけですね。はい。

というわけで、楽に対応できるItemDecorationを作りました!


どうやってるのか

簡単に説明すると

  • RecyclerVIewの左右に設定したい余白の半分のpaddingをつける。
    • もう半分は子Viewで
  • RecyclerViewのclipToPaddingをfalseにする。
    • 子Viewにpadding領域はみ出してもいいよ設定
  • 全幅のViewの左右Marginに設定したい余白の半分の負数を設定する。
    • お言葉に甘えてはみ出すよ
  • GridItemの左右に設定したい余白の半分のpadding(DecorationでのoutRect)をつける。
    • GridItem間はお互いの余白を足して設定したい余白になる

中身

  • ItemDecoration
public GridDecoration(int sideMargin) {
    this.halfMargin = sideMargin / 2;
    this.halfMinusMargin = -1 * halfMargin;
}

@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {

    int adapterPosition = parent.getChildAdapterPosition(view);
    GridLayoutManager lm = (GridLayoutManager) parent.getLayoutManager();
    GridLayoutManager.SpanSizeLookup ssl = lm.getSpanSizeLookup();
    int spanCount = lm.getSpanCount();

    if (ssl.getSpanSize(adapterPosition) >= spanCount) {
        // 全幅(ヘッダーとか)
        ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
        params.setMargins(this.halfMinusMargin, params.topMargin, this.halfMinusMargin, sideMargin);
        view.setLayoutParams(params);
        return;
    }

    outRect.left = this.halfMargin;
    outRect.right = this.halfMargin;
}
  • RecyclerViewへの設定
recyclerView.setClipToPadding(false);
recyclerView.setPadding(
    halfMargin
    , parent.getPaddingTop()
    , halfMargin
    , parent.getPaddingBottom()
);
recyclerView.addItemDecoration(new GridDecoration(sideMargin));

こんな感じです

githubに置いたので他の部分も見たい人はぜひ
GridDecorationTest/GridDecoration.java at master · g-devi/GridDecorationTest · GitHub


失敗例

// 上で全幅は除外してる想定

// 左端のView
if (isLeft(ssl, spanCount, adapterPosition)) {
    outRect.left = sideMargin;
    outRect.right = sideMargin / 2;
    return;
}

// 右端のView
if (isRight(ssl, spanCount, adapterPosition)) {
    outRect.left = sideMargin / 2;
    outRect.right = sideMargin;
    return;
}

outRect.left = sideMargin / 2;
outRect.right = sideMargin / 2;

パッと見、うまくいってると思ったが・・・
よくこのGridのViewに横幅に合わせた正方形の画像を置いて、下にテキストを表示するようなレイアウトがありますよね?
それを当てはめてみると・・・

f:id:gendevi01:20170329173532p:plain

2行目の4つのViewを見てください。
オレンジの部分が横幅に合わせた正方形のViewなのですが、左右端と間の2つでサイズがズレてますよね?

これはoutRectを設定したことで左右端のViewの描画幅が狭まっているためです。

よく利用させていただいているこのアプリを使うと分かりやすいです。
play.google.com

spanCountとspanSizeを設定してる時点で幅は決まっており、その範囲内でどれだけ余白をもたせるかのoutRectですね。

ってことで、Gridの全ViewのoutRectのleft, right合計が一緒でないといけないためoutRectだけでの設定は物理的に無理でした・・・
やっぱりRecyclerView自体にpadding設定しなきゃってことで上の対応になったわけです。

補足

  • 上のコードだけだと、いわゆるdividerを入れていないため上下のViewがくっつくのでその辺はお好きに。
    • サンプルだととりあえず "outRect.bottom = sideMargin" を入れて表示してます。
  • とりあえずItemDecoratoinだけで設定してみたかったので、githubのコードではItemDecoration内でRecyclerViewのpaddingを設定したりしてますが、実際に使うときはRecyclerViewをカスタムするか、もう一個上でDecorationのセットも含めて実行してくれるものを作ったほうがいいかと思います。
  • レイアウトxml側でRecyclerViewのclipToPaddingやpaddingLeftなどを設定したり、全幅のものにマイナスマージンつけたりしてもいいのですが、作る度にそれぞれ設定すると手間なのでItemDecoration上でできるようにしました。
    • 手間でなければお好きなほうで!

最後に

  • そのうち、GridのViewがCardViewのパターンについても書きます。
    • 旧バージョンを考慮するとCardViewの周りにshadowのための余白がつくので調整が必要になりますからね・・・
    • zeplinとかでの指定ではshadowを無視したCardViewの端から画面端までの余白とかになりますからね・・・
  • BASEではAndroidエンジニアも募集しているので、気になる方はぜひ!!

www.wantedly.com

独自ドメインのショップでhttpsでアクセスできるようになりました

SREチームの小林(し)です。

BASEでは独自ドメインで運用されているショップさんでHTTPSで表示できる機能を実装しました。

去年の3月にサブドメインで運用されているショップさんに関しては全てHTTPS化は実装していましたが、独自ドメインで表示されているショップさんはHTTPの表示のままでした。今回から独自ドメインを利用されているショップさんもHTTPSでアクセスが可能となり、全てのショップさんでHTTPSでのアクセスが可能となります。

今回の機能ではHTTPSアクセスに必要な証明書の取得は無料で行い、かつ管理は僕たちがやりますのでショップさんの方で証明書の取得・管理は不要です。

利用方法などはマニュアルにお任せし、今回は裏側の実装について紹介します

証明書

今回証明書を発行するにあたり、Let’s Encryptを利用させていただきました。Let’s Encryptは去年サービスが開始された無料で証明書が取得できるサービスです。

ただ同時に何枚も無制限に取得できるわけではなく、いくつかの制限が存在しています。逆に制限さえきちんと守れば同じアカウントからであれば証明書を複数枚取得することは許可されているようです。(運営に問い合わせ済み)

この Let’s Encryptから発行された証明書を使うことで、BASEの独自ドメインのショップをhttpsでアクセスすることを可能にしています。

外部サービスとの連携

取得はAmazon SQSと連携した取得専用のデーモンが自動で行なっています。 Let’s Encryptは初回はACMEアカウントと呼ばれる専用のアカウントが必要です。このアカウントが証明書を取得する際に認証として使われます。

f:id:srockstyle:20170321184658p:plain

PHPcon2016で発表した資料でもお話しましたが、BASEのサービスはロードバランサーに複数台のnginxを使っています。今回はnginxの設定を一部変更し、Let’s Encryptからのacme challengeと呼ばれる証明書取得確認のアクセスがきたら、取得専用のサーバにプロキシするようにしました。

取得専用サーバではacme challengeアクセスの受け皿としてドメインごとにディレクトリを作成しレスポンスを返しています。直下で認証が行われると証明書を取得するための一連の動作が専用のデーモンによって行われる仕組みです。

nginxへの同期と証明書の読み込み

f:id:srockstyle:20170321185659p:plain

Let’s Encryptの証明書は取得後「/etc/letsencrypt」ディレクトリに保存されます。これらをバランサーとして動作しているサーバに定期的に同期を走らせています。

nginxでの証明書読み込みはngx_mrubyをnginxにモジュールとして組み込むことで動的に行なっています。これにより同期された証明書がアクセスのたびに動的に読み込まれ、nginxを停止することなく、またnginxのメモリを圧迫することなく独自ドメインでのHTTPSアクセスができるようになりました。

まとめ

HTTPSはSEOでの効果もあるということなので、今回実装した機能はショップ運営の大きな助けになると思います。 独自ドメインを使っている方はぜひ利用してみてください!

BASEでは一緒にネットショップを開発・改善するエンジニアを募集してます。ご興味のある方はぜひ遊びにきてください。

BASE株式会社:採用情報

Try! Swift 2017 - 2日目「Swiftで堅牢なカラーシステムを構築する」

こんにちは、iOSエンジニアの遠藤(秀)です。 3/2(木)〜 3/4(土)の3日間に渡って開催された世界的なイベント「Try! Swift 2017」に参加してきました。

2日目のセッション「Swiftで堅牢なカラーシステムを構築する」について、まとめてみました。

セッション概要

これまで以上に多くの企業が、新しく増え続けるユーザーに今までよりも魅力的なアプリだとアピールするために、アプリを再設計しています。この講演ではあらゆる規模のプロジェクトにスケールできる堅牢なカラーシステムを構築するための戦略について議論します。これらのアプローチはデザイン上の決定を迅速に繰り返すのに役立ち、実行時にカラーパレットのテーマを変更するようなこともできるかもしれません。さらに、iOS 10で導入された新しいカラーフィルターのアクセシビリティ機能を使用して、色覚の問題を抱える人を支援することにも応用できることを示すデモンストレーションも行います。



キーワード 、まとめ

・プロトコル、エクステンションで堅牢なカラーシステムにする。
・ランタイムでカラーテーマを変更する。
・色覚障がいを含む、全てのユーザについて考慮すべき。



BASEアプリでのカラーマネージメント

BASEアプリでは、なるべくコード量を減らすためにclrファイルを作成して、xibファイル側に色情報を持たせるように移行している途中でした。

f:id:base_hideo:20170307150102p:plain

clrファイルの作成方法


セッション中の説明にもあったとおり、clrファルでカラーパレットを共有したとしても、カラーパレットに変更が起こったときには、全てのxibファイルを書き直す作業が必要になります。また、プログラマティックにカラーテーブルをダイナミックに変更することができません。

これらの問題を解決するので手法として、今回のセッションはとても参考になる内容でした。


エクステンションの作成

UIColorのエクステンションを作成してstructを定義します。

extension UIColor {
    struct Palette {
        static let ceruleanBlue = UIColor(red:    0.0 / 255.0, green: 158.0 / 255.0, blue: 220.0 / 255.0, alpha: 1.0)
        static let cannonPurple = UIColor(red:  147.0 / 255.0, green:  78.0 / 255.0, blue: 132.0 / 255.0, alpha: 1.0)
        static let mulberryRed = UIColor(red:  197.0 / 255.0, green:  81.0 / 255.0, blue:  82.0 / 255.0, alpha: 1.0)
        static let fireBushOrange = UIColor(red:  225.0 / 255.0, green: 148.0 / 255.0, blue:  51.0 / 255.0, alpha: 1.0)
        static let saffronYellow  = UIColor(red:  242.0 / 255.0, green: 190.0 / 255.0, blue:  46.0 / 255.0, alpha: 1.0)
        static let sushiGreen = UIColor(red:  118.0 / 255.0, green: 184.0 / 255.0, blue:  59.0 / 255.0, alpha: 1.0)
        static let black = UIColor(white: 44.0 / 255.0, alpha: 1.0)
        static let white = UIColor.white
    }
}


・クラス変数 / クラスメソッドにて色を取得します。

class var primaryText: UIColor {
    return Palette.black
}

class func contentBackground() -> UIColor {
    return Palette.white
}


・デザイナー・エンジニア間でZeplinを使用している場合、カラーテーブルをエクステンションとして出力することが出来て便利です。

f:id:base_hideo:20170307150119p:plain


カラーテーマの適用

・アプリの外観を変更(LINEの着せ替えとか。)するような場合、ノティフィケーションを使用してカラーテーマを変更します。

最初にColorUpdatableプロトコルを定義します。

/// A protocol which denotes types which can update their colors.
protocol ColorUpdatable {
    /// The theme for which to update colors.
    var theme: Theme { get set }
    
    /// A function that is called when colors should be updated.
    func updateColors(for theme: Theme)
}


ノティフィケーションの名前を定義するためのプロトコル、ColorChangeObservingを定義します。

/// A protocol for responding to `didChangeColorTheme` custom notifications.
protocol ColorThemeObserving {
    /// Registers observance of `didChangeColorTheme` custom notifications.
    func addDidChangeColorThemeObserver(notificationCenter:
                                                   NotificationCenter)
     /// Removes observance of `didChangeColorTheme` custom notifications.
    func removeDidChangeColorThemeObserver(notificationCenter:
                                                   NotificationCenter)
    /// Responds to `didChangeColorTheme` custom notifications.
    func didChangeColorTheme(notification: Notification)
 }


ヘルパー用の関数を作ります。

private extension ColorThemeObserving {
     /// Returns the theme specified by the `didChangeColorTheme` notification’s `userInfo`.
     func theme(from notification: Notification) -> Theme? {
         // . . .
         return theme
     }

     /// Updates the colors of `ColorUpdatable`-conforming objects.
     func updateColors(from notification: Notification) {
         guard let theme = theme(from: notification) else { return }
         if var colorUpdatableObject = self as? ColorUpdatable, theme != colorUpdatableObject.theme {
             colorUpdatableObject.theme = theme
             colorUpdatableObject.updateColors(for: theme)
        }
    }
}


カラーテーマが変更されたノティフィケーションを受信します。

extension UIViewController: ColorThemeObserving {
    @objc func didChangeColorTheme(_ notification: Notification) {
        updateColors(from: notification)
    }
}


テーブルビュー、コレクションビューでも同様にノティフィケーションを受信します。

extension UITableViewController {
     @objc override func didChangeColorTheme(_ notification: Notification) {
         updateColors(from: notification)
         tableView.reloadData()
     }
}

extension UICollectionViewController {
     @objc override func didChangeColorTheme(_ notification: Notification) {
         updateColors(from: notification)
         collectionView?.reloadData()
     }
}


テーマ毎に色の変更を行います。

extension UIColor {
    class func backgroundContent(for theme: Theme) -> UIColor {
        switch theme {
            case .light:
                return Palette.white
            case .dark:
                return Palette.black
            }
    }
    // . . .
}


ビューコントラー側でテーマカラーを適用します。

extension ViewController: ColorUpdatable {
    func updateColors(for theme: Theme) {
        view.backgroundColor = .contentBackground(for: theme)
        childView.updateColors(for: theme)
        // . . .
    } 
}



色覚障がいについて

・男性の8%、女性の0.5%が何らかの色覚障がいを持っているとのことでしたが、実際には地域によって割合は異なります。

色に意味を持たせて、緑色は正常、赤色は異常という文化が広がっていますが、それらを識別できないと、そのメッセージは伝わりません。

iOS10では、色覚障がい者向けのカラーフィルター設定を持っており、全ての色のコントラストに適用します。

f:id:base_hideo:20170307150130p:plain

色覚障がい者向けのフィルター設定を適用します。

import InclusiveColor

extension UIColor {
    class func primaryText(for theme: Theme,
                        blindnessType: InclusiveColor.BlindnessType = .normal) ->      UIColor {
        let color: UIColor = {
            switch theme {
            case .light:
                return Palette.black
            case .dark:
                return Palette.white
            }
        }()
        return color.inclusiveColor(for: blindnessType)
    }
}