2019年に使い始めた便利Tools まとめ

Notion

f:id:m2-b0c27:20200112225035p:plain www.notion.so

情報一元化メモツール。

カンバン形式・カレンダー形式・リスト形式・ギャラリー形式など表示方法が様々、機能も盛り沢山なメモツールです。

Google Keep や Inkdrop、EverNoteなど色々試した結果、Notionに辿り着来ました。 使い方に関しては別の記事で書こうと思っています。

SlidePad

slidepad.xyz

Mac上にスライドするブラウザーWindowを追加拡張するツール。

GmailGoogle Calendar, YouTube Music, Trello といったよく使うウェブツールを別Windowで開くことができるツールです。

似たようなツールにStationがありますが、Stationとの違いとしてSlidePadには簡単にWindowを開いたり閉じたりできる機能があります。

"Chromeから常駐しているタブを無くす"、"Chromeのタブの中から特定の常駐タブを探す無駄を無くす"ことが出来ます。

Flotate - ⚠️Deprecated

flotato.com

SlidePadを導入してから使うのをやめたのですが紹介します。

Webアプリケーションをデスクトップアプリとして扱えるようになるツールです。

これによって先程言っていた「"Chromeから常駐しているタブを無くす"、"Chromeのタブの中から特定の常駐タブを探す無駄を無くす"」事ができましたが、"アプリを切り替えるときにCmd+tabで表示されるアプリが増える"というペインが発生しました。

こっちの方が好みという人も多いかもしれません。

Dash

Dash for macOS - API Documentation Browser, Snippet Manager - Kapeli

Dashと聞けばドキュメントツールという印象があるかもしれませんが、自分はスニペットツールとしても使っています。

Githubのissueやpull requestを書くとき、質問するときに思考をまとめたいとき、現在時刻を書きたいときなどでスニペットを使っています。

Chrome Extensions

Vimium

vimium.github.io

Vimキーバインドを使ってChromeブラウジング出来るようにするプラグイン。 なるべくマウスやトラックパッドを使いたくない人におすすめ。

Vim同様にキーバインドを自分好みに変えることも出来るので、キーバインドを変えている自分でも大満足のツールです!

キーバインドの変え方は Key Mappings · philc/vimium Wiki を見れば大抵わかります。

紹介として自分のキーバインドも晒しておきます。

# Insert your preferred key mappings here.
map <c-j> scrollFullPageDown
map <c-k> scrollFullPageUp
map [ goBack
map ] goForward
mapkey <c-c> <c-[>
mapkey <c-d> d
mapkey <c-u> u

Quick Tabs

chrome.google.com

Chromeで開いているタブを検索出来るプラグイン

後で紹介するAlfred - Search Safari and Chrome Tabsの方が使い勝手が良かったので "タブを検索する" という機能は使っていないですが、"以前開いていたタブを開く"という機能を使うために入れています。

f:id:m2-b0c27:20200112230419p:plain

"Older"も"Newer"も出来るのでとても重宝しています。

Copy as Markdown

chrome.google.com

ChromeでリンクをMarkdownのリンクとしてクリップボードにコピーしてくれるプラグイン。 タイトルもそのリンク先に合わせてよしなに用意してくれるので、とても便利です。

自分は Cmd+Shift+L のショートカットを当てて使っています。

ex:

https://masegi.hatenablog.com/entry/useful_tools_2019
↓
[2019年に使い始めた便利Tools まとめ - まっせぎ blog](https://masegi.hatenablog.com/entry/useful_tools_2019)

Alfred Workflow

Bluetooth Connector

www.packal.org

ワイヤレスイヤホンの接続先をスマホからPCに奪い取るときに使っているworkflowです。 (AirPodsならよしなに切り替えてくれるのかな...?)

Github

github.com

Githubでレポジトリなどを検索するときに便利なworkflowです。 自分は主に、 アサインされたissueレビュワーにアサインされているPR を探すときに使っています。

アサインされたissue : "gh my issues assigned" レビュワーにアサインされているPR : "gh my pulls review requested"

Search Safari and Chrome Tabs

www.packal.org

Chromeの開いているタブの中から特定のタブをワードで探すworkflowです。

自分はChromeのタブを沢山開いてしまう人間なので、目当てのタブを探すのに時間が掛かってしまっていました。 それを解決してくれるworkflowです。

UICollectionViewLayoutについて - 準備編 -

UICollectionViewのDocumentを読んだので、覚え書き程度にまとめました。

登場するメソッドで何をするのかをまとめたもので、具体的な実装方法等(コード)は一切書いていません。具体的な実装方法を知りたい方は、実装編を参照してください。

既知のことが多いと思いますので、流し読みしてください。

UICollectionViewLayoutとは

UICollectionViewCellなどを、どの位置にどんな状態で表示するかを決定するオブジェクト

UICollectionViewのLayoutを調整するものとして、

- UICollectionViewFlowLayout
- UICollectionViewDelegateFlowLayout

などがある。

その中で最も自由度の高い方法がUICollectionViewLayoutを作成するやり方。

どれを選べば良いかはCookpadさんがブログで書いてくれています。

techlife.cookpad.com

今回はこのUICollectionViewLayoutについて紹介します。

UICollectionViewを構成する視覚要素

UICollectionViewにはレイアウトが必要な3種類の視覚要素がある。

- Cells
- Supplementary views
- Decoration views

UICollectionViewは様々なタイミングでこれらの視覚要素のレイアウト情報を提供するようにLayoutオブジェクトに依頼する。

Cells

セルはレイアウトによって位置を決める主要要素。データによってはセクション毎に分けたりする。

レイアウトオブジェクトの主な仕事はUICollectionViewのViewの中身にセルを配置すること。

Supplementary Views

Header, FooterといったCellとは違うもの。これもUICollectionViewLayoutによって指定しなければならない。(optional)

Decoration Views

装飾するView。Supplementary viewsに似ているが少し違う。

こちらで場所を指定できる。(セル間の区切り線 etc..)

qiita.com

レイアウトを定める

視覚要素(Cells, Supplementary Views, Decoration Views)をどのように配置するかを決めるのが以下のメソッド。

- collectionViewContentSize
- prepare()
- layoutAttributesForElements(in:)

- layoutAttributesForItem(at:)
- layoutAttributesForSupplementaryView(ofKing: at:)
- layoutAttributesForDecorationView(ofKing: at:)

UICollectionView自体のレイアウトが変更されたり、Cellが移動・追加・削除されるたびに新しいレイアウト情報を得るために呼び出される。

collectionViewContentSize

Collection viewの全体のheight, widthを返す。

prepare()

layoutAttributesForElements(in:)は頻繁に呼び出されるため、なるべく計算処理を含まない方が良い。

そこで、レイアウト情報が更新される前に呼ばれるprepare()をOverrideして、レイアウト情報を求める計算処理を記述するのが普通である。

layoutAttributesForElements(in:)

指定された短形内の全てのセルとViewのLayoutAttributesを返す。

Cells, Supplementary views, Decoration viewsの全てのLayoutAttributesを返す必要がある。

下記のメソッドを実装することで、それぞれの要素ごとに責務を分割できる。

これらは自分で呼び出さない限り実行されない

- layoutAttributesForItem(at:)
- layoutAttributesForSupplementaryView(ofKing: at:)
- layoutAttributesForDecorationView(ofKing: at:)

自分はこれらのメソッドを使わず、自分で実装した方が楽な時の方が多い気がします。

Layout Performanceの最適化

Custom layoutを作る時、実際に変更された部分だけを無効・更新するように書くことでパフォーマンスをあげることができる。

invalidateLayout()を呼び出すことで、レイアウト情報のみを更新することができる。(reloadData()はDataSourceとレイアウトを更新する。)

更にUICollectionViewLayoutInvalidationContextのサブクラスを作成し用いることで、より無効・更新する箇所を限定することができる。

以下で最適化するのに必要なメソッドを紹介する。

- shouldInvalidateLayout(forBoundsChange:)
- invalidationContext(forBoundsChange:)
- shouldInvalidateLayout(forPreferredLayoutAttributes:withOriginalAttributes:)
- invalidationContext(forPreferredLayoutAttributes:withOriginalAttributes:)

shouldInvalidateLayout(forBoundsChange:)

CollectionView自体のレイアウトが変更された時に、中のレイアウトを更新するかどうかを決める。

更新する時はtrue、しなくていい時はfalseを返す。

trueを返された場合はinvalidationContext(forBoundsChange:)が呼び出され、この返り値のUICollectionViewLayoutInvalidationContextを引数にinvalidateLayout(with:)を呼び出し、現在のレイアウトを無効にしてからprepare()を呼び出す。

invalidationContext(forBoundsChange:)

ここではUICollectionViewLayoutInvalidationContextを変更に応じて編集し、返す。

このContextをもとにinvalidateLayout(with:)でキャッシュしたレイアウト情報を削除する。

invalidateLayout(with:)

現在のレイアウト情報によるレイアウトを無効にする。

その際に、引数のUICollectionViewLayoutInvalidationContextの情報から更新しなければいけない箇所をを読み取り、事前に計算したレイアウト情報を修正する。(計算後にキャッシュしたレイアウト情報を削除する等々...)

shouldInvalidateLayout(forPreferredLayoutAttributes:withOriginalAttributes:)

Self-sizing(AutoLayout)を行うCellについて、preferredLayoutAttributesFitting(_:)で得られたLayoutAttributeslayoutAttributesForElements(in:)で指定したものと異なる場合に呼び出される。

先ほど紹介したshouldInvalidateLayout(forBoundsChange:)の引数が変ったもの。

同様に、trueを返すと更新処理に遷り、falseを返すと何もしない。

trueを返すとinvalidationContext(forPreferredLayoutAttributes:withOriginalAttributes:)が呼び出される。

Self-sizing (AutoLayout)の具体的な説明は後述する。

invalidationContext(forPreferredLayoutAttributes:withOriginalAttributes:)

先ほどのinvalidationContext(forBoundsChange:)と同様のことを行う。

(アニメーションを付ける)

アニメーションをつける場合は、これらのメソッドをオーバーライドして適切なレイアウト情報を提供する必要がある。

- initialLayoutAttributesForAppearingItem(at:)
- initialLayoutAttributesForAppearingSupplementaryElement(ofKing: at:)
- initialLayoutAttributesForAppearingDecorationElement(ofKing: at:)
- finalLayoutAttributesForDisappearing~(at:)

さらに、

- finalizeCollectionViewUpdates()

をオーバーライドすることで、全体にアニメーションを追加したり、レイアウト関連の最終タスクを実装したりすることもできる。

これらについては、本記事では割愛する。

Self-Sizing (Auto Layout)

Self-SizingによってCellのSizeが変更になった時、shouldInvalidateLayout(forPreferredLayoutAttributes:withOriginalAttributes:)が呼び出され、レイアウトを更新するか指定できる。

developer.apple.com

引数で与えられるforPreferredLayoutAttributesは、preferredLayoutAttributesFitting(_:)の返り値、つまり、AutoLayoutによって測定されたCGSizeが含まれる。

それをキャッシュし、用いることによってUITableViewのようなSelf-Sizingを行うことができる。

具体的な実装については実装編を参照してください。

おわり。

複雑な画面をRecyclerViewで作るEpoxy - Android

Androidで複雑な画面を作る際に、Airbnbが提供しているEpoxyというライブラリが良い感じだったので紹介します。

この記事はShibuya.apkで発表した、今更ながらEpoxyを元に書いています。 見ていただけるとありがたいです。

今更ながらEpoxy / Lateinit Epoxy - Speaker Deck

また、Epoxyを触った際に作ったレポジトリもGithubで公開しているので、そちらも参考にしてください。

github.com

Epoxyとは?

github.com

  • 複雑な画面をRecyclerViewで作るためのライブラリ
  • Airbnbが作成している
  • 今でも結構活動している
  • 2016/08/25に初リリース
  • androidx対応

ざっくり言うと、各Section(Row)毎のレイアウトを組んで順番に並べれば、それをRecyclerViewの再利用性を活用しつつ簡単に表示してくれる。というものです。

Airbnbが紹介記事を出しているので、是非こちらをお読みください。

medium.com

似たようなものとして、最近ではGroupieも有名です。自分はGroupieを使ったことがないので比較については書きません。

AirbnbEpoxyの他にも、MvRxPairsなどのライブラリを出していて、それらとの相性も良いです。

メリット

  • 簡潔に複雑な画面を作ることができる
  • RecyclerViewを使うことによって、各Viewが表示される直前にロードされるためパフォーマンスが良い
  • Viewの状態を保存しているので、Carouselなどのスクロール位置を画面外にいっても覚えている
  • 差分更新によって滑らかなアニメーションが実現できる

大まかな流れ

・セルのレイアウトを作成

.xml, .ktどちらからでも作成可能ですが、今回はより楽なXMLファイルとdatabindingを用いた方法で作ってみます。.ktで作成したい場合はこちらを参考にしてください。

・セルからEpoxyModelを自動生成

作成したセルをpackage-info.java内で指定することで、EpoxyRecyclerViewで使用するEpoxyModelXML(.kt)から自動生成します。

・Controllerを作成

RecyclerViewでいう所のAdapterとなる、Controllerを作成します。ここで、各セルの表示順序、データ、OnClick()等を設定します。

EpoxyRecyclerViewControllerをセット

最後に、EpoxyRecyclerViewをViewに追加して、Controllerをセットして完成です。

導入

それでは実際に以下のような画面を作ってみたいと思います。

1. 準備

app build.gradle

apply plugin: 'kotlin-kapt'

kapt {
    correctErrorTypes = true
}
dependencies {
  implementation 'com.airbnb.android:epoxy:3.x.y'
  implementation 'com.airbnb.android:epoxy-databinding:3.x.y'
  kapt 'com.airbnb.android:epoxy-processor:3.x.y'
}

x, yには最新のバージョンを入れてください。(投稿時は3.3.0

package-info.java

EpoxyModelをどのXMLファイルから作るか記述するものです。XMLファイルのみでdatabindingを使用してEpoxyModelを作る際に必要になります。

.kt (or .java)ファイルを置くフォルダの直下に作成してください。

@EpoxyDataBindingLayouts({R.layout.header_view, ... // other layouts })
package com.airbnb.epoxy.sample;

import com.airbnb.epoxy.EpoxyDataBindingLayouts;

これは下記のように接頭辞での指定も可能です。

@EpoxyDataBindingPattern(rClass = R.class, layoutPrefix = "view_holder")
package com.example.package;

2. セルの準備

これは特に変わったことはありません。databindingを使ってレイアウトを組む時と同じです。双方向のdatabindingは出来ないことに注意しましょう。

3. Controllerの作成

Controllerを作成する際はTypedEpoxyController<T>クラスを継承します。このTは中身のデータの型です。二つ渡したいときはTyped2EpoxyController<T, U>というクラスがあります。詳しくはこちらに記載されています。

Controllerでやることは、buildModels(data:)を実装することです。コードをみた方が早いと思うので今回作ったものを載せます。

2. セルの準備で作成したレイアウトをpackage-info.javaに記載後、BuildするとEpopxyModelが自動生成されます。

名前は、

R.layout.~による指定 -> そのまま
ex) R.layout.epoxy_header_view -> EpoxyHeaderViewBindingModel_.java
接頭辞による指定  -> 接頭辞以降
ex) (layoutPrefix = "epoxy")
R.layout.epoxy_header_view -> HeaderViewBindingModel_.java

といったようになります。

これを用いてbuildModels(data:)に記述していくのですが、便利なExtensionがあり、次のように記述出来ます。(今の所は...)

ここでidはViewを一意に判断するための識別子で、その後のciryName, descriptionXMLファイルで書いた<data />タグの中身です。

このようなViewのブロックを順に並べることで、その順通りに表示されます。

4. EpoxyRecyclerViewControllerをセット

残りはViewにEpoxyRecyclerViewを追加して、作成したControllerをセットして完成です。セットにはEpoxyRecyclerView.setController(controller:)を使って、controller.setData(data:)でデータを流します。

加えて..

EpoxyVisibilityTracker

スクロールイベントとして、それぞれのRowが表示されているか否かどれくらい表示されているかを引数としたコールバックを設定できます。こちらに詳しいことが記載されています。

val epoxyVisibilityTracker = EpoxyVisibilityTracker()
epoxyVisibilityTracker.attach(recyclerView)

Carousel

Epoxyの利点として、Carouselが標準で付いていることは大きいです。

導入はとても簡単で、以下のExtensionを追加すれば今までのRowと同様に導入することができます。詳しいことは公式ドキュメントで読んでください。

numViewsToShowOnScreen(num:)padding(padding:)でアイテムの間隔等を決めます。

padding(padding:)の引数としてPadding.dp(padding: itemSpacing:)を渡します。それぞれの変更箇所は図の通りです。現状ではitemSizeを指定してレイアウトを決めるということはできないようです。(2019/2/24)

ピッタっと止めるようにするには、RecyclerViewでも使われているSnapHelperFactoryを作成して、Carousel.setDefaultGlobalSnapHelperFactory(factory:)を呼び出すことで変更できます。詳しくはこちらで。

注意点として...

CoordinatorLayoutなどと一緒に使用する際(Nested Scrollが発生する際)は注意が必要です。

github.com

最後に

複雑な画面を作る際に、ScrollView + Fragmentで実装するとパフォーマンスも可読性も落ちてしまう場合があります。

複雑な画面を実装するのに苦労している時は、EpoxyやGroupieなどのRecyclerViewを活用するライブラリを用いて、より簡潔に簡単に作って楽になりましょう!

(次はMvRxについて書こうと思います....)

AVPlayerについて調べた - iOS

https://s3-ap-northeast-1.amazonaws.com/hatena.masegi/AboutAVPlayer/Group.png

AVPlayer周りのドキュメントを読んだので、 AVAssetAVPlayerItemAVPlayerについて書いてあること自分に分かりやすいように書き起こしました。

動画をViewに反映する方法については詳しく書いていませんので、ご了承ください。

読んだもの

AVFoundation

  • audiovisual assets
  • control device cameras
  • process audio
  • system audio interactions

Media Assets, Playback, and Editing

  • media assetsにアクセス
  • mediaの再生(再生をカスタマイズ)
  • mediaの編集や結合
  • import and export raw media streams

Asset Modelについて

Media Playback Programming Guide

AVAsset

  • 単一のメディアリソースを表す抽象的で不変の型(abstract, immutable type)
  • ローカルファイルだけでなく、プログレッシブダウンロードされたもの、HLSを使用してストリーミングされたものも表せる
  • formatを気にせずに扱えるようにする
  • locationを気にせず扱えるようにする(URLを用いてイニシャライズ)
  • AVAssetはAVAssetTrackのコンテナオブジェクト

AVAssetTrack扱っている種類

  • video
  • audio
  • subtitles
  • caption
  • timed metadata

Overview

let url: URL = // Local or Remote Asset URL
let asset = AVAsset(url: url)

実際にはAVURLAssetと言う具象サブクラスが生成される。

Wi-Fi下のみでのリソースのダウンロード等のオプションを付けたい時はAVURLAsset.init(url:option:)を使用する。

他にもAVComposition等でイニシャライズすることも可能。

リソースによっては、初期化が完了しても即時に再生できないものもある。再生を要求することはいつでも出来るが、そのためには呼び出し元のスレッドを止める必要があるかもしれない。

そのブロッキングを避けるために、AVAsynchronousKeyValueLoadingを使って完了通知を受けるように登録することができる。

AVAssetを再生するには、AVPlayerItemを生成してAVPlayerを使って再生する。

AVAssetオブジェクトをAVMutableCompositionオブジェクトに挿入することで、1つ以上のAssetからaudiovisual構造を組み立てることができる。

Topics

⚠️必要そうなものだけをピックアップしました。

生成方法

init(url: URL)

ローディングデータ

func cancelLoading()

全てのロードを中断する。

Assetについて

var duration: CMTime

assetの再生時間(CMTimeについて

AVPlayerItem

Overview

ざっくり言うとAVAssetにロードされたかどうかのStatus情報が付属されたもの。

これを渡してAVPlayerに再生してもらう。

init(url: URL)
init(asset: AVAsset)

上記のイニシャライザに加えて、事前にロードを開始する

init(asset: AVAsset, automaticallyLoadedAssetKeys: [String]?)

がある。

Topics

生成方法

init(url: URL)
init(asset: AVAsset)
init(asset: AVAsset, automaticallyLoadedAssetKeys: [String]?)

その他

var status: AVPlayerItem.Status

.readyToPlay, .unknown, .failedがある。

var loadedTimeRanges: [NSValue]

ロード済みの部分のタイムレンジ

var seekableTimeRanges: [NSValue]

シーク可能な部分のタイムレンジ

func seek(to: CMTime, completionHandler: ((Bool) -> Void)

シーク

func currentTime() -> CMTime

再生されている現在の時間

var presentationSize: CGSize

videoの場合にplayerに提示される表示サイズの比率

AVPlayer

Overview

media assetの再生、タイミング調整を管理するコントローラオブジェクト。

1度に1media assetしか再生できないため、Playerを再利用するならreplaceCurrentItem(with:)メソッドを使う。(AVQueuePlayerというものもあるが、便利ではないので使わない...)

AVPlayerは連続的に状態が変化する動的オブジェクトである。Playerの状態を監視するには二つの方法がある。

一般的な方法

Key-value observing(KVO)を用いて、再生中のアイテムや再生速度などを監視できる。

その際にはobserveValue(forKeyPath:of:change:context:)メソッドを用いる。

時間の変化による監視方法

ある一定時間が経過した時に特定の処理を実行するようにできる。

  • addPeriodicTimeObserver(forInterval:queue:using:)

特定の時間間隔ごとに処理を実行する。

  • addBoundaryTimeObserver(forTimes:queue:using:)

特定の時間が経過した時に処理を実行する。

Viewへの反映方法

AVPlayerは非視覚的オブジェクトであり、スクリーンに動画を反映することはできない。

スクリーンに反映する方法は主に二つある。

AVKitを用いた方法

AVKitにあるAVPlayerViewControllerを用いて表示を行う。(AVPlayerViewmacOSのみ)

AVPlayerLayerを用いた方法

AVPlayerLayerを用いて表示を行う。

AVPlayerViewControllerのようにコントローラのViewは無いので、自前で設定する必要がある。

Topics

生成方法

init(url: URL)
init(playerItem: AVPlayerItem?)

再生管理

func play()
func pause()

言わずもがな。

var rate: Float

再生速度。(rate = 0pause()

var actionAtItemEnd: AVPlayer.ActionAtItemEnd

再生が終わった時にどうするか。.advance, .pause, .noneがある。

func replaceCurrentItem(with: AVPlayerItem?)

再生するAVPlayerItemを変更する。

var preventsDisplaySleepDuringVideoPlayback: Bool

動画再生中に画面スリープするかどうか。

時間

func seek(to: CMTime)
func seek(to: CMTime, completionHandler: ((Bool) -> Void)

シーク

func currentTime() -> CMTime

再生されている現在の時間

func addPeriodicTimeObserver(forInterval: CMTime, queue: DispatchQueue?, using: (CMTime) -> Void) -> Any
func addBoundaryTimeObserver(forTimes: [NSValue], queue: DispatchQueue?, using: () -> Void) -> Any

監視する系

func removeTimeObserver(Any)

監視を外す

参考

developer.apple.com

nackpan.net

今までの自分とこれからの自分

今年もあと少しということで、今年何を得てどんな反省をしたのか、どのように生きていくのかを振り返ってみました。駄文になってしまいましたが、読んでいただければ幸いです。

今年何をしたのか

  • インターンiOSアプリを開発
  • ブログでのアウトプットを始める
  • Androidの勉強会にLTとして参加
  • 就職活動

Googleカレンダーを見返してみたら、あの時頑張ってコード書いてたなとか辛かったなとか、色々な感情が出てきて泣きそうになりました。 年々涙腺が緩くなっているのを切実に感じています...

それぞれについていろいろエピソードを書いていたのですが、長くなってしまうので割愛して言いたいことだけまとめておきます。

インターンiOSアプリを開発

去年の10月あたりから友人からフリーランスとして働いている方を紹介してもらい、その人の元でiOSアプリの開発を行なってきました。

業務としては、顧客から受け取った仕様やデザインをコードに落とし込みアプリにするという事を行なっています。

この1年で自分がどのようなコードが綺麗で読みやすいか、どんなコードはダメかという判断を少しですが出来るようになり、それがライブラリの選定にも繋がったことに成長を感じることができました。

ブログでアウトプットを始める

学んだ事をアウトプットして理解を深める。アウトプットするネタを探すために学ぶ。という俗に言うアウトプット駆動学習をはじめました。

あまり閲覧数は増えてはいないのですが、知識を整理する事で理解を深めることができたり、曖昧なところもより突っ込んで学ぶようになり、順調に効果を得られていると思います。

自分でインプットした知識をアウトプットする際に、その知識が正しいのか、より良い方法はないのかと振り返る機会が出来るので、より深いところに落とし込むことが出来ます。

Androidの勉強会にLTとして参加

アウトプット駆動学習を始めたことをきっかけに、ノリと勢いで勉強会のLT枠に参加させていただきました。

大勢の前で話すことが苦手だったのでその改善と、エンジニアの方とコネクションを作るという目的があったのですが、思った以上に成果があったので嬉しかったです。

特に今年はインターンiOS漬けだったので、Androidの最新技術をpick upするという意味でもLTは良い機会でした。

masegi.hatenablog.com

就職活動

自分にとって就職活動はとても辛いものでした。

  • 進路を決めるのが遅くなってしまいあまり余裕を持って行えなかった
  • 自分の行動理由を深掘りして相手に伝えるということが苦手だった
  • そもそも行動理由の深掘りを疎かにしていて自分の中で固まっていなかった
  • 相手にわかりやすく自分のことを伝えることが苦手だった

これらが辛かった理由です。自分は 行動理由 ≧ 技術力 と考えていたのですが、これは圧倒的に間違いで 行動理由 >>>> 技術力 であり、

そのことに気付かず、技術力を磨いては人事面接で落とされる。という沼にはまっていました。

なんとか内定をいただくことは出来ましたが、行動理由についてもっと考える時間を取っていれば就職活動も違った内容になっていたと思います。

今までの自分

今年は主に技術の深掘りや深掘る方法を学び、得た知識のアウトプットを行うことに注力してきました。

その事自体はとても良い事で、身になった事なのですが、どうしてそれをやる必要があったのか?と聞かれると返答に困ってしまいます。

こうして一年の振り返りをしてみると、以下の点が問題だと気づきました。

  • 自分が感覚的に必要だと思うものをただ闇雲に行なっていた
  • "新しい技術を試した"というツイートや記事を見るたび自分もやらなくては、と焦っていた
  • 具体的な目標を設定していなかったため達成度が分からない
  • 優先順位が 学ぶ時間 > 振り返る時間
  • 全てにおいて時間がかかり過ぎている

これでは自分が何をどうして行なったのか、何に向かって進んでいるのか、どれだけ進めたのかということが曖昧になってしまい、自信が持てず心理的安全性が欠けてしまいます。

実際に自分は自信が全くなく、そのことも起因して面接でも上手く話せなかったのだと思います。

これからの自分

2019年からは以下の事を実践して、上で挙げた問題を改善していきたいと思います。

目標を立てる

5年後の目標、1年後の目標、1ヶ月の目標、1間週の目標を考える。

5年後こうなりたいから1年後にはこうなっていないといけない。1年後にこの目標を達成するには1ヶ月間でここまで進めなければいけない。というように、期限付きの目標を遠い順から考えて、そこから逆算して細かい目標を立てるようにする。

目標に沿わないことはしないを徹底する。

優先順位を付ける

今までは技術力の向上 > 振り返る時間、目標を立てる時間だったのを、振り返る時間、目標を立てる時間 > 技術力の向上にしたように、優先順位をそれぞれのタスクに付けていく。

優先順位を決める際に必要な、自分の目標にあった優先順位の高さを決める軸を決める。

振り返り・目標を立てる > 習慣を付ける、より作業を効率よくする知識を付ける > 技術力の向上が当面の自分の軸だ。

振り返る時間を作る

立てた目標をどの程度達成できたのか、どうして達成できなかったのか、本当にこの目標は自分の進みたい道の途中なのか、ずれてはいないかを毎日5〜10分程度時間を取って振り返る。

また、1週間に1回は30分程度時間を取って振り返る時間を作るようにする。

何故それをしてるのか、どうしてそれをする必要があるのか、今後どのようになりたくてそれをしてるのかを明確にする。

知識を集める

どのようにすれば効率よく自分の目標に近付けるかを、様々な媒体を用いて集める。

本を読んで、自分にあったベストプラクティスを見つける。

自分がなりたいものになるにはどうする事が一番良いかを、実際に体現している人から話を聞く、コネクションを持つという事を積極的に行う。

自分のログをとる

知識を得たらそれを言語化する。何か形になるものに残す。ログをとる。

以前に調べたことのある事を再度調べ直すことは時間の無駄なので、ノートにまとめて直ぐに振り返れるようにする。

自分がいつ何をしたか、それにどのくらい時間を費やしたかを明確にすることで、振り返るときにどうしてそんなに時間をかけてしまったのか、改善のきっかけにする。

最後に

今までは自分について曖昧なことを山積みのまま過ごしてきました。これでは良いエンジニアにはなれない。

これからは全てにおいて明確にし、振り返り、自分の人生そのものをエンジニアリングするという事を心がけて生きていきます!

Custom View + RxSwift - iOS

Custom Viewを作るときに、どうやってRxと連携させるかを忘れないように記事にしました。

※ここでは、customView.rx.textDidChanged.~といったように、今までDelegateとして実装していたものをReactiveExtensionとして記述することをRx連携と呼ばせていただきます。

どうしてRx連携するの?

  • プロダクトがRxをゴリゴリ使っているから
  • コードが綺麗になる

プロダクトがRxSwiftをゴリゴリ使っている

自分が開発しているアプリではアーキテクチャMVVMで、ViewとViewModelとのつなぎにRxSwift(RxCocoa)を使っています。

そのため、RxSwiftで用意されていない場合は自分で連携させる必要があります。

コードが綺麗になる

コードの量が減るかどうかはプロダクトによりますが、Rxを使うと圧倒的に簡潔に分かり易くかけると思います。

どれくらい綺麗になるか、サンプルコードを書いてみたので比較してみてください。

Rx導入前

 Rx導入後

一番メリットとして感じるのは、今まで分離して関数を用意していたのがclosureとして記述できることだと思います。

結果的にUIViewControllerから煩わしいDelegateなどのコードが減るので、見通しもよくなります。

どうやって実装するの?

自作Viewを作るときに作成したDelegateを使って、DelegateProxy(Proxy: 代理)を作成し、それを使ってReactiveExtensionとしてDelegateの中身を実装します。

今回は、内部にUITextFieldを持っているFilledTextFieldという自作Viewを使って、Rx連携する手順を紹介します。

コードはGithubのgist上げているので参考にしてください。

CustomView + RxSwift · GitHub

例として自作ViewのDelegateの内の二つの関数をRx連携させてみます。

DelegateProxyの作成

DelegateProxy<P, D>DelegateProxyType, 自作ViewのDelegateを継承したクラス、DelegateProxyを作成します。

DelegateProxy<P, D>Pは自作View(FilledTextField)、Dは自作ViewのDelegate(FilledTextFieldDelegate)です。

ここでは自作ViewとProxyとがお互いのDelegateをつなげる作業を行います。

Delgateが複数ある場合は変更が必要ですが、そういったケースは稀なので今回は割愛します。

Extensionの実装

ReactiveExtensionとして実装していきます。

DelegateProxymethodInvoked(_:)を使って、Observableに変換します。

paramsDelegateの引数を指します。

完成!!

終わりに

一番FatになりがちなUIViewControllerのコードを綺麗に書くことができ、コードの可読性も向上できました。

RxSwiftを使いこなすことは難しいですが、上手く使えるようになればコードを綺麗に書けるようになると思います。

使い倒してどんどんコードを綺麗にしていきましょう!

SwiftyXMLParser + Moya + RxSwift 使ってみた

https://s3-ap-northeast-1.amazonaws.com/hatena.masegi/SwiftyXMLParser/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2018-11-19+3.44.29.png

iOSアプリを開発中にXMLを返すAPIを使うことになったので、XML形式のものをどうマッピングするかを忘れないように記事にしました。

なんでSwiftyXMLParserを使うの?

iOSでは標準でXMLParserを用意しています。

ではなぜXMLParserを使わないかというと、

  • 使い勝手が悪い
  • Rxと組み合わせて使うとなると色々大変

だからです。

サードパーティのXMLParser Libraryは何個かありますが、今回は

  • Moya (Alamoifre) と一緒に使う
  • Rxをゴリゴリ使っているプロジェクト

という状況だったのと、Star数を鑑みてSwiftyXMLParserを選択しました。

github.com

SwiftyXMLParserの使い方

導入の仕方、それぞれの要素へのアクセスの仕方については、READMEを参考にしてください。

ここでは、READMEに書いていない実際に使うときにはどうするかを書いていきます。

Modelへのマッピング

例としてradikoの番組を取得するAPIを使ってみます。(最近ではXML形式のレスポンスを返すAPI少なくて、探すの大変でした....)

以下のXML形式データをModelへマッピングします。

<station id="QRR">
  <name>文化放送</name>
  <progs>
    <date>20181119</date>
    <prog id="9463445504" master_id="" ft="20181119010000" to="20181119013000" ftl="2500" tol="2530" dur="1800">
      <title>加隈亜衣のアズールレーディオ</title>
      <url>http://www.joqr.co.jp/azur/</url>
      <pfm>加隈亜衣</pfm>
      <img>
        https://radiko.jp/res/program/DEFAULT_IMAGE/QRR/cl_20180328180436_859305.jpg
      </img>
      <metas>
        <meta name="twitter" value="#アズラジ"/>
        <meta name="twitter" value="from:azuradio_joqr"/>
      </metas>
    </prog>
    <prog id="9463445505" master_id="" ft="20181119013000" to="20181119020000" ftl="2530" tol="2600" dur="1800">
      <title>スクエニ第10BD開発局</title>
      <url/>
      <pfm>日高里菜 大久保瑠美</pfm>
      <img>
        https://radiko.jp/res/program/DEFAULT_IMAGE/QRR/20160927220522.png
      </img>
      <metas>
        <meta name="twitter" value="#joqr"/>
        <meta name="twitter" value="from:joqrpr"/>
      </metas>
    </prog>
  </progs>
</station>

XMLParsableプロトコルを作成する

SwiftyXMLParserのオブジェクトであるXML.Accessorからイニシャライズ可能を示す、XMLParsableを作成します。

XMLParsableを実装する

各ModelにXMLParsable#init(accessor: XML.Accessor)を実装します。

ネストしたオブジェクト・配列・attributeへのアクセスなど、引っかかりそうなものは拾い上げたので、

マッピングで苦戦している方は比較して真似してみてください。

Moya + RxSwift

Moyaのセットアップ

Moyaのセットアップについては、こちらを参考にしてください。

Serviceの作成

RxSwiftのSingleを返す関数を用意します。map()を使うことで、綺麗に書くことができます。

完成!

終わりに

XMLを返すapi絶滅危惧種ですが、使わざるを得ない際は参考にしてみてください。