try! Swift Tokyoに参加してみた(2日目)
try! Swift Tokyoの2日目です。
1日目のレポートはこちらをどうぞ。
では以下まとめです。
テスト可能なコードを書くということの2つの側面
- テスト可能なコードを書くのには、Testing OutputとTesting Inputの2つの側面から考える必要がある
- Testing Output
- Outputの検証。それが正しいかどうかテストを行って確認をする
- 大事なのは副作用をコードの境界におくこと
- Testing input
- Co-effectが問題
- Co-effectとは?
- 依存性の注入
- Co-effectがある場合のテストが難しい
- 一つのアプローチとして、グローバルへのアクセスを必ず一つのstructを通して行うことで、環境を制御するやり方
struct Environment {
let apiService : ServiceProtocol
let cookieStrorage: HTTPCookieStorageProtocol
let currentUser: User?
let dateType: DateProtocol.Type
let language: Language
let mainBundle: BundleProtocl
let reachability: SignalProducer<Reachability, NoError>
let Scheduler: DateSchedulerProtocol
let userDefaults: UserDefaultsProtocols
...etc
}
-
- だいたい24個とかそれぐらいある。
- たくさんあるけど、環境が制御できるのでやったほうが良い
誰もが知りたいSequenceとCollectionのすべて
- SwiftのArrayは、Seaquesnce、Collection、BidirectionalCollectionというProtocolをベースに作られており、それによってSwiftの堅牢さが保たれている
- Sequence Protocol
- 要素のリスト
- 無限に追加できる
- 1回だけのiterate
- Iteratorと、IteratorProtoclに適合したプロパティをもつ
- IteratorProtocolとは、Elementとnextというメソッドをもつprotocol
- Sequenceにcountというメソッドを追加してみる
let number = users.filter({$0.isAdmin}).count // => fine 一度countしてから捨てるので、処理が二重で行われてオーバーヘッドがある
let number = users.count({$0.isAdmin}) // => great 本当はこうしたい
-
- 次のように拡張すると便利
extension Sequence {
func count(_ shouldCount: (Iterator.Element) -> Bool) -> Int{
var count = 0
for element in self {
if shouldount(element){
count += 1
}
}
return count
}
}
-
- もう一つEach Pairという拡張がオススメ
- [A, B, C, D]のリストがあったとき、AB, BC, CDというペアを作って比較したいときがある。
- これをつぎのようなメソッドを追加することで実現する
- もう一つEach Pairという拡張がオススメ
extension Sequence where Self.SubSequence: Sequence, Self.SubSequence.Iterator.Element == Self.Iterator.Element{
func eachPair() -> AnySequence<(Iterator.Element, Iterator.Element)> {
return AnySequence(zip(self, self.dropFirst()))
}
}
}
- Collectionというprotocol
- Sequenceベースのprotocol
- すべてのCollectionは有限
- 複数回Iterateできる
- index, startIndex、endIndex, subscriptionメソッド, indexメソッドを持つ
- APIErrorCollectionはfileprivateで自分で制御できない、自分でextensionで追加すると便利
- BidirectionalCollectionというprotocol
- Collectionベース
- beforeというメソッドがあり、前にも後ろにも戻れる
- 他にもMutableColelction、RandomeAccessCollection、RangeReplaceableColelctionなどがある
様々な場面でSwiftを使う
- Swiftのオープンソース化にともなって、ウェブサービス、API、マイクロサービス、それ以外に、デーモン、ユーティリティ、ツールも作れるようになった
- Swiftを使う理由としては、型安全、protocol、パフォーマンスの良さ等があるので、他言語と比較してSwiftを選択するというのもありだと考えた
- SwiftはCと同じLLVM基盤なので、C言語のライブラリをimportして読むことが出来る
- 事例1 – クローリングサービス
- ウェブクローリングサービスをSwiftで書き直し始めている
- もともとはpythonで書いていたが、他言語との将来性を考えた結果、Swiftにした
- クローラー、DB(mysql)、APIサーバーの3つの構成
- API経由でクローラーを制御するようにした
- APIは型があるgRPCプロトコルベースでおこなっている
- クローリングに必要なものは、すべて既存のC言語ライブラリのimportで実現できる
- 今のところ20日程度動かし続けているけど問題は起こっていない
- ウェブクローリングサービスをSwiftで書き直し始めている
- 事例2 – センサーを動かす
- Swift for ARMというライブラリを使ってラズパイを動かす
- I2C Bus経由で圧力センサーにアクセスして、slackにpostする
- I2CBUSへのアクセスは、includeしてfopen, ioctlシステムコールを使えばいける
- https://github.com/novi/i2c-swift にあげた
- 作り方
- すでにC言語のライブラリがあればそれを使えばいい
- 他の言語のライブラリも、Swiftへのポートをすることで使える
- Tokyo-Server Side Swift Meetupというのをやっているよ!
- バッチ処理、APIサービス、コマンドラインツール、社内ツール等に使ってみると良い
- スクリプト言語のようにつかうこともできる
- (質問)クローラーをswiftに切り替えて、パフォーマンスは具体的にどう上がったのか
- まだあまり測定できていないが、使っていたライブラリのオーバーヘッドがあったので、それを使わなくなくなったのは大きい
- (質問)NSURLSessionを使わずに、libcurlを使っている理由はなぜなのか
- Foundationはlinuxでほぼ実装はできているので、NSURLSessionも使えるが、NSURLSessionはHTTPの仕様に近い部分がさわれないので、libcurlにした
- (質問)ラズパイとかにFoundationをのっけてるわけではないよね?
- ラズパイはSwiftをサポートしているlinuxがのるので動く
- ArduinoはOSがないので動かない
VRの革新と新たなユーザー体験(LT)
- VRをどのようにゲーム、エンターテインメント以外に使えるかを検討している
- ティム・クックはARはとても魅力的だと話している
- Retail業界
- アリババはバーチャルショッピングに対応
- 誰でもニューヨークの店舗で買い物が出来るようにした
- アリババはバーチャルショッピングに対応
- 不動産業界
- ラトビアの不動産会社が、物件の360度動画を取って、それをVRで体験することが出来る
- 自動車業界
- Volvo、Audi、ジャガー、ホンダ、フォルクスワーゲン等が、VRによるバーチャルショールームとテストドライブを提供していたりする
- ニュースやメディア業界
- New York Times等が、360動画による放送をしたりしている
- スポーツ
- 東工大が開発したVRスケートボードがある
- MobileBusinessInsight.com、VR Hub Tokyo Meetupを見てください。
iOSにおけるDocument IndexingとApp Search
- App Searchとは?
- Spotlight、Safariにアプリ内コンテンツのサジェスト/検索ができるシステム
- App Searchはそんなに使っている人は少ないがすばらしい機能なので是非使って欲しい
- Core spotlight API
- 特徴
- インデックス化をする処理を書く
- これはローカルユーザーだけが見れる
- 大量のアイテムのインデックス化に向いている
- 作り方
- Core spotlightをimportしてインデックス化する
- 重要な属性
- expirationDate – expireが設定できる
- domainIdentifierを使えば複数のアイテムをまとめてインデックスすることも出来る
- iOS10からは、spotlightからアプリの検索ができるようになる
- 特徴
- webマークアップ
- 対応するウェブサイトがある場合に使うと良い
- ウェブサイトにディープリンクを追加
- markupを使ってリッチ情報を提供する
- ユニバーサルリンクか、スマートバナーを追加
- safariで検索した時に、アプリにリダイレクトされるようになる
- 詳しくは、gridNAのgithubを見てください
- (質問)imageをインデックスに載せるにはどうしたらよいのか。リモートからダウンロードするけど、保存する処理を書いたりする必要があるのか。
- URLでインデックスに追加することが出来る
- 自動でシステムが縮小してくれる
- (質問)Spotlightはどれぐらいのユーザーが使っているのか
- 自分たちのアプリでは、50%ぐらいの人がSpotlightから来ている
- (質問)データはindexに載せたかどうか確認する方法はあるのか
- インデックスされたかどうか確認する方法はある
スタートアップのSwift
- 2016年にアプリを開発した
- 女性の友情を育むアプリMVP
- PARSE + SWIFTで開発したら早く開発ができた
- いくつかのメディアからインタビューを受けた結果、最初の2週間で10万人が集まってしまった
- あまりにも多くのユーザーが一度に入ってしまったので、幾つかの問題が出てきた
- 1分あたりのリクエスト数がParseの制限を超えてしまった
- 一部のlocationのユーザーしか使えないようにして、なんとかリクエスト数を絞り込んだ
- リテラシーが低いユーザーがどんどん使い始めた想定していないバグが露見した
- slackチャンネルと繋いで、テストユーザーを募集してテストをしてもらった
- 再現できないクラッシュに対しては、Slackでコアユーザーに呼びかけて会議に出てもらったりした
- 1分あたりのリクエスト数がParseの制限を超えてしまった
- Swiftの機能を使って安全に書く方法を覚える必要があった
- 新しいことを覚えたら、コードをどんどん修正していかなければならない
- そのうちはじめに書いたコードは全部捨てないといけなくある
- また、Parseがサービスを閉じることになった
- マイグレーションをしなければならない
- Parseに依存していたので、APiクラスでラップしていなくて、新しく全部別のサービスに載せ替えなければならなかった
- swiftの言語のバージョンの変更に対応するのも大変だった
- ただし、swiftは開発し易い言語だし、改善しやすい言語であるので、結果、大事なことに集中できると思う
きちんと型付けされたメッセージ
- 可読性の重要さ
- 複雑性を制御する方法を学ぶ
- 複雑性は我々のロジックから来ているものと、我々がコントロール出来るものがある
- 具体的に可読性が高いコードを書くということは?
- 設計
- e-mailをユーザー名とドメインに分ける処理
- func split(email: String) -> (String, String)?
- 可読性をあげるレベル1 – コメントを書く
- コンパイルに影響を及ぼさないので、それを理解するかどうかはエンジニア次第
- リーダビリティレベル2
- typearliasや、structでSplitEmailというオブジェクトを定義する
- それぞれ代償がある
- 認識のコストは下がるが、オーバーヘッドや煩雑さが増える
- リーダビリティレベル3
- 返り値がoptionalであるということ
- 次のようなwrapperを使ってステートを管理する
enum SplitEmail {
case valid
case infalid
func init {
if hoge {
self = .valid
} else {
self = .invalid
}
}
}
-
-
- これによりSplitEmailには2つの状態があることが理解できる
- リーダビリティレベル4
- splitterを定義する
Split<String, EmailSplitter>
-
- 2つめの世界 – Delegation
- Appleの定義するdelegateはoptionalである
- これはライブラリのスタンダードな使い方
- しかし自分たちはそうしたくない
- Appleの仕様どおりではない場合、むしろ理解を妨げてしまうかもしれない
- 複雑性、監修、モデリングのコスト…etc
- 可読性を高める最短のルートはGodモード。つまり人の身になって考えるということ
忙しい人のためのApp Transport Security(LT)
- 安全なhttpsと安全でないhttpsがある
- 暗号化の方式によってはhttpsであっても安全でないと判定される
- 詳しくはAppleのドキュメントである、Information Property Listをみるとよい
- nscurlコマンドでもATS対応しているか確認をすることができる
- ATSの無効化はいくつは種類がある
- 全無効化
- ATSの特定のドメインのみ無効化
- iOS10から追加された項目
- AVFoundationの通信に関して無効化
- WebViewの通信に関して無効化
- ローカルホストに対する無効化
- WebView無効化を試した
- HTTPの通信、安全なHTTPSの通信は出来るが、安全でないHTTPSの通信ができない
- iOS10.2では、UIWebViewは通信できるという事実(WKWebViewはできない)
Swiftで堅牢なカラーシステムを構築する
- カラーシステムのアーキテクチャー設計を1から作った
- カラーシステム構築による便利さ
- カラー設定を一箇所でできる
- プラットフォームで一貫したカラーパレットを提供できる
- 適用が楽になる
- Appleのヒューマンインタフェースガイドラインは改訂を何度もしている
- Dark modeの追加等
- 色の選択はUXにかなり影響する
- デザインはInterface Builderで決定するか、プログラマティックにするかという問題
- Interface Builderはパワフルになってる
- .clrファイルを共有すれば、カラーパレットの共有も可能
- Interface Builderは共通のリファレンスがない
- .clrファイルを共有しても、すべてのintereface builderを設定するのは大変
- 複数のテーマでviewをカスタム化することができない
- プログラマティックにおこなう
- すべてのUIエレメントを一気に更新することができる
- 影響範囲は一つのファイルのみ
- カスタムカラーを作成できる
- Zeplinというカラーエキスパートツールはこの処理をシンプルに行える
extension UIColor {
struct Pallet {
static let hogehogeBlue = UIColor(...)
static let white = UIColor(...)
static let black = UIColor(...)
...
}
class var hogeText: UIColor {
return Pallet.black
}
}
- 色を反転した時に、強調の加減が同じになるかどうかなどをチェックしたほうが良い
- ユーザーのアクションによって全体の色を変更したい場合
- Notification Centerを使って複数のViewを一度に変える
- 次のように実装すると良い
protocol ColorUpdatable {
var theme: Theme{get set}
func updateColors(for theme: Theme)
}
protocol ColorChangeObserving {
func addDidChangeColorThemeObserver(notificationCenter: NotificationCenter)
func removeDidCngeColorThemeObserver(notificationCenter: NotificationCenter)
func didChangeColorTheme(notification: Notification)
}
private extension ColorThemeObserving {
func theme(from notification: Notification) -> Theme? {
...
return theme
}
func updateColors(from notification: Notification){
...
}
}
extension UIviewController: ColorThemeObserving {
@obj func didChangeColorTheme()...
}
- Inclusive Design
- 男性の8%、女性の0.4%が色覚障害を抱えている
- iOS10は色覚障害向けフィルターが設定できる
- Inclusive Colorというライブラリを使うと良い
- Inclusive Designの戦略
- モノクロではなく色を使うと強調することが出来る
- 大きなテキストは高いコントラスト比をもつのがよい
- 色ではなく、コントラスト比に注目するということ
- 昨年のWWDCのInclusive Design Sessionを参考にするとよい
サーバサイドSwiftの実例(LT)
- Vaporを使っている
- 開発は、linux固有のバグに悩まされたくなかったこともあり、xcodeで行っている
- vaportのテンプレートを用意した
- 実演したように、簡単にサーバーサイドSwiftは実現できる
モックオブジェクトをより便利にする
- Interaction Test
- protocolを定義することで、fake objectとreal objctの両方が使える
- モックオブジェクトにverfyメソッドを追加すると、複数のassertionを一気にできる
- default valueで#file、#lineを追加してロギングすることで、エラー時のコードの位置がわかりやすくなる
- 大事なものに反応して、大事でもないものは反応しないようにするというテクニック
- 大事でないものについては、マッチメソッドを外部から追加することで対応する
- Hamcrest Matchers – 様々な言語で採用されているマッチャー
- greaterThanOrEqualTo(2)、anything()というマッチャー
- fakeオブジェクトを作る理由
- リソースをつかうとき、グローバルへの影響が大きい時…
- qualitycoding.org/tryswift2017 にサンプルコード等があるので参考にしてください
チームの生産性を改善するために決断疲れを最小化する
- ソフトウェアエンジニアは常に意思決定をしているため、決断疲れが起きる
- 特に何もしないと個々で判断をするので、それぞれで決断疲れを起こしてしまう
- 意思決定の流れをシンプルにすることで問題を解決する必要がある
- (問題)Xcodeのどこにファイルの置き方
- ファイルを探すのに意外に時間がかかっているという事実
- 色んなアプローチがあるが、シンプルにapplication, components, UIに分けた
- Application
- アプリ自体に関する情報
- info.plistとかassetとか、launchscreenとか
- components
- 各コンポーネント毎にまとめた
- UI
- 各View毎、ViewController毎にまとめる
- ペアプロをやっている
- ほとんどがペアプロ
- 2人が同じコードを見れるように、一つのiMacに2つのディスプレイと2つのキーボードを追加
- Ping-Pongプログラミング – 順番にプログラミングしていく
- Driver-Navigatorプログラミング – 経験があるほうがナビゲートしていく
- (問題)プロパティ、メソッドの一覧性
- Swiftの
// MARK:-
を使う - これを自動入力するためにXCodeのテンプレートを作る
- protocol conformance – extensionでprotocolに適合するメソッドを分けるということ
- Swiftの
- Cross-Functionalペアリング
- エンジニア-デザインのペアリング等の複数の職種間でのペアリング
- ボタンをどう定義するかの話し合いとかを行う
- (問題)UIオブジェクトのデザイン
- 次のようにしてスタイリングを予め定義しておく
enum UIButtonStyle {
case primary, nagative
func applyTo(button: UIButton {)
case .primary:
...
return cutomButtom
case .negative
...
return cutomButtom
}
}
- Retrospective(振り返り)
- コアチームメンバーだけで行う
- 食事やお酒を出したりする
- ホワイトボードをKeep, Discuss, Improveに分けてタスクを分ける
- ファシリテーターは最後の締めくくりをポジティブになるようにする
- 決断は一人で行わせるのではなく、チームでブレストすることで行う
- (質問)ペアプロという二人で働くことにより損なわれる快適さについてどうか
- 確かに人との関係に集中しないといけない
- ただ、自分ではやっていないことを学ぶ機会ではある
- 自分の好みを犠牲にして、もっと良いものを見つけることができる。また、他の人に自分の好きなものを進めることが出来る
- (質問)リモートで働いているとペアプロが難しいがどうしたらよいか
- 人によってはスカイプを使ってペアプロをしたりしている
- 自分の経験から、リモートでもスカイプなどでペアプロすることで寂しさがなくなって良かった
- (質問) 奇数の開発者がいた場合どうやってペアプロしたら良いか
- DoITというツールを使う
- 一人の人がでるようなら、頻繁にペアを変えるようにする
- 一人の時には小さな仕事をやってもらうようにする
Client-Side Deep Learning(LT)
- Metal Performance Shaderに追加されたCNN (convolutional neural network) APIs – MPSCNNというのがある
- 今まではiPhoneからAPI経由でサーバーに送る必要があったが、今はこのMPSCNNを使うことでローカルで行うことが出来る
- 学習は強力な計算力を持つPCでやって、iOSに学習した結果をもたせて処理を行うという仕組み
- MPSCNNはいろんなファイルフォーマットに対応している
オープンソースコミュニティをスケールさせる
- オープンソースの流れ
- ソースコードを公開する
- ユーザーが使い始める
- ソリューションになる
- どんどんスケールする
- プロジェクトが大きくなるとどうなるか
- プロジェクトの勢いを保つということが難しくなる
- アクティブユーザーが増えるとユーザーサポートに時間がかかってしまう
- なんらかの方法で機能追加を検討するプロセスが必要
- 他のことに時間を大きく取られて、メインテナー自身が一番のユーザーでなくなってしまうという事実
- ユーザーとの情報のアンバランスがおきる
- ユーザーはissueを投げたら覚えているけどメインテナーは覚えていないという問題
- プロジェクトの勢いを保つということが難しくなる
- どうしたら良いか
- エラーメッセージの改善
- 余計なエラーメッセージを表示することをやめて、重要なメッセージは色をつけるようにした
- 余分なエラーメッセージを表示するオプションをつけた
- エラーが発生した時点で関連するものを提示する
- issueへのリンクを貼るとか
- issue botを使う
- ボットを使うことで、オープンイシューとプルリクが大きく減った
- issueテンプレートを導入した
- ほとんどの人が守ってないけど
- コマンド1つでユーザーの環境変数を収集して、バグ報告に役立てられるようにする
- 適切なタイミングでボットによってコマンドを提示するようにする
- 似たようなissueが沢山上がるような場合はドキュメントを充実させる必要がある
- 適切な箇所にリンクを貼る必要がある
- これも関連するissueが投げられたときは、botが自動でリンクを提示するようにする
- ボットを使うことでissueの一貫性を保つ
- しばらく投稿がないとissueを自動でクローズする
- これによってissueのライフサイクルを保つことができる
- Dangerというサービスをつかうことで、プルリクされたら自動テストをまわして、テストに失敗したらユーザーにテストを改善するように通知するようにする
- コントリビューターを増やす
- コントリビューターを増やすためにもコード規範を作る
- 全員が検証できるように透明性を保つ
- コードベースのシンプルさを保つ
- いつでもフレンドリーに受け入れる体制にする
- ユーザーがソフトを拡大できるようにする
- DSLを利用できるようにすることでユーザー自身である程度制御できるようにする
- プラグインとして追加できるようにしても良い
- エラーメッセージの改善
- (質問)ライブラリでメンテナンスできていないものはあるか。それをどうするか
- 透明性を確保することで、誰かに引き継ぐ
Swiftでのエラーハンドリングとエラー耐性についての教訓
- ここではError型の話ではなく、コードの中で理想的なコードパスではないものすべてをエラーと呼ぶことにする
- エラーハンドリングにおけるinputについて話す
- 明示的入力 – 引数、self等
- 暗黙の入力 – 状態
- 状態
- グローバル変数とシングルトン
- 時間的状態
- コードを実行する順番とか
- コメントや、間違った順番でコンパイルできないようにするとか
- リアル世界の状態
- ファイルシステムやネットワーク、以前のコードの実行出力等
- これらの状態の変更は無計画にやるべきではない
- ハンドリング方法
- 特定の状態を表すインスタンスを作る
- init時に、特定の状態の型を入れるようにする
- optionalを使う時
- あまり大きなエラーではないとき
- 発生頻度がやや起こりがちなエラー
- Error型を使うとき
- 致命的な問題に使われるとき
- 発生率が低く、影響範囲が広いエラーに利用するのが適切
- 入出力の流れを意識してあらゆる状態に対処する
- (質問)fatalerrorを使うときはどういうときか?
- 確かに”!”を使うことはあるが、そういう箇所の確認はテストを回して確認することにしている
- また、なぜそれをやらなければならないかをちゃんと探る
ゲーム&ウオッチ
- 人は昔から時計でゲームをやってきた(任天堂のゲーム&ウォッチの写真を見せながら)
- SpriteKitでゲームを開発できるようになった
- C++のNESエミュレータでやってみた
- ウォッチでは低レイヤーの描画が使えない
- フレームごとにテクスチャーを生成して対処
- サウンドはAVFoundationが使えるようになったのでそれで行った
- ただしシミュレータでは動作しなかった
- 多くのセンサーが利用できそう
- タッチイベントが、Touch Up取得できないという事実
- LongPressイベントの時間をめちゃくちゃ短くして対応
- Watchはマルチタッチイベントに対応していないため、デジタルクラウンでBダッシュを実現
- Carthageに対応したので、誰でも簡単にApple Watchアプリに組み込むことができるよ!!
なぜ登るのか
- オススメする理由1
- チームスポーツはメンバーのレベルがみんな同じでないと面白くない
- ボルダリングは違う
- 同じ壁で複数のレベルが楽しめる
- オススメする理由2
- 登るのには色んなアプローチがある
- 体のやわらかさでいくか、筋肉量にものをいわせていくか、ジャンプ力でいくか、バランス感覚でいくか…など
- これらの要素をミックスさせて、自分だけの解決法を考えられる
- 登るのには色んなアプローチがある
- オススメする理由3
- 一人でできるスポーツ
- でもグループで競争でやっても面白い
- これ、Swiftと同じではないか?
- みんなボルダリングをやるといいよ
- (質問)東京周りで、今この時期はどこにボルダリングがオススメか。
- インドアのボルダリングが良い
- 新宿のレックスジムにはフローティングルーフがあり、これがとても面白い