try! Swift 記録 2日目

try! Swift に参加してきました!

www.tryswift.co

ので、メモっていたことを書きます。 本当に雑なので、間違っているかもしれません。 2日目の分です。 1日目の分はこちら

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

なぜテスト

  • バグを防げる
  • テストができるコードだけ書くようにしている
  • 関数型のように
  • テストをドキュメント化している
  • それに向かって振る舞うように実装している
  • ファイルを読み出してその内容を計算してコンソールに出力して結果を返すという場合
    • 入力と出力を分ける
    • 入力は引数以外にも、関数の内部から直接呼び出しているものも隠された入力と言える
    • 明示的なインプットと暗黙的なインプットがあるということ

アウトプットに関して

  • 難しいのは side-effect があるから
  • でも sideeffect はコード上で判断できるようにしておくべき
  • どうやるか
    • print の引数を直接返すようにする

インプットに関して

  • 難しいのは co-effects があるから
    • これは新しい分野なので詳しい説明はない
  • 何が操作されるのかが見えないといけない
  • グローバルに扱うものに関しては singleton などを直接使わずに間に struct を挟む
    • グローバルに定義しないでグローバル用の一つの struct を用意して入れてしまう
    • currentUser とか date も cookieStrage も langage も apiClient も scheduler も userDefaults も
    • 基本的に全て protocol を間に挟んで行う
    • こうして挟んだ protocol を使って引数を挟むようにする。

Q.どのタイミングでテスト書いているか
テスト駆動開発でやっている

Q.プロパティベースのテストは行なっているか
行なっていないが行いたい

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

Sequence

  • エレメントのリスト
  • 有限である
  • 一度だけしかイテレートできない
  • Sequence Protocol は map や fileter が適宜されている protocol
    • これに沿って何かを実装するれば、 map や filter が使えるようになる
  • 型に条件を持たせるには where を使う
    • 副作用として複雑になってしまう

BidirectionalCollection

  • 前にも後ろ戻りもできる Collection
  • 他にも Collection が3つあるのでそこら辺見ると良さそう

Iterator の何が嬉しいか

  • 基本的は内部で使っているだけなのであまり使うことがない
  • 自分で何か順番で見ていく処理を追加したい時に Iterator を見るというようにすることができる。

iOSにおけるDocument IndexingとApp Search

App Search を使う

  • Core SpotLigth API
  • spotlight で検索したときにからアプリ内のデータを返してタップしたらアプリを開く
  • 実装
    • import CoreSpotlight
    • CSSearchableItemIndexDelegate protocol を実装する
    • 情報を最新にしておく
  • beginBatch is background thread
  • apple のドキュメントはない
  • endBatch を呼んでから beginBatch する
  • スレッド処理は手動でやらないとならない
  • developer setting の identifer で Extension を取り出せる

きちんと型付けされたメッセージ

  • 可読性の高いコードを書く
  • メソッドのインターフェースに着目する
  • 引数にどういう処理がされて返り値が返ってくるのか
  • 返り値が何のための値なのか

方法

返り値をどう分かりやすくするか

  • コメント
  • 返り値の型を typealias で作る
    • この型がどういう定義なのかを確認しなければいけなくなる
    • 一度しか使わないのであればデメリットの方が大きい

返り値が optional の場合にはどうするか

  • typealias
  • enum で valid, invalid を作る
  • これらをやると結局複雑になっているので必ず良いとは言えない

weak の値をつかう場合

  • weak はいかを表す
    • 保持されていない
    • 要件ではない
    • 変更可能
  • これは本当に必要なものなのか
  • こういったことは慣習を元に用意されている
  • これを防ごうとしてラッパーを用意するとまた複雑になってくる
    • 慣習であるならば隠蔽すると逆にわかりづらくなってくるのでは

結論

  • 最適解というのはない
  • 他者がどういうところを疑問に持つのかを考える
  • メンバーの価値観を把握してコードを書くことが大事

モックオブジェクトをより便利にする

  • メソッドが呼ばれたことを判断するフラグは Bool じゃなくて Int にすれば回数まで担保できる
  • フラジャイルテストは避ける
    • 壊れて欲しくないときにも壊れてしまうようなテスト
  • エラーメッセージを良いものにしよう
    • 引数に file や line を メッセージ をメソッドに渡す
  • Hamcrest Matchers おすすめ

Client-Side Deep Learning

  • MPSCNN
  • metal は早いのでリアルタイムの画像解析が行える
  • ニューラルネットワークをつかうことでクライアントサイドでディープラーニングができるようになった
  • リアルタイムでサーバサイドと通信すると負荷がすごくなるのでクライアントサイドでやれると嬉しい

try! Swift 記録 1日目

try! Swift に参加してきました!

www.tryswift.co

ので、メモっていたことを書きます。 本当に雑なので、間違っているかもしれません。 1日目の分です。2日目の分はこちら

Swift開発者が知りたかったけど聞きにくい機械学習のすべて

  • 機械学習ではデータを集めて関数を定義する
  • 誤差をエラーという
  • 身n長を平均する関数の場合
    • 男女で分ければ誤差が違うので精度が上がる
    • 複数の要素の係数を定義して足し合わせていくと正確度が上がっていく
    • 線形回帰
  • アプリ開発者に影響があるか
    • ある
  • 自分でも理解が深くなくてもできるか
    • できる
    • 機械学習は言語でいうとまだ ver1.0 みたいなもの
    • 普及してきていて勉強できる機会が増えている
      • グーグルの TensorFlow とか
  • 機械学習はSwiftでできるのか
    • 以下の2STEPでできる
      • モデル定義して訓練
      • デプロイして動かす
    • モデルにはパイソンとテンサーフローを使うべき
    • テンサーフローのライブラリをSwiftでバンドルすることができる
    • Swiftでモデルのポーティングができる
  • どういう時に使える
    • AVFoundationやCoreImageを使うようなもの
  • UIデザインを使うようなものに似ている
    • 確実ではないものに対して
  • 機械学習のモデルのインプットはUIのような曖昧なものに向いている(例えば入力フォームとか)
    • iOS は人間の体のような曖昧なもので成り立っている
    • そういった曖昧なインプットを扱っているので機械学習が向いている
  • どこから始められる

Q.例で挙げたメガネの作り方は
サーバで演算して結果をモバイルに送ってレンダリングしている
ニューラルネットワークはすごいので今後はサーバ側でやっていることもモバイルでやりたい

Swift on Android

  • SilverSwift の話ではない
  • C言語は全ての橋渡しとなる言語
    • Swift はそのC言語を触るのが得意
  • UIKit は Android に移植されることはない
  • Swift が Android で動くことを考えるので UIKit とかのライブラリとかは関係ない
  • 本当の AndroidSDK(java)で書かなければならなかった
    • NDK がでて来てC, C++でかけるようになった
    • C,C++ は Swift は動かない
  • NDK はお粗末
  • Lua
  • Blurr SDK
    • CMake でプリロードしてそれをシッピングする
  • twitter は @codingswift

Q.メモリ管理の違い
SDKではNDKを使っている
AndroidJavaメモリ管理を使っている
NDK ではあらゆるメモリを使えるようにしている

SwiftのPointy Bits

  • Swiftを安全にしているもの
  • 安全とは
    • クラッシュするコードもかけてしまう
    • 予期せぬ振る舞いの時だけクラッシュする
    • 予期せぬ振る舞いを防ぐという意味で安全
  • メモリアドレスはbiteが最小単位
  • バッファポインタはメモリアドレスのrangeを格納する

アプリを新次元に導く3D Touch

メリット

  • 3DTouch はやりたいことに早くアクセスできる
  • 3DTouch 特集という取り上げられ方もある
  • 3DTouch API は使いやすいよ

実装

  • ホーム画面で3D Touchした時には static と dynamic アクションがある
  • static は Info.plist で定義する
    • 必須項目が二つあって二の項目はオプション
  • dynamic はコード上で動的に定義する

Peek & Pop

  • プレビューするやつ
  • ユーザが3DTouch使えるか確認する必要がある
    • viewDidLoad とかで forceTouchCapability == .availabe をチェックする
    • 3D Touch 使えない場合はロングプレスで代替機能を実装できる
  • updateもする(表示するデータの更新?)
  • デリゲートメソッドは二つだけ
  • UIPreviewInteraction で圧力の強さも0~1の範囲でアクセスできる

Q.テストできるのか?
まだ試していない
Q.後方互換としてロングプレスを実装することで一応対応できるといったがラッパークラスは用意しているか OSS があるのでそれが使えそう

Pixcels プロセスと情熱

  • 高価なものを安価に利用できるようにするためにアプリを作った
  • 大事なこと
    • 課題に注目するが大事
    • 技術ではなくて人のモチベーション
    • 自分のコネを生かす

毎日リアクティブ

どのような時に使うのか

  • 複雑なコードがある場合に一定までは複雑になるがそれを越えると簡素化される
  • 非同期なイベント(UIや通信)に有効

問題

  • call stack が使えないのでデバッグがしづらい
    • 一定のストリームごとでデバッギングする
  • KVO がパワルのなのでどこでも使いたくなる
    • オーバーヘッドがでかく、パフォーマンスが悪くなる。
    • バインディングを使っていくことで解決できる
  • サイドエフェクトに注意深くinjectしなければならない
  • 混沌を避ける
    • 必要ないところでは使おうとしない
    • state はイミュターブルにすると良い
    • 使いどころは選ぼう
    • シンプルにできるところで使おう
    • Appleフレームワークに沿っていないので厳しくなるときもあるので

Q.初見の人がいる場での導入できるかと学習コストはどうか 学ぶのは楽ではないがそれほど問題ないだろう
毎日触れていれば大丈夫
リアクティブを試すコミュニティも増えている

クックパッドアプリのテストを味合う

  • 全コードは10万行くらい
  • Swift コードは 30%
  • リリースサイクルは2週間から最近は1ヶ月
  • 狩野モデルにいて最低限必要なこと(must be quality)は画面遷移でクラッシュしないこと
  • なぜUIテストを実装し続けてきたか
    • クラッシュを防げる
  • tarnip(ターニップ)でシナリオを書いている
  • 設定とかでパスが必要な場合はバージョンに依存してしまうのでハードコーディングしない
  • UITest は image diff でやると良い
  • API の request 回数のテストも書いている
  • UIの8割のテストをカバーしている
    • そのおかげで早く安定して開発を行えている
  • Appium を使っている

Q.Appium を選択した理由
アーキテクチャの関係

独自のツールを構築する

  • ネイティブはコンパイル遅いからリアクトネイティブやってみた
  • 抽象化してみようとやってみたところドメイン思考にいきつく
    • 再利用できないという結論に
  • プロジェクトを分けて差分だけビルドしようと思ったけど長期的に見ると複雑になって大変そう
  • API志向にネイティブ開発は向いていないがリアクトネイティブは向いている

Q.どんなアプリでもリアクトネイティブにできるか - 高度なアニメーションをしたいときにはリアクトネイティブが適切かというとどうだろうか - やろうと思えばできるだろうが

Q.???
- ポッドで入れた - ライブラリーのような扱いで入れた - メインのアプリケーション起動ではアプリとして扱うようにした

instantiateInitialViewController() が nil になる

同じことで何度もはまっている気がするのでメモ。

SampleViewController の Storyboard と Class を追加して、Storyboard から以下のようにインスタンスを作ろうとすると、nil になってしまう。

let storyboard = UIStoryboard(name: "SampleViewController", bundle: nil)
let viewController = storyboard.instantiateInitialViewController() // => nil

typo もないし、何でだ〜〜〜!となっていたけど、

Storyboard の Show the Attributes Inspector で Is Initial View Controller がチェックされていなかった、、、

Swift で循環参照するケース

循環参照という言葉を最近よく耳にしていて、なんとなく相互参照の結果メモリリークしてしまうというのは分かっていたのですが、じゃあどうしたら起きてしまうのかというところがあやふやだったので調べてみました。

循環参照

インスタンスは参照カウンタという値を持っており、変数に代入されたりして強参照されることで参照カウンタが 1 増えます。この参照カウンタが 0 になるとインスタンスは解放されます。

class Sample {}

// Sample クラスのインスタンスが生成されるが参照カウンタが 0 のままなのですぐに解放される
Sample()

// Sample クラスのインスタンスが生成されて sample に代入されているので参照カウンタが 1 となり解放されない
var sample: Sample? = Sample()

// nil を代入すると参照がなくなるのでインスタンスの参照カウンタが 0 になり解放される
sample = nil

循環参照とは、参照カウンタが 0 になり得ない状態をつくってしまう参照のことです。
これにより、インスタンスは解放されることなく残り続けてしまい、メモリリークとなってしまいます。

具体的なケースをいくつか書いてみます。 ( Swift 3.0 )
図では、赤枠が変数、青枠がインスタンス、緑枠がクロージャを表しています。

ケース1

f:id:komaji504:20161218022711p:plain:w500

class Computer {
    var printer: Printer?
}

class Printer {
    var computer: Computer?
}

var computer: Computer? = Computer() // Computer の参照カウンタ1
var printer: Printer? = Printer() // Printer の参照カウンタ1

computer.printer = printer // Printer の参照カウンタ2
printer.computer = computer // Computer の参照カウンタ2

computer = nil // Computer の参照カウンタ1
printer = nil // Printer の参照カウンタ1

// 各インスタンスの参照カウンタは1なので解放されていない
// しかしプログラムから参照することはできない

インスタンスのプロパティで一方のインスタンスを強参照しているので、変数からの参照がなくなった後も各インスタンスは解放されません。
変数からインスンタンスへの参照がなくなったことによりプログラムからインスタンスを参照することができなくなってしまい、無駄なインスタンスが生じている状態になっています。

対処例

このケースはプロパティに対して弱参照を用いることで解決できます。

class Computer {
    var printer: Printer?
}

class Printer {
    // weak をつけることで弱参照となる
    weak var computer: Computer?
}

var computer: Computer? = Computer() // Computer の参照カウンタ 1
var printer: Printer? = Printer() // Printer の参照カウンタ 1

computer.printer = printer // Printer の参照カウンタ 2
printer.computer = computer // Computer の参照カウンタ 1
// 弱参照は参照カウンタを増やさない

computer = nil // Computer の参照カウンタ 0 , Printer の参照カウンタ 1
// computer が解放されることで computer.printer からの Printer への参照もなくなる

printer = nil // Printer の参照カウンタ 0

weak をつけてプロパティ定義をすると、そのプロパティからインスタンスへの参照は弱参照となります。
弱参照とは、参照カウンタを増やさずに参照する方法で、これにより循環参照を防ぐことができます。

delegate を実装するときに delegate プロパティに対して weak をよくつけるかと思いますが、それはこのケースのためです。

ケース2

f:id:komaji504:20161218022731p:plain:w200

class Printer {
    var printClosure: (() -> Void)
    
    init() {
        self.printClosure = {
            print("Printer name is", self)
        }
    }
    
    func run() {
        printClosure?()
    }
}

var printer: Printer? = Printer() // Printer の参照カウンタ 2 , クロージャの参照カウンタ 1

printer = nil // Printer の参照カウンタ 1

クロージャは、生成されたタイミングでクロージャ内で扱われているインスタンスへの参照をキャプチャします。つまり、クロージャが解放されるまで参照が保持されるので、強参照をしている場合はインスタンスの参照カウンタが 1 増えたままということになります。

そのため、上記のケースでは、 Printer のインスタンスが生成されたときに self ( = Printer ) を強参照するクロージャが生成されているので、 Printer のインスタンスの参照カウントが 1 になります。

そして、このクロージャをプロパティへ代入しているため、 Printer のインスタンスクロージャを強参照し、クロージャもまた Printer のインスタンスを強参照しているので循環参照となっています。

対処例

ケース1と同様にプロパティを弱参照とすることもできますが、クロージャがキャプチャする参照を弱参照へと変更する方法もあります。
その場合は以下のように [weak self] と記述します。

class Printer {
    var printClosure: (() -> Void)
    
    init() {
        // self の参照を弱参照にする
        self.printClosure = { [weak self] in
            print("Printer name is", self ?? "")
        }
    }
    
    func run() {
        printClosure?()
    }
}

var printer: Printer? = Printer() // Printer の参照カウンタ 1 , クロージャの参照カウンタ 1

printer = nil // Printer の参照カウンタ 0, クロージャの参照カウンタ 0

これにより循環参照を防ぐことができます。
しかし、弱参照とすることで参照先のインスタンスが解放されることを許容することになるので、クロージャ内ではオプショナル型として扱われます。上記の場合では、 self はオプショナル型となっています。そのため、オプショナルバインディング等でアンラップする必要があります。

unowned

ちなみに unowned というのもあり、これをつけることで参照カウンタを増やさずに参照することができます。
weak との違いは、 オプショナル型と暗黙的オプショナル型の違いのように、 参照先が解放されてしまっている ( nil になっている ) ときに実行時エラーとなるかどうかということのようです。

weak は実行時エラーになりませんが、 unowned は実行時エラーとなります。

ですが、 unowned の場合はオプショナルバインディング等でアンラップする必要がありません。

ケース3

f:id:komaji504:20161219002036p:plain:w500

class Computer {
    var printer: Printer?
    
    func printName() {
        printer?.run {
            print("Computer name is", self)
        }
    }
}

class Printer {
    var printClosure: (() -> Void)?
    
    func run(printClosure: @escaping () -> Void) {
        self.printClosure = printClosure
        run()
    }
    
    func run() {
        printClosure?()
    }
}

var computer: Computer? = Computer() // Computer の参照カウンタ 1
var printer: Printer? = Printer() // Printer の参照カウンタ 1

computer?.printer = printer // Printer の参照カウンタ 2
computer?.printName() // Computer の参照カウンタ 2 , クロージャの参照カウンタ 1

computer = nil // Computer の参照カウンタ 1
printer = nil // Printer の参照カウンタ 1

このケースでは、 func printName() 実行した時点で循環参照となってしまっています。
このメソッド内では クロージャを受け取るメソッド func run(printClosure: @escaping () -> Void) を実行しているのですが、渡したクロージャが Printer のプロパティへと代入されてしまっているのでクロージャへの強参照が生じています。

これにより、クロージャが Computer のインスタンスself で強参照する、 Computer のインスタンスがプロパティで Printer のインスタンスを強参照する、 Printer のインスタンスがプロパティでクロージャを強参照するという循環参照となってしまっています。

対処例

class Computer {
    var printer: Printer?
    
    // self の参照を弱参照にする
    func printName() { [weak self] in
        printer?.run {
            print("Computer name is", self ?? "")
        }
    }
}

class Printer {
    var printClosure: (() -> Void)?
    
    func run(printClosure: @escaping () -> Void) {
        self.printClosure = printClosure
        run()
    }
    
    func run() {
        printClosure?()
    }
}

var computer: Computer? = Computer() // Computer の参照カウンタ 1
var printer: Printer? = Printer() // Printer の参照カウンタ 1

computer?.printer = printer // Printer の参照カウンタ 2
computer?.printName() // Computer の参照カウンタ 1 , クロージャの参照カウンタ 1

computer = nil // Computer の参照カウンタ 0 , Printer の参照カウンタ 1
printer = nil // Printer の参照カウンタ 0 , クロージャの参照カウンタ 0

他と同様に弱参照を用いて防ぎます。

このケースの厄介な部分は、 func printName() の処理を見ただけだとクロージャがどう扱われているのかわからないという点です。 今回は func run(printClosure: @escaping () -> Void) の処理を見たときに、クロージャをプロパティへ代入していることがすぐにわかりますが、メソッド内の処理が複雑になってくると、クロージャがどう扱われるのかというのが把握しづらくなってしまいます。

@escaping

そこで @escaping というものがあります。

func run(printClosure: @escaping () -> Void) という部分で使われていますが、 これが付いていると、そのクロージャがどこかで強参照されるということを保証してくれます。そのため、このメソッドにクロージャを渡す際は、クロージャ内で扱うインスタンス[weak self] のように弱参照としておく必要があるということがすぐわかります。

まとめ

なんとなくわかった気がしたのですが、普段コードを書いていてもシュッと「これは循環参照するぞ」なんてわからなそうなので、迷ったらとりあえず weak をつけておけばいいのかなあという感じがしました。

追記

komaji504.hateblo.jp

上記のエントリで改めて @escaping 属性について書きましたが、 @escaping 属性がついていれば循環参照の可能性があるので弱参照にする、ついていなければ強参照にするで良さそうです。

UIPickerView の使い方

UIPickerView とはこういうやつです。

f:id:komaji504:20161212003154p:plain:w300

これを使うための最低限の部分です。

UITableView 等と同様に DataSource と Delegate を設定して、表示する列数、行数、要素を各メソッドで return するといった感じです。

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var pickerView: UIPickerView! {
        didSet {
            pickerView.dataSource = self
            pickerView.delegate = self
        }
    }
    
    let components = (1...100).map { "\($0)" }
    
}

extension ViewController: UIPickerViewDataSource {
    
    // 列数
    // 1列であってもこのメソッドを省略できない
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }
    
    // 行数
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return components.count
    }
    
}

extension ViewController: UIPickerViewDelegate {

    // 要素
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return components[row]
    }
    
}

各要素の表示領域の backgroundColor を変更したりとか色々手を加えたい場合は、上記の Delegate メソッドではなく

func pickerView(UIPickerView, viewForRow: Int, forComponent: Int, reusing: UIView?)

UIPickerViewDelegate - UIKit | Apple Developer Documentation

を使って、色々手を加えた View を返すようにすることでできそうです。

ちなみに、UIDatePicker の countDownTimer の hour や min のように固定ラベルをつけたいとなっても、それようのプロパティやメソッドはなさそうでした。 やるなら UIPickerView とは別途、固定の UILabel を置く等しなければならなそうです。

UIAlertController 使ってみた

UIAlertController を使うことで Alert や Action Sheet を画面に表示することができます。 Alert や Action Sheet とはこういうやつ↓です。

Alert Action Sheet
f:id:komaji504:20161024011540p:plain f:id:komaji504:20161024011551p:plain

使う際の流れとしては、以下のような感じです。 Alert も Action Sheet もやることは同じなので Alert について書きます。

  1. Alert となる UIAlertController のインスタンスを生成する
  2. 表示する各項目 (キャンセルボタンとか) を作る
  3. 各項目をインスタンスに設定する
  4. 表示する ViewController で present する

Alert となる UIAlertController のインスタンスを生成する

インスタンス生成時にタイトルとメッセージと Action か Action Sheet かを設定します。

let alert = UIAlertController(title: "Alert",
                              message: "Alert Message",
                              preferredStyle: .alert // ここを .actionSheet に変えると Action Sheet になります
)

表示する各項目 (キャンセルボタンとか) を作る

項目の style (UIAlertActionStyle) は .default, .destructive, cancel の3つがあります。
.cancel 以外は複数指定できます。

let defaultAction = UIAlertAction(title: "Default",
                                  style: .default,
                                  handler: { action in
                                    print("Default")
})

let destructiveAction = UIAlertAction(title: "Destructive",
                                      style: .destructive,
                                      handler: { action in
                                        print("Destructive")
})

let cancelAction = UIAlertAction(title: "Cancel",
                                 style: .cancel,
                                 handler: { action in
                                    print("Cancel")
})

各項目をインスタンスに設定する

インスタンスに対して addAction(action: UIAlertAction) で設定できます。

alert.addAction(defaultAction)
alert.addAction(destructiveAction)
alert.addAction(cancelAction)

表示する ViewController で present する

現在表示されている ViewController で Alert を表示する場合は以下のように書きます。

present(alert, animated: true, completion: nil)

iPad 対応

iPad の場合、 Action Sheet はポップオーバーで表示されます。

Action Sheet for iPad
f:id:komaji504:20161024012758p:plain

この時、以下の設定がされていないとクラッシュするので忘れないよう気をつけてください。

// 表示先の View
actionSheet.popoverPresentationController?.sourceView = view
// 表示場所
actionSheet.popoverPresentationController?.sourceRect = rect

サンプル

こちらにサンプルを置いたので良かったら試してみてください。

github.com

shibuya.swift #5 で初めての LT をしてきた

shibuya.siwft で生まれて初めての LT をしてきました。

shibuya-swift.connpass.com

shibuya.swift にはお手伝いとして何度か参加させてもらっていたのですが、今回は自分が話す側だったのでとても緊張しました。頭が真っ白になることなく無事終わったので良かったです。

話すことになった経緯

会社の先輩に「なんか話してよ」と言われたので、「あ、はい、わかりました!(参加ボタンぽち)」という感じで LT をすることになりました。

自分は、今年配属されてからの6ヶ月間は Ruby on Rails でのAPI開発をやってきました。iOS開発をやるようになったのは7月からで、まだ3ヶ月しか経っていません。そのため、話す内容なんてない...とか思っていたのですが、勢いで参加ボタンを押してしまったので、何かネタを見つけなければと思いひたすら考えていました。

ペーペーの自分が技術的な話をするのはちょっと厳しいなと思っていたので、なんか経験談みたいのを話せればなあと思っていました。

だったら新卒の数ヶ月間で API開発とiOS開発両方やってきた人はあんまりいなそうだからそういうこと話せばいいのかあ、でも需要あるのかなあとか思っていたところ、「新卒でAPI開発からiOS開発やってきてどうだったかみたいな話が面白そう」という助言をいただいたので、やっぱりこれしかないと思い内容を練り始めました。

資料づくり

LT は見たことあるけどどうやって資料作れば良いかわからん!という感じだったので、まずは他人の発表資料を漁って、 LT の資料とはどんな感じかをつかむところから始めました。 資料だけだとちょっとよくわからんという感じもあったので、動画も見たりしました。

他にもLT 資料 作り方 みたいな感じでググったりしてみたりもしました。

まあ雰囲気は掴めた気がしたのですが、結局よくわからんとなったので、内容を練ることにしました。

まずは頭の中で、どんなことをやってきたか、どんなことを学んできたかを振り返ってみました。 ざっと、こういうことがあったなあという感じには振り返ることができ、なんとなくの話の流れをイメージすることができてので、次に、各話をもっと深堀りするためにマインドマップを書いてみました。

まあ書いてみたのですが、頭でざっと振り返った以上のことは書けずに特に進展がなかったので 、 実際にどんなことがあったのか具体的に思い出すために、 GitHub の PR や issue を眺めることにしました。

ネタ探しにはこれが一番効果がありました。この時に自分はどう考えて、どういうアドバイスをもらって、どういうことを学んだのかということを結構具体的に思い出すことができました。

そして、項目ごとに文章に書き出してみました。これを幾つか書いていると、ここの話を書こうとか、ここはやめておこうとか結構まとまってきたので、先輩に見てもらって、内容とか流れをブラッシュアップしていきました。

これを何回か繰り返して文章ではまとまったので、スライドを作り始めました。

まずスライドにはひたすら箇条書きで書いていきました。最初から最後までずらっと適当にスライドに書き起こしてから、一通りの流れを見て、それからスライドを分割したり、まとめたりを繰り返していきました。

最後にレイアウトやデザインを少しだけ修正して完成させました。

speakerdeck.com

練習

あまり緊張せずとも人前で話すことができる人もいるかと思いますが、自分はとても緊張してしまいます。

今回緊張してしまう理由は、大人数の・初対面の人に対して・LT をする ことに慣れていないことだと思います。常日頃当たり前にやっていることは緊張しないのと同様に、これにも慣れてしまえば緊張もしなくなると思います。

ただ、大人数という部分にすぐ慣れるなかなか難しいかと思います。初対面の人に対してという部分も同様です。ですが、LT をするという部分は、練習を重ねることで慣れることができます。そのために、何度も練習を繰り返しました。

練習は、以下の4つのフェーズに分けて行いました。

  1. 原稿を読みながら練習する
  2. 原稿をなるべく見ないようにして練習する
  3. スライドだけを見ながら練習する
  4. 何も見ずに練習する

これらをタイマーで時間をはかったり、音声を録音したりしながら行いました。

本番

とにかくやるだけ!!

やってみて

慣れていないことをするにはとても抵抗があって、最初の一歩がなかなか踏み出せないのですが、なんだかんだやってしまえばやれるもだなあと思いました。クオリティはどうであれ。

今回の LT も準備すればできるということが分かり、とても自信となったので、また機会があればやってみたいなあという気持ちになりました。

今後は、技術的な話にもチャレンジしていきたいです。

最後に、会場準備をしてくださった Fabric さん、ありがとうございました!