読者です 読者をやめる 読者になる 読者になる

My Favorite Things - Coding or die.

とある技術者の経験記録、的な。

try! Swift 2017 Tokyo に参加してきた - 2日目:午前

さて2日目です。
https://www.tryswift.co/tokyo/jp

1日目と同様にその場でのメモと感想を書いていきますが、ちょっとボリュームが大きいので午前・午後の2つに記事を分割したいと思います。(個人的に見返しやすくもあるので)

テスト可能なコードを書くということの2つの側面

メモ

  • 冒頭
    • テストするためには2つの要素が必要
    • Haskellのような関数型コードを例に
    • 我々の成果をOSS化した
  • なぜテストをするか?
    • テストできるコードだけテストする
    • 関数型で書くのは良い
    • テストをドキュメント化する
    • エッジケースも十分に考慮する
  • compute(file:) -> Int
    • 単純に見えてもいろんな値を参照している
      • Bundle
      • String(contentsOfFile:)
    • 副作用がある
      • Consoleへ出力
    • なぜテストしづらいか?
      • ハードドライブという外部の状態に依存する
      • コンソールへの出力を確認する必要がある(外部に影響を与えていないか)
    • 2つの世界
      • インプット
      • アウトプット
  • 副作用
    • OUTPUT
      • 我々は慣れているので考えやすい
      • 影響がないか確認する必要がある(それ単体ではなく連鎖的に反応していないか)
      • 副作用のあるコードを境界部分にのみ持ってくる
      • 戻り値をIntの代わりに(Int, String)にしてコンソール出力を無くす
    • INPUT
      • Co-effects - 共作用
        • INPUTは共作用のテスト
        • 他のグローバルな関数を参照している
        • Co-effects = アウトプットを出すための前提条件
        • 言い換えると、それがないと結果を出せないようなもの
      • やりかた
        • すべてをstructに入れ込む
          • apiService: ServiceProtocol
          • cookieStorage: HTTPCookieStorageProtocol
          • currentUser: User?
          • dateType: DateProtocol.Type
          • language: Language
          • mainBundle: BundleProtocol
          • reachability: SignalProducer<Reachability, NoError>
          • scheduler:
          • userDefaults: UserDefaultsProtocol
        • 日付を取るのはCo-effectsのわかり易い例
          • 日付を参照すると副作用がある
        • バンドルも当然Co-effects
        • 通信状態も
        • Rxにおけるスケジューラも
        • 当然UserDefaultsも
      • リファクタリング
        • バンドル
          • 成功 or 失敗
        • 全部引数に持ってくる
        • 宣言が複雑にみえるけれど副作用が宣言部分に集約されている
  • 結論
    • なぜテストが難しいかというとCo-effectsがある
    • グローバルに自由に参照しないように、strutctを用意して、それを利用するようにする
      • そこに副作用をすべて集約する
  • FAQ
    • 1
      • バグレポート = 失敗したテスト
    • テスト駆動をよくやっている?
    • 2
      • ReaderMonad / StateMonad
        • あまり意識していない
      • プロパティベースのテストを行うのか
        • やりたいとは思っているが、まだ行っていない

断片メモコード

ファイル名を受け取って、その行数を返す関数

file: String = "number.txt" -> compute(file:) -> Int (12345)

感想

どうやって品質の良いテストコードを書くか、という話。

テストするための2つの要素「インプット」「アウトプット」について非常に詳しい説明がありました。もっとも一般的な入出力の話ではなく、副作用についての話でした。

発表者も言われてましたが「アウトプット」、例えばDatabaseやファイルを書き換えたり、iOSであればUserDefaultsを書き換えたり、という副作用は考え慣れているので簡単だけれど、「インプット」についてはあまり考えなかったりする、とのこと。

その前提条件となる「インプット」のことを「Co-effects(共作用)」というらしいが、それは最近の研究で話に上がってきたものらしく、あまり広まっている用語ではない模様。具体的には「現在時刻」などがわかり易い例で、アウトプットを出すための前提条件となるものが「共作用」と呼ばれるようです。グローバル関数もその例に当たる、と。

ではどうやって解決するかというと、インターフェースをプロトコルとして宣言して、そのプロトコルを引数として全部渡せば良い、とのこと。

たしかに関数の宣言は複雑になってしまうけれど、副作用がその一箇所に集約されるので、結果的に品質が安定するとのこと。(副作用のあるコードを境界部分のみに集約する)

そしてテストではなく実際の値を返すプロトコルの実装群は、グローバルなStructにまとめておくと良いとのこと。

などなど、私にとっては得るものが大変多いセッションでした。

ちなみに質問でReaderMonadを使わないのか、プロパティベースのテストは行っているのか、といった、非常にレベルの高い質問がされていました。

誰もが知りたいSequenceとCollectionのすべて

メモ

  • 構造
    • BidirectionalCollection
    • Collection
    • Sequence
  • Sequence
    • 要件
      • リスト
      • 有限 or 無限
      • 1回だけIterateできる
    • Iteratorを生成する
    • LinkedList
      • enumを使って実装すると素直
      • indirectで自分自身で使うことを宣言
      • iteratorは状態(現在の位置)を持つ
      • currentで現在の位置(要素)を保持
    • Count
      • users.filter({test}).count // => 配列を生成しちゃうので少しコストある
      • users.count({test}) // => great
      • Rubyとかだとお馴染み?
    • Each Pair
      • zip(list, list.dropFirst) が一般的なアプローチ
        • これも良いけれど・・・
      • where Self.SubSequence: Sequence
      • Elementを==で型制限
        • ごちゃごちゃするけれど仕方ない
  • Collection
    • 要件
      • Sequenceを引き継ぐ
      • 有限
      • 何回もIterateできる(Sequenceは1回)
    • 実装
      • Indexを持つ
      • start / end
    • APIErrorCollection
      • privateだとアクセス出来ないので?
  • BidirectionalCollection
    • 要件
      • Collectionを引き継ぐ
      • 前にも戻れる
    • 実装
      • afterに加えてbeforeが追加される
    • RandomeAccessCollection
      • その値に直接
    • RangeReplaceableCollection
      • 中間値を変換?
    • Appleのドキュメントを見ればいろいろ分かる
  • FAQ
    • 他の言語ではSequenceはIteratorではなくElement、何のメリット?
      • IteratorはSwift内部で使う
      • 独自の走査方法を作る時にCollectionではなくIteratorを実装する

感想

Swift の Iterator、Sequence、Collection、BidirectionalCollection について、どんな感じになっているのか詳しく見ていこう、という話。

微妙に理解が怪しい部分だったのだけれど、順を追った分かりやすい説明だったので、頭が整理されたような気がします。

個人的には xs.filter(predicate).count ではなく、 xs.count(predicate)という書き方が勉強になりました。もしかしたら Advanced Swift あたりで一度見ていたかもしれないけれど。

あとは EachPair の実装方法も面白かったです。

簡単にまとめると、以下のような感じでしょうか。 - Iterator:任意の値列を返す - Sequence:1回だけIterateできる - Collection:複数回操作できる - BidirectionalCollection:前にも戻れる

もう一度自分でコードを書いて、理解を深めておきたいところです。

様々な場面でSwiftを使う

メモ

  • Apple以外のプラットフォームの話
  • サーバサイドSwift / ライブラリ
  • OSS
    • 2015年に公開(Foundtion、SwiftPM)
    • x86_64のLinux環境サポート
    • Oct 2016: サーバサイドAPIを統一するための指針
  • Apple以外のプラットフォーム
    • Web
    • マイクロサービス
    • Deamon、Utility、Tool
  • なぜ?
    • 型付けされている
  • サーバサイドSwift
    • バックエンドのクローラ
      • Pythonで作ってたけど設計とか見直したい
      • リスクはあるけれどSwiftを使ってみることに
    • クローラ
      • ヘッダなどの余計な情報を落として、本文のみを保存する
      • MySQL/S3をデータストレージとして
      • APIを使って貯めたデータを取得するように → 社内で使えるように
      • gRPC protocol
        • Protocol Buffers
        • SwiftやGoでも使える
    • ライブラリ
      • HTTP(S) Client
        • libcurl(C based)
          • curlコマンドベース
      • HTMLパーサ
        • libxml2(C based)
        • XPathとかは簡単なものだけだったので正規表現
      • MySQL
        • mysql connector(C based)
      • S3 Client
      • 日本語変換(SJISEUC_JP) → クロール対象が日本語ページが多い
        • NKF(C based)
      • gRPC
        • 最近はOSSとして開発されている
    • SwiftとC言語
      • LLVMベースなので簡単に呼び出せる
      • C言語のライブラリがそのまま使える
    • 実際のプロジェクトについて
      • クローラ:10,000 lines
      • ライブラリ:9,000 lines
      • macOSで動かしてる
        • 20日動いてる
      • Linux環境も
        • 1週間くらいは動いてる
      • AWSへ移行する予定
  • 個人プロジェクト
    • ボードコンピュータで動かす
    • ラズパイとかで動かす
    • Swiftライブラリがそのまま動くように
    • ARM版のSwiftがある
    • ボード上でSwiftサーバを動かす
    • やり方
  • ポイント
    • C言語のライブラリをラップする
      • それを使ってSwiftっぽく
      • ex) libcurl, libxml, mysqlconnector
    • 他言語を呼び出す?
      • その実装が生のC言語APIを呼び出していればそれを
    • テスト済みのライブラリがあればテスト部分も減る
  • SPM
    • Node.jsのnpmに似ている
  • シェバンで使うと簡単なスクリプトとしても使える
  • まとめ
    • 安全で効率的
    • OSS
    • Cベースのライブラリ使える
  • FAQ
    • Pythonからパフォーマンスはあがったか?(C言語のスピードが大きい?)
      • これから測定予定
      • まとまったら記事として公開したいと思っている
    • NSURLSessionやNSURLConnectionは不十分なのか?(Foundationは不十分?)
      • Foundationの実装状況は公開されている
        • 殆ど実装されているので実プロダクト的には問題ないレベル
        • HTTPのローレベルを操作するためlibcurlを使った
    • ArdinoはLinuxではないのでSwiftは動かない

感想

Swiftをサーバサイド(MacLinux)やボードコンピュータ(ラズパイ)とかで動かす、という話。既存のPythonAPIの設計がイケてなくてパフォーマンスも改善したかったので、せっかくだからSwiftを使ってみた、とのこと。

他のセッションでもあったけれど、C言語のABIが安定していて、かつSwiftがLLVMを利用しているので、既存のC言語の資産を使えるとのこと。

実際にSwiftで開発したことで、いろいろと面倒なこともあったのではないかと思うけれど、そのあたりの話があまりなかったので、実際のところはどうなのだろうとは思います。

ただ実績として20日間動いているとのことなので、サーバサイドSwiftはもう実プロダクトとして利用できるレベルには達したのかな、と感じます。

⚡️🎤 VRの革新と新たなユーザー体験

メモ

  • VRをゲーム以外でも
  • 360°のVRが重要になってきている
  • ティム・クックはARは重要だと発言している
  • 事例
    • ショッピング(Alibaba)
      • VRショッピング(自宅にいながら有名なショップで買物)
    • 不動産
      • 360°の写真を取って、それを見られるようにする
    • 自動車
      • デザインやアクセサリの変更
      • 美しい形式でのドライブ
      • 店舗のスペース削減にも
    • ニュース・メディア
      • 360°の放送
      • 自らが実際に居るような体験ができる
      • NewYork Times
    • 運動
      • スケートのイメージトレーニング
      • 実際に効果があるとのこと

感想

VR(AR)が実際にどのような箇所で効果的に利用されているか、という実績紹介。

個人的にあまり強い興味は持っていない分野だったのだけれど、かなり身近になってきたし、うまく活用すれば良いサービスが提供できるのだと感じた。

iOSにおけるDocument IndexingとApp Search

メモ

  • 検索をアプリ内で
  • App Search → ほとんど使っている人がいない
  • App Search API
    • Core Spotlight API
      • private
      • 大量のデータの時に便利
      • 重要な属性
        • expirationDate(有効期限)
        • attributeSet(SpotLightで可視化)
        • uniqueIdentifier(まとめて管理が便利?)
      • プラクティス
        • 非同期でバッチ処理をする
        • コンテンツを常に最新にしていく
    • Search Continuation(from iOS10)
      • アイテムを探す時に便利?
      • 注意
        • beginBatch()はバックグラウンドスレッドで動く
        • beginBatchするまえにendBatchしないとクラッシュ
      • どうやってテストするか?
        • 開発者設定 > すべてのアイテム?
      • シミュレータでは機能しない(ので実機でテスト)
    • NSUserActivity
      • 見えるものだけが対象になる
    • SearchebleItem
    • Universal Link
      • Webページからアプリへ直接遷移
  • 統合する理由
    • コンテンツへのアクセスが速くなる

感想

iOS9からのAppSearchについて、実装方法とかの話。

個人的にはAppSearchはそれほど良い使われ方をしているように感じないので、まぁ効果的だと感じる人もいるのだなぁ、というくらいの感想でした。

ただ実践でのノウハウが共有されたのはありがたいと感じました。

スタートアップのSwift

メモ

  • Swiftの現場での経験(初心者からはじめて1年間)
  • スタートアップ
    • スタートアップは0から新しいものを作る、それもすぐに
    • 製品の様々な面を自分たちでカバーする必要がある
  • どうやって作るか?
    • Parse + Swift = Fast (To write)
    • CocoaPodsでいろんなOSSがあったので活用できた
  • リリース
    • 最初の2週間で10万人
    • 予想外の人数
  • 問題
    • 秒間リクエスト数を超えてしまった
      • リクエストを絞込
      • お金を払っても解決しなかった
      • 地域も絞る
    • Slackとテストフライトでフィードバックを得られるように
    • CEOがSlaskでクラッシュ解決を募集(再現している人)
      • 一人来てくれたのでiPhoneを繋いで調査した
      • ユーザ名が入っていなかった
    • 他に問題
      • 強制アンラップを使用しないように
      • switchのデフォルトシンタックス
      • 最初だから知らなかった
    • Extension
      • コードベースの改善に便利だった(丸くする、とか)
    • 最初に書いたコードは捨てる時期にもなってきている
    • Parseが閉じるアナウンス!
      • バックエンドをマイグレーションする必要がある
      • サービス性質上、全員分のデータを一気に移行する必要があった
      • ダウンタイムの間でデータ移行
      • 問題
    • Swiftのバージョン移行は大変だった
    • Swiftの学習カーブは低い(短期間で習得しやすい)

感想

スタートアップで Swift + Parse を利用した結果、サービスがどのように成長し、どのようなトラブルに巻き込まれかというリアルな話。

使いたいからではなく早く作るために Swift + Parse を採用し、その結果としてスケールしない問題に出くわしたり、それを解消するためにどのような対策を取ったのか。クラッシュの原因が分からない問題に対して、Slackでユーザを募集したり、そこからのフィードバックをいかに反映したか。そして Parse が閉じる事になった時に、どのようにデータ移行を行ったか。

などなど、非常に貴重な話を聞くことが出来ました。

外部サービスに依存した場合のリスクという話は以前からも聞いていましたが、今回の発表を聞いてそういったことが実際にあるのだという実感がわきました。

またクラッシュを解消するためにSlackでユーザを募集するというのは衝撃を受けました。日本でそんなことはまず出来ないと感じたからです。

try! Swift でスタートアップのリアルな話が聞けるとは思わなかったので、思わぬ誤算という感じのセッションでした。