カテゴリー: programming

iOSDC Japan 2017にてベストトーク賞をいただきました

 2017/09/15〜17 の 3日間にわたって開催されたカンファレンス iOSDC Japan 2017 に参加してきました。私は2日目の 14:20〜14:50 に Track A 会場にて「RxSwift の Observable とは何か」というタイトルでセッションを持たせていただきました。

 このまわりの話をちゃんと解説しようとすると、最低でも60分は必要かなと思っていたのですが、最長の枠が30分しかなかったのでやむなしで CfP を提出しました。発表内容については以前からコードを追っかけてた分野だったので改めてする作業もそんなになく(とか思ってたんですが、以前に真面目にコードリーディングしたSwift1.2/2.x時代と比較してかったるい diff とかがあって地味に胃が痛かった)、スライドとか口頭発表原稿とかを書き起こす程度でした。ただ当時の自分は頭がおかしかったようでスライドを Illustrator + In Design で作り始めてしまい、次回から素直にパワポで作ろうと思いました(細かいレイアウトを指定できるのはいいけどめんどくさかった)。

 お話の構成をするなかで、300lines近くの実装コード解説をどうやったら、スライドとトークの内容で聴いていただく皆さまに理解してもらえるかをだいぶ悩みました。ただでさえ、発表のスライドにコードがあるとウッとなるはずなのに、それを30分間ひたすらやり続けたら、地獄でしかないので、この部分をクリアする点に力を注ぎました。そこで、ベースとなるソースコードを頭のスタック領域に置いていただくための説明に発表の半分以上の時間かけて、その上で、Reactive Extensions 特有の diff を解説するというトーク構成にしようと決めました(発表の7日くらい前に)。

 実際発表のリハーサル的なものを自分一人でやってみたんですが、どうやっても45分くらいかかってしまい、泣く泣くスライドのタイトルに(余談)という文字列を入れて、口頭発表の内容からは削ったりしました。来年から45分枠とかほしい…。

 今回のお話は決して技術的レベルの高い話ではなくて、まとめてしまうと有名な設計パターンとその発展形についてのお話でした。他の方のセッションをいくつか拝聴させていただいたのですが、私よりも技術的にはるかにハイレベルなことをやっていたり、普段自分が無意識にやっていてわりと共感できるようなお話がいくつもあって楽しかったです。

 特に面白かったセッションは @k_katsumi さんの Building High Performance and Testable UI component で、自分が普段 iOS のプレゼンテーション層を書くときの考え方とだいぶ近くて非常に共感できました。自分のやっている方法を自分自身でしっかり論理的根拠を持ってある程度正しそうという気持ちを持てるようにできるのが一番いいですが、やっぱり似たような手法を他の方もやられているという話を聞くと気持ち的には安心できますね…。

 あとは @kitasuke さんの Introducing protobuf in Swiftのあとに、Ask the speaker まわりで protobuf vs swagger みたいな話ができて楽しかった。サーバー〜モバイルクライアント間での protobuf 利用がどのようにされているのかの事例、ちゃんときいたことがなかったので興味深かった。あと protobuf とエラーハンドリングまわりの話も大変参考になった。最後には、結局APIクライアントを人間が書くことを撲滅していきましょうみたいな気持ちをみなさんと共有できたところがよかったですね…。撲滅していきましょう。

 素晴らしいセッションがたくさんある中、私の稚拙な発表を聴いていただいた皆さま、運営スタッフのみなさまなど本当にありがとうございました。そしてまさか自分がベストトーク賞をいただけるとは本当に思っていなかったので、LT枠の無限ビールタイムの時間にビール4本飲んでいい感じに意識があいまいになっていました。粗相してなかったですか?大丈夫ですか?

 ベストトーク賞の商品としてサイバーエージェントさんから素晴らしいお品をいただけるようで本当にありがとうございます。アベマTVさん最高のサービスです。アメーバブログさんでよく声優さんのブログ記事と写真を見ています。最高のサービスです。いただいたディスプレイでアベマTVさんとアメーバブログさんを見ます!

アメーバブログさんで特に見ているのは以下のブログです

アメーバブログさん最高のサービスです。私も声優さんのブログになりたい。

 2日目の発表が終わったあと、残念ながら Wake Up, Girls! 4th Live Tour 東京公演 夜の部がイベ被りしており、懇親会には参加できませんでしたが、来年 Wake Up, Girls! のイベントが被っていなければ懇親会に参加したいなと思います。ハッシュタグ見てたら美味しそうな料理と充実したクラフトビールとハイレベルなエンジニアがたくさんいてとても楽しそうだった。私、ビール(詳しくはないのですが)大好きなので、スポンサーの転職ドラフトさん来年も無限ビールお願いします。

 1日目夜のスピーカーズディナーは、やっぱり真剣に技術を追っている人たちが集まっているだけあって、楽しかった。まだまだ私は技術的にも精神的にも未熟な社畜4年生で、年上の方はもちろんのこと、最近だと高校生・大学生なのに、いまから自分がどれだけ努力しても追いつけそうにない人がインターネット上にはたくさんいるので、ちゃんと技術やお仕事に真摯に向き合う姿勢だけは忘れずに生きていきたいな、というなんか真面目な文章を、最後に書き残しておきます。

ひとまずみなさまお疲れ様でした。ありがとうございました。

appendix

iOSアプリ開発の全体像

超技術書展で頒布したiOSアプリ開発の全体像をだらだら書いた本を記事として公開。
ただのポエムです。

1. iOSアプリ開発を取り巻く環境

iOSアプリ開発には、基本的にmacOSを搭載したコンピューターとXcodeとよばれるソフトウェアが必要です。もともと主にObjective-Cという言語が使われるケースがほとんどでしたが、2014年6月にAppleがプログラミング言語Swiftを発表して以後の新規開発には、ほとんどの場合Swiftが採用されているようです。またSwiftは、Objective-Cのコードと共存できるため、もともとObjective-Cで開発されていたアプリを徐々にSwiftに移行しているという話もよく聞きます。

ただし、広告やSNSなどのSDKや、幅広く使われることが予想されていて安定性が必要なライブラリについては、依然としてAPIが安定しているObjective-Cで開発が行われているケースが多いと感じます。裏を返すとSwiftは言語としてまだ成熟していないということです。Swift2系までではマイナーバージョンアップデートでも、激しい破壊的変更が行われていました。Swift3系ではある程度落ち着きは見えますが、安定しているとは言い難い状況ではあります。それでもObjective-Cに比べて平易な構文、オプショナルやクロージャといったモダンな言語機能、そして静的型付けによる安全なプログラミングなど、得られる恩恵が大きいのは確かです。

1.1. iOS, Xcode, Swift, macOSのアップデート

Appleは定期的にiOSのメジャーバージョンアップデートをリリースしています。またSwiftのアップデートもさかんに行われています。これらに対応するためにはXcodeの更新が必要になるケースが多いです。Xcodeはファイルサイズが大きく、回線速度にもよりますがダウンロードに数十分、圧縮ファイルの解凍にまた数十分かかります。さらに、新しいXcodeを入れるには、新しいmacOSを入れなければならないことが多々あります。macOSの更新に1〜2時間かかることが珍しくありません。

実際のところ、こうした大きな更新があると最悪、半日ほど開発がストップしてしまいます。また、これらの作業は開発者やCIサーバーも含め全体のマシンでやる必要があり、考慮に入っていないと意外に手痛い時間のロストとなります。

これまでの動向では、Swift下位バージョンの切り捨ては早く、新しいバージョンに素早く追従しないと最新のXcodeではビルドができないなどの問題が発生する可能性があります。Swiftを利用すると決めた時点で、遅くとも2〜3ヶ月以内に最新のSwiftバージョンでビルドできる状態に持っていくような開発体制をとれるよう、あらかじめ開発工数のバッファをとることを強くおすすめします。

1.2. Xamarinという選択肢

iOSアプリ開発にはSwiftやObjective-C以外の言語を使うこともできます。その中でも代表的なものが、クロスプラットフォーム開発ツールのXamarinです。これによりC#やF#といった言語でのiOSアプリ開発が可能になります。これらは言語としては安定していて、C#はエンタープライズ方面での実績も十分にあります。またIDEとしてVisual StudioやXamarin Studioなどを利用することができます。ストーリーボードなどのUIコンポーネントを見た目通りに配置しながら画面を作るためのツールInterface Builder相当のことも、これらのIDEで実現可能です。

Xamarin社はMicrosoft社に買収され、現在個人での利用や特定の条件下において無料で開発環境を手にすることができるようになり、徐々に普及している段階の技術です。とはいえ、すでにフェンリル社が開発を行なったNHK紅白歌合戦のアプリなどで十分な実績があり、実用に耐えうるものといってよいでしょう。

Xamarin.iOSとXamarin.Forms

XamarinのなかでもXamarin.iOSとXamarin.Formsという2つのAPI群の選択肢があります。Xamarin.iOSはiOS SDKのAPIとほとんど1対1に対応しており、SwiftやObjective-Cと似たような感覚でアプリを構成することができます。一方、Xamarin.Formsは各プラットフォームのAPIを抽象化したものを使ってアプリを構成していくため、Xamarin.Formsに特化した知識をつける必要があります。その代わり、1つのコードから複数のプラットフォームをターゲットとしたアプリを作成することができ、開発効率の向上やマルチプラットフォーム間での実装のズレを防げるといったメリットがあります。

1.3. React Nativeという選択肢

Xamarin以外にもクロスプラットフォーム開発ツールとしてFacebookが推し進めるReact Nativeという技術があります。すでにInstagram, airbnb, Facebook Messengerなど大きなアプリですでに利用されています。各プラットフォームにおけるUIコンポーネントを抽象化したReactコンポーネントを組み合わせてアプリを構成するという思想を持っています。おおよそReact.jsと同じような形でアプリケーションを構成することができます。最近流行りのReact+Redux構成を取ることもできるため、JavaScriptに詳しい方はチャレンジしてみるのもありかもしれません。

1.4 iOSアプリ開発未経験者がとるべき選択肢

iOSアプリ開発においては、基本的にiOS SDKの基本的な振る舞いを理解することは必須であるため、経験者がひとりもいないチームでの開発はObjective-CかSwiftを採用するのが良いでしょう。当然ながら書籍や情報の量、サポートできる人材の量も一番多いです。iOSアプリ開発経験者がいるのであれば、どの方法を選んでも良いと思いますが、Xamarin.Formsを選んだ場合には、iOS SDKを直接触った場合とかなり勝手が異なるはずなので、十分な技術調査・検討を行ってから採用することを強くお勧めします。

2. iOSアプリ開発の流れ

iOSアプリはおおまかに「企画」「要件定義」「設計」「実装」という流れで開発を進めていくことが多いでしょう。最終的にはAppleによる審査を経てApp Storeでの公開が可能になります。この審査は他のプラットフォームに比べて厳しく、単純で機能が少ないアプリはAppleによりリジェクトされる可能性が高いです。例えばアプリ開発の練習として簡単な電卓などを作っても、App Store上には類似のアプリが無数にあるため、公開できない可能性があるということになります。

2.1. 企画

アプリを通してどのようなユーザーにどのような体験を与えたいのか、ターゲットユーザーと実現したいことを明確にする必要があります。似たようなアプリがある場合は、それらの調査を行い、どのように差別化を図りたいのかを検討すると良いでしょう。また「App Store審査ガイドライン」に違反する内容のアプリはたとえ出来上がっていてもApp Storeに出すことができない可能性が高いため、企画を始める段階からある程度を考慮することを強くお勧めします。特にアプリ内課金を導入する際にはAppleの審査はよりいっそう厳しくなるため、審査ガイドラインに沿った形に企画を着地させる必要があります。

2.2. 要件定義/デザイン

おおまかな企画が定まったら、アプリをどのように構成するのかを考えるフェーズに入ります。ユーザーにアプリの中をどのように回遊してもらい、どのような体験を提供するのかを具体化させていきます。また提供するコンテンツやアクションの階層構造/論理構造をはっきりとさせておくと、要件やデザインに落とし込むときに混乱を防ぐことができるので、この段階で整理しておくといいでしょう。例えば電子書籍リーダーを作る場合は、本一覧がベースの階層となり、1つ深い階層にシリーズが存在します。さらにシリーズには複数の本が紐づくといった具合です。

おおまかにやりたいことが見えてきたら、プロトタイプを作成してみましょう。紙ベースで画面を描いたり、矢印で動きを表現したりといった簡単なもので構いません。またAdobe Experience Design(XD)などを使うとより高度なプロトタイピングが行えます。実装コードを書かずにおおまかな動きを見ることができるため、複数のチームメンバ間でイメージを共有するにはうってつけのツールだといえます。iOSアプリの細かなデザインについての話は3章に記します。

2.3. 設計/実装

要件がある程度定まったら、アプリの技術的な設計フェーズに入ります。また特に要件のなかで実現が難しい点があれば、本当に実現可能なのか検証してみる必要があります。具体的な話は4章以降に記していきます。

2.4. ベータテストと品質保証

開発中はCrashlytics BetaやDeployGate、小規模であれば自前でのAd-Hoc配信などを通して開発チーム内でドッグフーディングを行うことにより、アプリの不具合検出や改善などを行います。また、ある程度の状態まで達したら開発に携わっていない第三者にも利用してもらい、フィードバックをもらえるようにしておくと、開発チームでは気づけない問題が発見できるでしょう。

いよいよリリースしても問題ないという段階が近づいてきたら、アプリの動作確認項目一覧表などを作り第三者にチェックをお願いすると、より安定したアプリをストアに出すことができると思います。これは一般的に品質保証(QA)のステップとよばれることが多いです。

2.5. 審査提出とリジェクト対応

大きなアプリとなると一発で審査をパスするのは難しく、Appleから何らかの指摘を受けてリジェクトされると思っておいたほうがよいでしょう。あらかじめリジェクトされることを想定して、ある程度アプリが出来上がってきたら、とりあえず審査に出してみるのも戦略のひとつです。これによりリジェクト対応のための仕様変更が生じる点を早めに知ることができます。

2.6. リリース

審査に通過したら晴れてアプリをApp Storeにリリースすることができます。審査提出時にはリリースタイミングのオプションがあり、そこで指定されたとおりにリリースが行われます。オプションは以下の通りです。

  • 手動でリリース: 審査通過後、リリースボタンを押したタイミングでリリースされる
  • 自動でリリース: 審査通過後、自動でリリースされる
  • 指定時刻以降に自動でリリース: 審査通過後、指定の時刻を過ぎたタイミングでリリースされる

なお、オプションはiTunes Connectというアプリの管理に使われているサービスの仕様変更によって変わる可能性があります。

3. iOSアプリのデザイン

iOSアプリのUI/UXに関してはAppleが公式に「iOSヒューマンインターフェースガイドライン」を制定しています。ガイドラインに沿わないデザインや実装を行うと一部は審査でリジェクトされる可能性があるため、開発を始める前にざっくりと目を通しておくとよいでしょう。ここではアプリ全体のデザインに影響してくるポイントを数点、記述します。

3.1. なるべく標準UIに沿ったデザインにする

特段デザインにこだわりがなければ、iOSのメールやSafari、設定画面などの標準的なアプリに沿った形でUIコンポーネントを利用し、画面を構成していくのがよいでしょう。UIコンポーネントを過剰にカスタムして使うと、場合によってはユーザーがどのように利用すればよいのか迷ってしまう可能性があります。また、iOSバージョン間で見た目や振る舞いを統一するのが難しくなり、最悪の場合は特定バージョンにおいて期待した動作を提供できなかったり、クラッシュを引き起こしたりする原因にもなります。

3.2 アプリ内の画面遷移

アプリを作る際に1画面だけで構成することは非常に稀で、ほとんどの場合には画面を遷移させる必要が出てくるでしょう。iOSの画面遷移には、主に次の2つのパターンが存在します。

  • プッシュ遷移
  • モーダル遷移

プッシュ遷移とはiOSの設定画面などで見られる右方向に階層構造を掘るように遷移するタイプのものを指します。この遷移は、スワイプで戻ることができ、コンテンツ階層間の移動をスムーズに行うのに適しています。この階層構造は、提供するコンテンツの階層構造と揃えてあげるのが一般的で、ユーザーにも理解しやすい形になるでしょう。たとえば音楽プレイヤーであるiTunesアプリは、ライブラリ→プレイリスト一覧→プレイリスト→楽曲というコンテンツ階層構造になっています。当たり前のことではありますが、きちんと分類して構成されているアプリは意外に多くありません。アプリデザインの構成を考える際に同時に提供するコンテンツ階層構造を整理することは、きっとデザインをする上でも役に立つことでしょう。

一方、モーダル遷移は、ビューが現れている間はその要素内でしか操作ができないようなものを指します。例えば、iOSのSafariのブックマーク一覧はこのタイプの遷移をします。また注意喚起のダイアログなどもモーダル遷移にあたります。何かオプションを指定したり選択したりする際に使われる傾向にあります。しかし、モーダルを閉じるには基本的にはタップをする必要があるため、ユーザーに煩わしさを感じさせてしまう可能性が高まります。最近は、モーダルビューを前の画面の上に浮いた状態で表示し、モーダル画面自体をスワイプすることで閉じる実装も目立つようになってきました。これにより閉じるためにタップをしなければならない問題が解消され、モーダル遷移に対するストレスを緩和させることができるため、可能であればそういった実装も考えてみると良いかもしれません。

3.3. デバイスの大型化とデザイン

iPhone 6 PLUSの登場時には、そのデバイスの大きさに随分と驚いた方も多いのではないでしょうか。実際iPhone 5のデバイスサイズは4インチであったのに対して、iPhone 7 PLUSでは5.5インチになっています。解像度も高くなり表示領域が増え、デザイン表現の幅が広がったのは間違いありません。

しかし、デバイスが大型化する一方で、人間の手の大きさは変わっていない点には注意する必要があります。右手でデバイスを操作する場合、左上や左下に配置したボタンなどには指が届きにくく、アプリのスムーズな操作の妨げになります。以前はモーダル画面の閉じるボタンを左上に配置するケースが目立っていたのですが、近年は右上や右下などに配置されているのをよく見ます。また、先述したとおり、モーダル画面自体を上下にスワイプすると閉じることができるように構成されているものもよく見ます。本質的には触り心地が良い形に画面を構成していければ、きっと良いアプリになるのではないかと考えています。

4. アプリをどのように構成すれば良いのか

初めてアプリ開発をする際に何に気をつければよいのか、とても不安になる気持ちは非常にわかります。筆者も初めてアプリを構成する際に何かアプリ開発特有の構成・設計を取る必要があるのではないかと思い調査をしました。結局のところネイティブアプリは、裏で処理をしつつも、ユーザーの入力を常に待ち受けつづける必要があるため、UIスレッドをブロックしないように注意するという点に注意していれば、一般的なプログラミングとほとんどかわらないと思います。したがって基本的なプログラミングができる方は何も心配せず、以前から実践していた技法を使いアプリを構成することができるでしょう。

iOSアプリ開発が本当に初めてである場合は、まずラベル(UILabel)、ボタン(UIButton)、テキストフィールド(UITextField)、画像(UIImage)などの基本的なコンポーネントの使い方を覚えると良いでしょう。続いて複雑なビューを構成する基本的な要素であるスタック(UIStackView)、テーブル(UITableView)、コレクション(UICollectionView)、スクロール(UIScrollView)、タブ(UITabBar)の使い方を学ぶと、自分の思い描いたビューを実現するための下地が整うのではないでしょうか。

4.1. 複雑な設計を採用する前に考えたいこと

近年、iOSアプリ開発をする上でどのような設計手法を取るかというような話題をよくみかけます。「iOSクリーンアーキテクチャ」や「ヘキサゴナルアーキテクチャ」など熱心に議論が行われています。これらの議論や記事ではアプリの規模感についての前提が共有されていなケースが多く、場合によっては過剰な構成となり、コードの可読性や開発のスピードを下げてしまうこともあるでしょう。開発メンバー間で考え方も一致させていく必要があり、本当に必要なものが何なのかを見極めることが非常に重要です。

iOSアプリのコードベースは往々にしてそれほど大規模なものにはならず、だいたい1万〜2万行程度で構成されることが多いと思います。アプリ全体のアーキテクチャパターンを考えることは確かに大切なことですが、私自身はSOLID原則やDRY原則、YAGNI原則などのシンプルで基本的なプログラミング原則を意識してコーディングしていくように心がけています。アーキテクチャのたくさんの決まりごとを意識しつづけるのは難しいことです。少数のシンプルで明確な決まりごとを意識していると実は自ずと、本に書かれているようなアーキテクチャになっていることは多々ありますし、そもそも複雑なアーキテクチャの根底思想にはシンプルな原理原則があることが多いと思います。

4.2. ライブラリの選定

アプリのすべてのコードを自前で書くことは稀で、多くの場合オープンソースライブラリの助けを借りることになると思います。ライブラリをうまく使いこなせば大幅に開発期間を短縮することができ、場合によっては多くの人に使われたり、メンテナンスされているため、一人で書いたものに比べ品質の高いコードを利用することができるなどというメリットもあるでしょう。

ただし、Swiftのライブラリについては、今後予想される言語自体のアップデートに継続的に対応させる必要があります。過去に大きな変更が幾度も入ったため、GitHubでStarがたくさんついていて、多くの開発者が利用しているライブラリでも、最新の言語バージョンにアップデート対応がされず、放置されていることは珍しくありません。利用する前に、メンテナンスが継続して行われているかをGitHubのPulseなどで確認しておくことをおすすめします。また、メンテナンスが止まってしまった場合、公開リポジトリからフォークして、自分でメンテナンスしていく覚悟が必要です。

これはSwiftのライブラリに限ったことではないですが、基本的にはコードの8割程度をざっくりと斜め読みし、コードの設計・品質・メンテナビリティ・適切なテストが存在するかなどを確認してから依存を決めると失敗がないと思います。また利用した各ライブラリのライセンスを遵守し、必要であれば必ずライセンス表記をしましょう。

4.3. ライブラリへの依存

ライブラリを導入すると一口に言ってもいろいろなやり方があります。たとえば秘匿情報などを管理できるキーチェーンにアクセスするライブラリを使うことを想定してみましょう。何も考えずにViewControllerの必要な箇所でライブラリをimportして依存コードをばらまいていくというやり方は、最初の実装者にとっては一番手が抜けて楽かもしれません。しかしこれには大きな問題があります。ものにもよるとは思いますが、おそらくライブラリを利用している周辺のコードは似たような実装が繰り返し登場しているのではないでしょうか?またライブラリのインターフェースが変更されたり、利用するライブラリを差し替えたりする場合、あちこちに散らばったコードに手を加える必要がでてきます。

したがって、個人的にはライブラリを利用する際にはなるべく依存コードを記述する範囲を狭くするように意識することが多いです。大半の場合はひとつのクラスの中に閉じ込めることができるはずで、その上でプロトコルを切り、実装クラスを差し替えられるような構造にするのが好みです。こうすることによってライブラリを差し替えたり、自前の実装に切り替えたりするなどの対応も容易になります。またテストの書きやすさにも繋がる場合もあると思います。

5. アプリと非同期処理

ユーザーインターフェースの存在するアプリは常にユーザーからの入力を受け付ける状態を保たなければならないという制約があります。したがって、何か重たい処理をするときは別のスレッドに仕事を任せる必要があります。この重い処理にはどんなものがあるでしょうか。例えばサーバーにデータを取得する際はリクエストを送ってからレスポンスが帰ってくるまで時間がかかります。この間ユーザーの入力を一切受け付けないアプリは控えめにいって最悪でしょう。また画像の変換や複雑な計算などはユーザーからのアプリへの入力をブロックしてしまいます。

これを避けるためには、重たい処理はユーザーからの入力を受け付ける担当とは別のものに任せればよいでしょう。ユーザーからの入力を受け付けている窓口はメインスレッドとよばれています。対してその裏で処理を行なっているものをバックグラウンドスレッドとよんでいます。また、あるタスクの実行を止めずに別の処理を行うことを非同期処理と言います。iOSアプリ開発において非同期処理はスレッドを利用して実現されていますが、Swift, Objective-Cからは開発者が直接スレッドを触ることなく非同期処理を取り扱える仕組みが用意されています。それがGrand Central Dispatch(GCD)とオペレーションキューになります。それぞれどのようなものかざっくりと特徴をみていきます。また、Appleが公式で提供している「並列プログラミングガイド」および「スレッドプログラミングガイド」を参照するとコードレベルの技術詳細に迫ることができると思いますので、ご覧ください。

5.1. Grand Central Dispatch(GCD)

GCDを利用する場合もオペレーションキューを使う場合も、基本的にはキューにジョブを積むというのが基本的な実装内容になると思います。ではこの2つの何が違うのかというと、用意されているキューの種類とジョブをクロージャで渡すのか、オブジェクトで渡すのかという点になります。

GCDには、キューイングされた処理を逐次実行していく直列ディスパッチキュー(Serial Dispatch Queue)と並列に実行していくのが並列ディスパッチキュー(Concurrent Dispatch Queue)が用意されています。さらに特別なキューとしてメインスレッドで行いたいタスクをキューイングするためのメインディスパッチキューというものが用意されています。メインスレッドはひとつしかなく、並列にできないので当たり前ではあるのですが、これは構造的には直列ディスパッチキューになります。

さて、実際のアプリの中でこれらがどのように利用されるかを少し想像してみましょう。例えばボタンを押したイベントを皮切りにサーバーにHTTP通信を走らせ、その結果をテキストボックスに表示するケースを考えてみましょう。最初にメインスレッドがボタンの押下イベントを受け付け、イベントハンドラを呼び出します。続いて、イベントハンドラは並列ディスパッチキューにクロージャをわたして、あとの処理をバックグラウンドスレッドにお任せします。このように、裏側で行わせたい仕事をキューに積んで放置することにより、ユーザーからの入力を受け付けられる体制に即座に戻ります。一方、バックグラウンドスレッドではどのようなことが行われているでしょうか。並列ディスパッチキューに渡されたクロージャの中身をみてみましょう。まずHTTPリクエストをサーバーに送るでしょう。そしてレスポンスが帰ってきたら、そのデータをよしなに加工して、テキストボックスに反映させる必要があります。このときに注意が必要で、基本的にUIコンポーネントを更新する際はメインスレッドでやる必要があります。したがって、加工し終わったデータの準備ができたら、今度はメインスレッドに処理を行わせるための直列ディスパッチキュー(DispatchQueue.main)にクロージャでお仕事を渡します。お仕事内容は単純にデータをテキストボックスに反映させるだけです。

5.2. オレペーションキュー

オペレーションキューを用いる非同期処理では、タスクを表現したデータ構造であるOperationを作り、キューに渡していくという流れになります。したがってあらかじめタスクを仕込んでおいて一気に並列/直列で実行するなどということが可能です。実際のところGCDのラッパーでしかないため動作原理はほぼほぼ同じになります。しかし、クロージャではなくタスクというオブジェクトを引き回せることでより柔軟で複雑なプログラミングが可能になると思います。

5.3. スレッドセーフな実装

複数のスレッド間で共有されるオブジェクトは不正な状態になる可能性をはらんでいます。不変なオブジェクトであれば問題はないですが、状態を持つオブジェクトには不正な状態になることを防ぐための実装を施してあげる必要があります。一般にマルチスレッド間でのオブジェクト共有に対して安全であることをスレッドセーフな実装とよんでいます。スレッドセーフな実装を実現させるためのツールにはアトミック操作とメモリバリア、ロック、条件変数などがあります。それぞれ特徴が異なっており、用途に適したものを利用する必要があります。詳細についてはAppleが公式で提供している「スレッドプログラミングガイド」を読むと良いと思います。

6. アプリ内通信のこれから

多くのアプリではウェブAPIを利用したり、画像/音声/動画リソースをインターネット上から取得します。これからSwift/Objective-Cで開発をはじめようという方はどのようにしてHTTPリクエストを走らせるのかという点が気になるのではないでしょうか。

ネットで検索をするとAFNetworkingやAlamofireといったライブラリの名前がヒットすると思います。しかし個人的には標準で提供されているURLSessionで十分足りると考えています。複雑なPOSTリクエストの組み立てなどには一部ライブラリの助けがあると楽をすることができるかもしれませんが、それを除けばライブラリに実装されている機能を利用する機会はそれほど多くないでしょう。通信ライブラリに限らない話ですが、本当にそのライブラリを導入する必要があるのか、よく検討してみてください。

6.1. App Transport Security

App Transport Security(ATS)とはiOS9.0以降で導入されたサーバークライアント間でのセキュアな通信を保証するための仕組みです。Appleが推奨するTLSバージョンと暗号スイート、サーバー証明書とそのハッシュアルゴリズム、サーバー証明書の署名キーを満たしていない場合に接続エラーとなります。

2017年4月現在ではATSに対応することは必須ではなく、HTTP通信やAppleの推奨条件を満たさないHTTPS通信を行いたい場合は、Info.plistに設定を追加することでATSによる接続エラーを回避することができます。しかしながら、AppleはATSに正当な理由なく対応していないアプリをリジェクトするとの予告を出しており、今後アプリ開発をしていく上ではこれに対応することはほぼ間違いなく必須条件となるでしょう。

少なくとも自前で立てているサーバーと通信を行う場合は、Appleが定めた推奨条件に対応させましょう。ただし自分の管理外サーバーとの通信を行う場合は、ATSに対応しない正当な理由として認められるようです。

6.2. APIクライアントは人間の書くものではない

大半のアプリではREST APIを叩き、そのレスポンスをもとにビューを更新するなどの処理が入るのではないでしょうか。こんなときにはAPIクライアントの実装が必要になるでしょう。

残念ながらSwiftでAPIクライアントを書く作業はSwiftyではありません。URLSessionを用いてレスポンスを取得し、正常なリソースが得られたらJSONなりXMLなりをパースしてJSONObjectを手に入れます。続いてJSONObjectの各キーから値を取り出してSwiftで定義したエンティティの構造体にマッピングします。JavaやC#などを使うとシリアライズやデシリアライズの工程はだいたい手で書く必要はなく、十分に実績のあるライブラリが自動的にやってくれるため、とてもSwiftyなのですが、Swiftには現在そのような機能を実現するライブラリは存在しないようにみえます。またあらかじめスキーマが決まっているエンティティの構造体を書くのも非常に面倒臭い作業で自動化したい機運が高まります。

APIクライアントのレイヤーを自動で解決するための方法はいくつかあります。ひとつ目はSwaggerというREST API作成のためのフレームワークを利用する手法です。仕様を表現したYAMLファイルから自動でAPIクライアントのコードを生成できるのが特徴です。

またREST APIからは外れますが、Protocol Buffersを使うことによりデシリアライズやエンティティの作成を自動化できます。こちらはサーバー側から送出されるレスポンスもProtocol Buffersに対応させなければならないため、自前のサーバーを利用したアプリ作成のときに使える手段となります。サーバーとクライアント間の通信も含めて、すべて自動でコード生成したい場合はRPCフレームワークのgRPCの利用を検討してみてください。

7. ユーザーに笑顔を届けるまでがiOS開発

Appleによる過酷なダンジョンを切り抜けついに審査を切り抜けたみなさんは、ついにリリースボタンを押すことになるでしょう。いままで開発をすすめてきたメンバーと一緒にリリースボタンを押すとウェイ感がでてとてもエモいので是非おためしください。

7.1. AppStoreのねぼすけ

さて、時間はお昼の12時、無事リリースボタンを押した我々はAppStoreに飛び、アプリをダウンロードしてみようと試みます。しかしながら、アプリは見当たりません。Appleの実装はとてもlazy(怠惰)なのでリリースボタンを押してから反映までに30〜60分程度要すると思っていただいてよいでしょう。もし決まった時間にプレスリリースやSNSなどでリリース告知をする際には、あらかじめこっそりとアプリをリリースして、AppStoreに反映させておくのが良いと思います。

そんなこんなでアプリは13時にはストアに反映されていたとしましょう。ところがtwitterでエゴサするとダウンロードがうまくできないとの酷評が。リリースと同時につけられる1ツ星評価。実際のところ何が原因か分かっていないのですが、お昼の時間帯などはAppStoreからのダウンロードがうまくいかないときがあるようで、個人的にはリリースはアプリのユーザーがアクティブでなくなってくる時間帯にするようにしています。個人的にはAppStoreのアプリ配信サーバーを増強してほしいという気持ちをここに掲載させていただきます。

7.2. 俺たちの闘いはこれからだ!

アプリをリリースして、開発を終わらせるぞ!そう思っていた時期が私にもありました。しかしアプリ開発はリリースしてからが本番です。まずは多くのユーザーに使ってもらうためにきっと様々な仕掛けが必要でしょう。また発生させてしまった不具合の対応や、アプリをさらに快適に利用できるような新機能の提供などやることはたくさんあるはずです。そう、俺たちの闘いはこれからだ!

System.Net.Httpのリクエストやレスポンスをロギングする

SwiftのURLSession使って自前でHttpClient書いてたときは、リクエストパラメタとかいろいろをデバッグ時にログ出力してくれる便利なやつを自作していたんですが、.NETのHttpClientまあそのまま使うので、ロギングどうしようと思ってたらちゃんと用意してくれている。とても良い。

これを使ってリクエストを送ると例えば以下のようなログ出力が得られる

便利

ES2015+Flowなlambda関数をApexで管理する

ES2015 + 型チェッカー flow で書かれた AWS Lambda 関数を Apex で管理する方法について、つらつらとメモ。この記事は @nagisio 先生のご指導のおかげで成立しています(謝辞)。

  • キーワード
    • AWS Lambda
    • yarn
    • ES2015
    • flow
    • Apex

記事読むの面倒くさいので、動くリポジトリよこせという方はここをcloneすべし(動かしてから理解する方が楽なのですよねー :wakaru:)

ES2015 + flow + Apex の環境構築

yarn の導入

導入はかんたん。

  1. npm initpackage.json を作る
  2. npm install yarn --dev で yarn を導入する
    • global に入れたい方はどうぞ

package.json はきっとこんな感じになっているのではなかろうか

ESS2015 + flow の環境構築

まずは必要なものを片っ端からいれていく

次に .babelrc を次のようにする

node_modules/flow-bin/cli.js init でflow の設定ファイル .flowconfig を作り、以下のような感じにする

もう多分この時点で ES2015 + flow のコードが書けるので適当に src/hello/index.js に適当なコードを書いてみよう

続いてトランスパイルして functions 下に成果物を出力してみよう

成果物は次のようになっているだろう

良い感じにハローワークに行きたいくなる良いラムダ関数ですね。さて、ここまできたらあとは Apex を導入して AWS にデプロイするだけです。

ちょっとその前に、トランスパイルするコマンドが長ったらしくてだるいのでエイリアスを張っておきましょう。package.json の script の部分をちょちょいといじるといい感じに npm run build で諸々実行できます。

Apex の導入

今回 AWS Lambda 関数の管理には Apex をつかいます。Apexの挙動は非常に単純で project.json に指定されたリソースやタイムアウト時間、IAMロールの設定で、functions直下にある各ディレクトリをそれぞれラムダ関数とみなし、デプロイしてくれます。

導入はかんたん

project.json をイカのよう設定すればよいでゲソ(各自IAMロールは適切なものを指定してください)

apex deploy でデプロイできます。apex invoke hello で実行できます。

良い

ESLint の導入(お好みで)

ESLintを入れたい方は適当に必要そうなやつをどうぞ(適当)

.eslint を適当に書く

deploy 前に uglify の処理を入れる

lambda の起動時間は多分デプロイされる成果物のサイズが小さい方がはやいので、uglifyをかけたほうがよさそう。packaage.jsonを書き換えればいい感じに実現可。

npm run build:prod で functions 下にできる成果物を確認してみるとちゃんとできてるか確認出来る。

ステージ管理

このあたりの対応が一番筋がよさそう👉 Lambda with Apex: 環境変数で環境別にLambda環境を整える

おわりに

ES2015 + flow + Apex で快適な AWS Lambda ライフを!

RxJava本がなかなかよさそうな内容だった件について

おととい発売された RxJava リアクティブプログラミング、 Amazon から背表紙が裂けた悲しい状態で到着し商品交換依頼を Amazon に出しつつ、本をナナメ読みしてたら休日が終わってしまった今日この頃、みなさまいかがお過ごしでしょうか。

 さて、本の内容がなかなか良い内容な雰囲気だったので軽く感想とともにアフィリエイトリンクを貼ってみんなにふんでもらって金儲けがしたいなぁとか思ったので、感想文を書きました。

 あ、あと私そんなに Rx サイコーみたいな人間ではないので… 念のため。(とはいえ iOS アプリ開発では RxSwift が一番よさそうっぽい気がする)

第1章の素晴らしいボリューム感

 Rx 系のライブラリというと、まずリアクティブプログラミングとは何かについて、オタクは嬉しそうに早口で語り始めます。だいたい領域外の人間の理解なんて御構い無しの言説を垂れ流してくる方が多いですよね。この本の第1章では、初見の方に必要のない部分についてはどんどん後回しにして、押さえるべきキーワードをピンポイントで押さえてあり、かつ実際に動くコードを提示した簡潔で適切な例示により基本的なライブラリのふるまいなどがわかりやすくまとまっています。RxJava 1系と2系の根本的な違いについては、普段 RxJava を使ってない(RxSwiftを使ってます)自分にはなるほどなぁと思わせてくれる内容でした。Reactive StreamsやRxJavaの基本的な仕組みについてもわかりやすいシーケンス図とコード、解説があってよかった。その後は実際に RxJava を使ったコードとそのふるまいの解説が載っていて、基本的にRx系ライブラリは「まず使ってみてふるまいを見てから理解したほうが学習しやすい」と思っている自分の考え方にマッチしていてよかった。1章読んで、写経すればだいたいみんなライブラリ使えるようになるのでは…?(ホントか?) Rx系の経験がないかたでも 3時間あれば読んでコード動かせるボリューム感だと思いますのでトライしてみてください。

Observable についてきちんと説明がある

Rx の柱の一つである Observable の背後に潜む、push 型 Observer パターンについてしっかりと解説されていて、とてもわかりやすい。私もむかしこのあたりのことについて記事にまとめたり、発表したりしました(唐突なリンク)。

また非同期処理の部分のスケジューラまわりについては Java でどうなっているのか気になっていたので、簡潔にまとまってて、とてもありがたかった。

第4章オペレーターまわりはだいたい知ってるのでまあ読まなくていっかみたいな雰囲気でいま5章を読んでる。Rx系のライブラリをこれから使おうという方は公式のチュートリアルとか見つつ、Qiitaの適当な記事みるくらいならこの本が良いのではとか思ってたりします。

現場からは以上です。


ちなみに自分は基本的に RxJava は触ったことありませんが、悲しいことに Swift という大変すぐれている言語を書いている今日この頃ですので RxSwift はそこそこ触っているほうだし、ソースコードも8割くらい読んだりしてます。なので、ライブラリ自体はほぼ触っていない RxJava 本とはいえ、スラスラ読めました(だいたい知ってる内容だった)。

serf を試す

  • serf で /etc/hosts とか config の更新とかをクラスタ全台にサクッと反映させたい
  • でも serf そのものがよくわからんのでまずは hello, world から
  • 資料は https://www.serf.io/intro/getting-started/install.html

  • ご覧のように、全台に簡単に通知を飛ばせる
  • この通知をフックして、更新をするシェルスクリプトを仕込めば /etc/hosts の更新や config の更新がお手軽にできる気がする

そこで以下のような簡単なスクリプトを用意(node01, node02に配置)

一旦各マシンで serf を落とし、イベントハンドラを指定して再起動

これは良さそうだ。実践編として /etc/hosts や config 系の反映についてどうすれば良いか気が向いたら書きます。

iOSアプリ開発基礎ハンズオン

この資料はラビットハウス社内で開催される、iOSアプリ開発未経験者向けのハンズオン会向けに作成されたものです。このハンズオンを一通り行うことにより、iOS開発において以下のことができるようになります。

  • 基本的なUIKitコンポーネントの利用
    • UILabel: テキストの表示
    • UIButton: ボタンの表示
    • UIBarButtonItem: バーボタンの表示
    • UITextField: テキストフィールドの表示
    • UITableView: テーブルの表示
    • UITableViewCell: テーブルセルのカスタマイズ
    • StoryboardとAuto Layoutの利用: UIをデザインベースで構成する仕組み
    • プッシュ遷移とモーダル遷移: iOSの大きな2つの画面遷移パターンへの理解

お品書き

0. はじめに

 iOSアプリ開発自体は技術的にはそれほど難しくないため、他の分野のエンジニアに開発に関わってもらえるようにハンズオン会を開催しました。また、場合によっては、簡単なプログラミングができるアプリディレクターやデザイナーにも理解出来るようになるべく平易な内容とするように心がけました。 if, else などのキーワードや変数、関数などについてざっくりと理解していて、簡単な関数を実装できれば、この資料を見ながらきっと誰でもアプリ開発ができると思います。

 2時間でTODOアプリを作るところをゴールとして進めていきますが、プログラミングに慣れている方であれば1時間で終わってしまう内容かもしれません。しかしながら、iOSアプリ開発のフローは一通り体験でき、Googleで検索をしながらアプリ開発を進めていけるレベルには達するのではないかと思います。また、このハンズオンで実装を進めていくアプリのソースコードは https://github.com/53ningen/iOSHandsOn に置いてありますので、困ったときは参照してみると良いかもしれません。

それでは、iOSアプリ開発の世界に飛び込んでいきましょう。

1. 環境構築

このハンズオンを進める上で必要な Xcode 8.2 の実行環境を構築します

1.1 Xcode 8.2 の導入

 Xcode 8.2 を含めた過去のバージョンの Xcode は以下の URL からダウンロードすることができます。ダウンロード、およびインストールにはかなりの時間がかかりますので、時間に余裕のあるときにやることを強くお勧めします。

https://developer.apple.com/download/more/

1.2 .gitignore

自分がよくつかってる .gitignore は以下のような感じです

2. プロジェクトの作成、 Hello, world とその周辺

 ここでは実際に開発を進めるためのプロジェクトを作成し、簡単な文字を表示させるところまでを行います。

2.1 プロジェクトの作成

Xcode を起動し、 Create a new Xcode project を選択してください。

xcode

続いて Single View Application を選択してください。

ChooseATemplateForYourNewProject

 ProductName として、とりあえず今回のハンズオンでは iOSHandsOn を指定しておきましょう。また、Organization Name は適当に、Organization Id についてはサービスのドメイン名や、github の id を指定することが多いかもしれません。

 このあたりの指定については実際にプロダクトを AppStore へリリースするなどの際に重要になってきますが、今回のハンズオンアプリではとりあえずの値を指定しておけば良いでしょう。また、後から変更することも可能ですので、必要であればそのときに直せば良いと思います。

ChooseOptionsForYourNewProject

 以上をすすめると最後にプロジェクトを保存するパスを指定するダイアログが出てきますので、お好きな場所に保存すると良いでしょう。これでひとまず iOS アプリ開発のためのひな形を作成することができました。この状態で Command + R (アプリ実行へのショートカットキー) を押すと、iOSシミュレーターが立ち上がり、真っ白な画面が表示されると思います。

2.2 Hello, world

続いて簡単なサンプルとして画面に文字を表示させてみましょう。目標とするイメージは下図のようなものです。

 早速 Main.storyboard を選択し、右下にあるパネルからラベル(UILabel)を選択して上図のような画面を作ってみてください。ラベルに表示される文字はラベルをダブルクリックすると編集できるようになります。作業が終わったら再びシミュレーターで実行してみてください。きっとストーリーボードに入力した文字どおりの表示が出ているかと思います。しかしながら、このままでは画面回転をするときっとレイアウトが崩れてしまうでしょう。試しに Command + → キーを押して画面を回転させてみてください。

 画面が回転した場合でも上下中央位置に表示させたい場合にはConstraints(制約)を指定してあげる必要があります。ラベルを選択したあと、ストーリーボード編集画面右下のボタンを押し、下図のような設定にチェックを入れ Add 2 Constraints ボタンを押してください。それぞれ Horizontally in Container は水平方向中央揃え、Vertically in Container は上下方向中央揃えの意味となります。最後に右下5つアイコンがならんでいる部分の一番左、更新マークのボタンを押すと、追加された制約がストーリーボードに反映されます。

 この状態で再びシミュレーターを立ち上げ画面を回転させてみてください。きっと画面がどのような方向を向いていてもラベルは画面上下中央位置に表示されたのではないでしょうか。もしストーリーボードを使わずコードでUIを構成する際も同じような形で Constrain を指示してあげないと、画面回転時や異なる縦横比のデバイスでの表示がおかしくなることがありますので、デバッグ時に画面を回転させながらデザインが崩れないかをチェックしてみてください。

まとめ
* 開発中はこまめに画面回転を試し、制約のつけ忘れなどミスがないかを確認しながらすすめる

2.3 ボタンを押したら文字がかわる機能をつける

 画面にただ単に Hello, world と表示されているだけではつまらないので、ボタンを設置して、押したときの時刻が表示される機能を搭載してみましょう。まずはストーリーボードに対して下図のようにボタンを追加してください。

 続いてXcodeの右上にある ボタン(真ん中)を押してみてください。ストーリーボードとソースコードの2画面表示になったかと思います。この状態でラベルを選択したのち control キーを押しながらソースコードの適当な位置にドラッグすると下図のようなダイアログが出てくるでしょう。

 Name に label と入力し Enter を押すと @IBOutlet weak var label: UILabel! というコードが追加されると思います。これはストーリーボード上にあるラベルをコードから操作できるようにリンクしてあげたと思っていただければ大体良いと思います。ボタンに関しても同様に操作を行うと @IBOutlet weak var button: UIButton! というコードが追加されるでしょう。

 続いて、ボタンを押したときのイベント処理を書いてみましょう。 ViewController.swift を開いて、次のようなメソッドを追加してください。

 最後にボタンのタップイベント発生時にこのメソッドを呼び出すように設定するコードを追加します。

 これでボタンが .touchUpInside、つまりタップして指を離した瞬間に、先ほど定義したメソッドを呼び出すという紐付けができました。早速シミュレーターで動かして試してみてください。おそらく、ボタンを押すと現在時刻が表示されるようになったのではないでしょうか?

 ついでに、同じ仕組みを異なる方法で実装しておきましょう。一旦ストーリーボードのボタンを control キーを押しながらクリックして、New Referencing Outlet に紐付いているものを削除し、もう一度 control キーを押しながらソースコードへドラッグします。その際に下記のように ConnectionAction に変更します。この場合 Name の欄にはボタンの名前ではなく action の名前を指定することになるので buttonOnTouchUpInside など適当な名前を指定してあげてください。すると今度はメソッド定義が生成されたのではないでしょうか。この手法ではこうして生成されたメソッド内に、ボタンが押されたときの挙動を定義していくことになります。

さきほどと同じ内容を実現しようとすると最終的にコードは次のようになるかと思います。

 実装が終わったらシミュレーターで実行してちゃんと動作するかを確認してください。これで iOS で UI を構成する最も基本的なUI部品である UILabelUIButton の使い方の雰囲気は理解できたのではないでしょうか。

まとめ
* ボタンのイベント処理には @IBOutlet を使う方法と UIButton.addTarget を使う方法がある

2.4 トラブルシューティング

 この先ますます複雑なことをやるとしばしば謎のエラーがでて、うまくシミュレーターで実行できない場合がでてくるかと思います。そんな際は command + shift + alt + K キーを押して、ビルドした成果物をディレクトリごと消すということを試してみてください(mvn clean的なものになります)。また Xcode がおかしな挙動を見せてくることがしばしばありますが、たいてい再起動すれば治るかと思います。

 何か問題が生じた場合は無理に頑張ろうとせず、まず上記の2点を試してみるということを忘れずに、寛容な心で開発を進める必要があります。

3. 2種類の画面遷移

 1画面だけで構成されるアプリはほとんどなく、プロダクトとして出すものは、ほとんどの場合画面遷移が発生します。iOSの画面遷移には主に次の2つのパターンが存在します。

  1. モーダル遷移(下からビューが飛び出してくる、閉じるボタンでビューを閉じる)
  2. プッシュ遷移(右方向にビューがスタックする、スワイプで戻れる)

 iOSヒューマンインターフェースガイドラインにこの2つの遷移の使い分けの思想について書かれているので、Apple 信者の方々は是非そちらをごらんください。そうでない場合には普段のiOSアプリの利用の仕方に照らし合わせて適切な方を選んでいくと良いでしょう。例えばコンテンツの構造として 「漫画アプリ」 ⊃ 「漫画作品」 ⊃ 「漫画エピソード」 という図式が成り立つのであれば、きっと漫画アプリトップから作品への遷移と作品からエピソードへの遷移はプッシュ遷移が適しているはずです。また、この間の任意の場所でログインさせるというアクションをユーザーにさせたい場合はモーダル遷移でログイン画面をユーザーに提示し、ログインが完了したらその画面を閉じてあげると良いでしょう。

 余談になりますが、近年はデバイス自体が大型化してきており、モーダル遷移時の「閉じる」ボタンの位置が左上にあると手の大きさ的に届きにくい位置になるため、モーダルで出すビューの画面デザインをする際にこのあたりのことを気をつけると良いと思います(※ 個人の感想です)。前話が長くなってきたのでこのあたりにして、早速それぞれの遷移を実装してみましょう。

3.1 プッシュ遷移の実装

 プッシュ遷移をするためには現在の ViewControllerUINavigationController に属している必要があります(正確にいうと UINavigationControllerchildViewControllers の要素である必要ということ)。こんなこと言葉で言われてもわからないと思うので、早速作業をしながら理解していきましょう。

 ここではボタンを押すとプッシュ遷移をする仕組みを実装していきたいので、その準備段階として Main.storyboard でいままで「時刻を表示」となっていた部分を「遷移する」というテキストに変えてみましょう。続いて、ViewControllerを選択し、右カラムのメニュー Identity -> Storyboard ID の部分に Main というテキストを入力しましょう。

 そして「上部メニュー:Editor」→「Embed in」→「Navigation Controller」を選択してください。すると Navigation Controller とかかれたよく分からない画面が出現したと思います。と、同時にさきほどまでのビューの上部にも謎の灰色のスペースが生じてしまいました。とりあえず起動して動作確認してみましょう。

 上図のような状態になっているのではないでしょうか。さて、ここからプッシュ遷移を実装をしていきます。新しい画面を作るのは一旦後回しにして、まずは同じ画面に遷移するコードを書き足してみましょう。そのためにボタンのタップイベント処理のメソッド内を次のように書き換えます。

 この状態でシミュレーターで実行すると、ボタンを押したときにきちんとプッシュ遷移できるようになっているのではないでしょうか? またスワイプや左上の < ボタンで前の画面に戻れるようになっていると思います。

まとめ
* プッシュ遷移には navigationController?.pushViewController を使う

3.1.1 (プログラマ向けトピック) UINavigationController のふるまい

 UINavigationController の振る舞いについてちょっとだけ見ておきましょう。冒頭で UINavigationController は UIViewController をスタックしていくことが役割だと言いました。その様子を実際に見るためにページ遷移が終わった瞬間(viewDidAppear)に navigationControllerchildViewControllers をログ出力するコードを ViewController に仕込みます。

この状態でシミュレーター実行をし、遷移をしたり戻りながら、ログ出力を観察してみましょう。

きちんとスタック構造になっていることがわかるかと思います。

3.1.2 (プログラマ向けトピック) Storyboard と 型

 静的型付け言語になれたプログラマであれば、ViewController を取得するときのコードについて文字列で指定しているあたりに恐怖心を覚える方もいるのではないでしょうか? 実際この部分の指定を間違えると、静的チェックが走らず実行時にアプリがクラッシュします。

 この部分に対する対応策は各自で取らざるを得ないのが現状で、例えば次のようなやり方をしている人が多いです。

  1. 動作確認を徹底することによる対応(筋肉系)
  2. ViewController を生成するファクトリを作り、そのファクトリのすべてのメソッドについてテストを書き、確実にインスタンスが得られることを保証する
  3. ViewControllerStoryboard ID(または Storyboard のファイル名) の命名規則を定める、ViewController の型を受け取り、そのインスタンスを生成するファクトリメソッドを用意し、アプリ内ではそれを通してインスタンスを得る

 3. については具体的には、MainViewController に対する Storyboard の名前は必ず MainViewController にするという規則を設け、次のように ViewController の型名を取得して上記のようなことを実現するという手法になります。命名をミスれば安全でもなんでもない手法ですが、StoryboardやViewControllerの名前を変更することはまれであるため、手軽な方法ではないかと考えています。

3.1.3 (プログラマ向けトピック) Storyboard ID を使わず Storyboard に紐づくViewController を取得する

以下のようなコードで可能です。口頭で説明いたします。

3.1.4 プッシュ遷移画面から戻るボタンの実装

 プッシュ遷移をしたあとにスワイプで前の画面に戻れますが、ある条件のときに強制的に前の画面に戻したいことがあるかと思います。ここでは、ボタンを押すと前の画面に戻る機能を実装してみましょう。まずはおなじみ Main.storyboard を次のような状態に改変しましょう。またトラブル防止のため、各UIButton を control + クリックし Outlet を一旦クリアにしましょう。

 続いて、ストーリーボード上のボタンとコードを結びつける作業をもう一度やりましょう。その作業が終わったら、まず「プッシュ遷移する」ボタンについて以前やったとおりに addTarget を利用してプッシュ遷移の実装を行ってください。これがおわるとコードは以下のような状態になっているかと思います。

 つづいてポップボタンについてですが、こちらについては最初の画面では戻り先が存在しないのでボタンを無効化しておくのが親切だと思います。そのためにビューが読み込みされた時点(viewDidLoad)で navigationController!.childViewControllers.count (スタックされているViewControllerの数) が 1 より大きかったら有効にしてあげる処理を記述します。

 また、プッシュ遷移から戻る処理は navigationController?.popViewController(animated: true) という命令を呼び出してあげれば実現できるため、この処理とボタンのタップイベントを addTarget で紐付けましょう。

 シミュレーターで実行すれば期待したとおりの動作になっているのではないでしょうか。

まとめ
* プッシュ遷移からもどるときには navigationController?.popViewController を使う

3.2 モーダル遷移

 同様のノリでモーダル遷移についても実装していきましょう。モーダル遷移については UINavigationController うんぬんは一切関係なく、どのビューからもこの遷移が使えます。いつもどおり Main.storyboard に「モーダル遷移する」ボタンを、これまでの流れと同じ手順で一つ増やし、右上にモーダル画面を閉じるボタンを設置しましょう。イメージは下図のとおりです。

 例によって最初の画面では戻り先の画面が存在しないので、右上の × ボタンを無効化したくなります。モーダル遷移からの戻り先があるかどうかの判定には presentingViewController が使えます。これの中身が空っぽだった場合、戻り先がないということになります。諸々の実装は以下の通りになります。

まとめ
* モーダル遷移には present を使う
* モーダル遷移後の画面から元の画面に戻るには dismiss を使う
* モーダル遷移の戻り先の VC は presentingViewController に格納されている

4 TODO管理アプリの作成

 UITableView(テーブルビュー)はiOSアプリでしばしば見かけるUIコンポーネントのひとつです。たとえば標準のメールアプリや設定画面などに使われています。さて、ここからは簡単なTODOリスト管理アプリを作っていきましょう。TODOリストの表示にはテーブルビューが適していると思いますので、さっそく実装方法を見ていきましょう。

4.1 新しい画面の作成

 Storyboard に新たな ViewController を追加するほうほうもありますが、ここでは新しいストーリーボードと新しいViewControllerファイルを追加しましょう。TODOMainViewController.storyboardTODOMainViewController.swift を作成して以下のように画面を構成して、遷移できる状態まで持って行ってください。

 TODOMainViewController.swift のソースコードは以下のような状態になっていると良いかと思います。

これでTODO機能を実装する下地が整いました。

4.2 テーブルビューを組み込んでみる

 テーブルビューを組み込んでみましょう。TODOMainViewController.storyboardTableViewを追加して適切な constrain を設定しましょう。左・右・下方向への制約は constrain to margins のチェックボックスを外す必要がある点に注意してください。またViewController自体について Under Top Bars のチェックボックスをオフにしてください。TableViewのStyleは Grouped を選択します。

 つづいて TableView をコードに紐付ける作業を行ったあと、以下のようにコードを記述してください。tableView.dataSource はテーブルのデータを供給するための設定でここには、UITableViewDataSource プロトコルを満たす全てのインスタンスを指定することができます。また、tableView.delegate はテーブルのふるまいを指定するための設定でここには UITableViewDelegate プロトコルを満たす全てのインスタンスを指定することができます。

 
 ここで罠になるのが tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) です。ここに 0 を指定するとなぜかテーブルビュー上部に大きな余白ができます。なので、雑に対応したければ、0.1などの小さな値を指定するとよいでしょう。厳密にやりたければここに任意の数字を指定して、tableView自体を上方向にその任意の数字分ずらしてあげれば良いと思います(ホントか)。

4.3 テーブルビューのセルをスワイプできるアレ

テーブルビューのセルをスワイプできるアレを実現したいときには以下のようなコードを extension TODOMainViewController: UITableViewDelegate に追加します。

4.4 テーブルビューセルの表示を作る

 画面を構成する部品を作るには Storyboard ではなく、 xib というファイルを作ったほうが良いことが多いです(※あまりこだわりはないし、宗教的な問題だと思うので思想の強い方はブコメとかで暴れていただければ)。さっそくタスク名を表示するだけのセルを作ってみましょう。iOSHandsOn 直下に TODOMainViewTableCell.xibTODOMainViewTableCell.swift というファイルを作り、xibにViewを紐付けてください。このあとの作業はストーリーボードでやったときと一緒なので、同じノリでやって下図のような状態にしてみてください(だんだん説明が雑になってきた)。

4.5 「タスクを追加するテキストフィールド」を上部に適当に作る

もうだいたい絵を見れば実装する方法わかってきましたよね? TODOMainViewController.storyboard を以下の状態に変更して、コードベースとの紐付けもよしなにやってください(雑)。

4.6 TODOデータの入力・保存・削除の仕組みをひと通り実装する

 TODOデータを保存する仕組みとしてとりあえず、文字列のリスト([String], Array<String>) を使いましょう。注意して欲しいのは、この実装ではTODO管理機能のモーダルを dismiss するとすべてのデータが水の泡になるという点です。ちゃんとデータを保持するためのほうほうは後ほどご紹介しますが、基本的にはここで完成させたものに少し手をいれて、iOSデバイス内のファイルに保存するという形になりますので大筋の流れは同じです。というわけで、まず TODOMainViewController に以下のコードを追加しましょう。

 また、表示させるセルの数はタスクの数と同じにしたいので tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) を変更する必要があります。セルにはタスク名を表示させたいので tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) にも手を入れます

4.6.1 タスクの削除

 削除については対象のセルのインデックスと変数 tasks のインデックスが一致しているので、変数からタスクを削除してテーブルの再描画命令を呼び出してあげればよいので簡単です。削除はセルをスワイプすると出てくるボタンで実現するので、tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) に手を入れます。

シミュレータで実行すると、もうタスク削除機能がうまく動作していることがわかるのではないでしょうか?

4.6.2 タスクの追加

 テキストフィールドに文字を入力して、エンターを押した瞬間にタスクが追加されるという仕様を実装に落とし込みましょう。テキストフィールドの振る舞いは UITextFieldDelegate プロトコルを実装したクラスのインスタンスを指定することができます。

また普通のアプリだとキーボードで文字入力中にテキストフィールド以外の適当な部分をタップすると、キーボードを閉じて入力を中断することができます。その振る舞いを実装するには次のようなコードを追記すればよいと思います。

 まあこのままじゃテーブルビュー領域をタップした時にキーボードを閉じないんですけどね…。

4.6.3 ローカルへのデータ保存

あとで書く

4.7 テーブルビューのよくある実装

 テーブルビューを実装するときによく使われるのが「引っ張って更新」と「セルの逐次読み込み」なので、それぞれどのように実装されているのかを軽くおさらいしておきます。

4.7.1 引っ張って更新の実装

説明あとで書く

さりげなく非同期処理の導入をしてしまった

4.7.2 ページネーションの実装

あとで書く

5. CocoaPods の導入

 iOS開発にはしばしば依存ライブラリ管理ツールとして CocoaPods が利用されています。これは node における npm や Java における maven などのようなものです。CocoaPods は Ruby で作られていて、Mac には標準で Ruby の実行環境が入っていますが CocoaPods の最新バージョンは古い Mac に標準で入っている Ruby では動作しない可能性があります。

 そこでまず Ruby のバージョン管理ツール rbenv を導入しましょう。続いて Ruby のライブラリ管理ツールである bundler を導入し、それを利用して CocoaPods を導入します。よく分からないと思った方は、とりあえずおまじないだと思って以下のコマンドを実行していってみてください。余談ですがチーム開発においては、手元の環境で CocoaPods が正常に動くとしても新しいメンバーが開発に加わりやすいように rbenv を導入しておくと何かとスムーズで良いと思います。1

5.1 rbenv を使った Ruby 2.3.3 の導入

 rbenv とは、複数のRubyバージョンを切り替えることができるツールになります。マシンによっては OS X 標準で入っている Ruby のバージョンが古く、iOSアプリ開発によく使われる Ruby のライブラリ(gem)がうまく利用できない場合があるため、rbenv を利用して特定環境かにおけるつまづきどころをなくす意図があります。

5.2 bundler と CocoaPods の導入

プロジェクトルートに Gemfile を以下のように追加して rbenv exec bundle install を実行してください

続いて rbenv exec bundle exec pod initPodfile が生成されるので、いい感じに依存を指定して、 rbenv exec bundle exec pod install すれば良いと思います。

6. 追加課題

  • データ構造の定義(リスト/遅延リスト/スタック/キュー)
    • ユニットテストを書く
  • 電卓アプリの作成
    • その1: ボタンベースのiOS標準の電卓を目標に進める
    • その2: テキストフィールドベースでユーザーに数式を入力させ、計算結果を表示するアプリ
  • 本格的な TODO アプリを作成してください
    • タスクをどういった形で持てば拡張性を維持できる?
    • タスクがマイルストンやタグといったものに紐づくときにはどうすればよい?
    • データやロジックと UI がなるべくひっつかないようにするためにはどうしたらよい?
  • 株価/天気アプリの作成

時間が余ったら

  • URLSession を使った HTTP 通信
  • DispatchQueue を使った非同期処理
  • RxSwift を用いた非同期処理

などを試してみてください。


  1. rbenv を利用した開発フローに載せてくださった @koki_cheese さん、ありがとうございます 

UnicodeScalar とは?

docs: https://developer.apple.com/reference/swift/unicodescalar

The UnicodeScalar type, representing a single Unicode scalar value, is the element type of a string’s unicodeScalars collection.

UnicodeScalar は Unicodeスカラ値を表す値

UnicodeScalar から String への変換

ansible のソースコードを実行する

ansible のソースコードを直接実行したい

  • 頻繁にバージョンを切り替えたり、なんなり色々したい

誤り検出についての整理

誤り制御について頭の中を整理〜。誤り制御の方法は以下の二つ。

  1. 誤り検出 により再送
  2. 誤り訂正 により自己修復

参考資料

誤り検出: パリティチェック

  • 誤り検出用のパリティビットを付加してデータの整合性を検査する方法
    • 偶数パリティ: データの和が偶数になるようにパリティビットを付加する
    • 奇数パリティ: データの和が奇数になるようにパリティビットを付加する

例えば、7bitデータを送出する場合に偶数パリティを採用するとすれば以下のように、パリティビットを付加すればよい

[0,0,0,0,0,0,0] [0] ← パリティビット
[0,1,0,0,0,0,0] [1]
[0,1,1,0,1,0,0] [1]

VRC(Vertical Redundancy Check) – 垂直パリティ

  • キャラクタ単位でパリティチェックを行う

偶数パリティの場合8bit目をパリティビットとすれば次のような感じ

LRC(Longitudinal Redundancy Check) -水平パリティ

  • 伝送した文字列の終端に Block Check Character(BCC) と呼ばれる誤り検出のためのキャラクタを付加する
  • 伝送した各ビットの正当性を確認することによりキャラクタ単位ではなく文字列全体のなかに誤りがあることを検査することができる

偶数パリティの場合以下のような具合で

群係数チェック

  • 水平方向の和の下2桁を取り、下位ビットのチェックビットとする

偶数パリティであれば以下のような具合

CRC(Cyclic Redundancy Check) – 巡回冗長検査

  • バースト誤りを検出できるのが特長的
  • 書いてる途中で飽きた(続き暇なときにまとめる)