2019年に使い始めた便利Tools まとめ
Notion
情報一元化メモツール。
カンバン形式・カレンダー形式・リスト形式・ギャラリー形式など表示方法が様々、機能も盛り沢山なメモツールです。
Google Keep や Inkdrop、EverNoteなど色々試した結果、Notionに辿り着来ました。 使い方に関しては別の記事で書こうと思っています。
SlidePad
Mac上にスライドするブラウザーWindowを追加拡張するツール。
Gmail や Google Calendar, YouTube Music, Trello といったよく使うウェブツールを別Windowで開くことができるツールです。
似たようなツールにStationがありますが、Stationとの違いとしてSlidePadには簡単にWindowを開いたり閉じたりできる機能があります。
"Chromeから常駐しているタブを無くす"、"Chromeのタブの中から特定の常駐タブを探す無駄を無くす"ことが出来ます。
Flotate - ⚠️Deprecated
SlidePadを導入してから使うのをやめたのですが紹介します。
Webアプリケーションをデスクトップアプリとして扱えるようになるツールです。
これによって先程言っていた「"Chromeから常駐しているタブを無くす"、"Chromeのタブの中から特定の常駐タブを探す無駄を無くす"」事ができましたが、"アプリを切り替えるときにCmd+tabで表示されるアプリが増える"というペインが発生しました。
こっちの方が好みという人も多いかもしれません。
Dash
Dash for macOS - API Documentation Browser, Snippet Manager - Kapeli
Dashと聞けばドキュメントツールという印象があるかもしれませんが、自分はスニペットツールとしても使っています。
Githubのissueやpull requestを書くとき、質問するときに思考をまとめたいとき、現在時刻を書きたいときなどでスニペットを使っています。
Chrome Extensions
Vimium
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
後で紹介するAlfred - Search Safari and Chrome Tabsの方が使い勝手が良かったので "タブを検索する" という機能は使っていないですが、"以前開いていたタブを開く"という機能を使うために入れています。
"Older"も"Newer"も出来るのでとても重宝しています。
Copy as Markdown
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
ワイヤレスイヤホンの接続先をスマホからPCに奪い取るときに使っているworkflowです。 (AirPodsならよしなに切り替えてくれるのかな...?)
Github
Githubでレポジトリなどを検索するときに便利なworkflowです。
自分は主に、 アサインされたissue
や レビュワーにアサインされているPR
を探すときに使っています。
アサインされたissue
: "gh my issues assigned"
レビュワーにアサインされているPR
: "gh my pulls review requested"
Search Safari and Chrome Tabs
Chromeの開いているタブの中から特定のタブをワードで探すworkflowです。
自分はChromeのタブを沢山開いてしまう人間なので、目当てのタブを探すのに時間が掛かってしまっていました。 それを解決してくれるworkflowです。
UICollectionViewLayoutについて - 準備編 -
UICollectionViewのDocumentを読んだので、覚え書き程度にまとめました。
登場するメソッドで何をするのかをまとめたもので、具体的な実装方法等(コード)は一切書いていません。具体的な実装方法を知りたい方は、実装編を参照してください。
既知のことが多いと思いますので、流し読みしてください。
- UICollectionViewLayoutとは
- UICollectionViewを構成する視覚要素
- レイアウトを定める
- Layout Performanceの最適化
- (アニメーションを付ける)
- Self-Sizing (Auto Layout)
UICollectionViewLayoutとは
UICollectionView
のCell
などを、どの位置にどんな状態で表示するかを決定するオブジェクト
UICollectionView
のLayoutを調整するものとして、
- UICollectionViewFlowLayout - UICollectionViewDelegateFlowLayout
などがある。
その中で最も自由度の高い方法がUICollectionViewLayout
を作成するやり方。
どれを選べば良いかはCookpadさんがブログで書いてくれています。
今回はこの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..)
レイアウトを定める
視覚要素(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(_:)
で得られたLayoutAttributes
がlayoutAttributesForElements(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:)
が呼び出され、レイアウトを更新するか指定できる。
引数で与えられるforPreferredLayoutAttributes
は、preferredLayoutAttributesFitting(_:)
の返り値、つまり、AutoLayoutによって測定されたCGSize
が含まれる。
それをキャッシュし、用いることによってUITableView
のようなSelf-Sizingを行うことができる。
具体的な実装については実装編を参照してください。
おわり。
複雑な画面をRecyclerViewで作るEpoxy - Android
Androidで複雑な画面を作る際に、Airbnbが提供しているEpoxy
というライブラリが良い感じだったので紹介します。
この記事はShibuya.apk
で発表した、今更ながらEpoxy
を元に書いています。
見ていただけるとありがたいです。
今更ながらEpoxy / Lateinit Epoxy - Speaker Deck
また、Epoxyを触った際に作ったレポジトリもGithubで公開しているので、そちらも参考にしてください。
Epoxyとは?
- 複雑な画面を
RecyclerView
で作るためのライブラリ Airbnb
が作成している- 今でも結構活動している
- 2016/08/25に初リリース
androidx
対応
ざっくり言うと、各Section(Row)毎のレイアウトを組んで順番に並べれば、それをRecyclerView
の再利用性を活用しつつ簡単に表示してくれる。というものです。
Airbnbが紹介記事を出しているので、是非こちらをお読みください。
似たようなものとして、最近ではGroupieも有名です。自分はGroupieを使ったことがないので比較については書きません。
AirbnbはEpoxyの他にも、MvRx、Pairsなどのライブラリを出していて、それらとの相性も良いです。
メリット
- 簡潔に複雑な画面を作ることができる
RecyclerView
を使うことによって、各Viewが表示される直前にロードされるためパフォーマンスが良い- Viewの状態を保存しているので、
Carousel
などのスクロール位置を画面外にいっても覚えている - 差分更新によって滑らかなアニメーションが実現できる
大まかな流れ
・セルのレイアウトを作成
.xml
, .kt
どちらからでも作成可能ですが、今回はより楽なXML
ファイルとdatabinding
を用いた方法で作ってみます。.kt
で作成したい場合はこちらを参考にしてください。
・セルからEpoxyModel
を自動生成
作成したセルをpackage-info.java
内で指定することで、EpoxyRecyclerView
で使用するEpoxyModel
をXML
(.kt
)から自動生成します。
・Controllerを作成
RecyclerView
でいう所のAdapter
となる、Controller
を作成します。ここで、各セルの表示順序、データ、OnClick()
等を設定します。
・EpoxyRecyclerView
にController
をセット
最後に、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
, description
がXML
ファイルで書いた<data />
タグの中身です。
このようなViewのブロックを順に並べることで、その順通りに表示されます。
4. EpoxyRecyclerView
にController
をセット
残りは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が発生する際)は注意が必要です。
最後に
複雑な画面を作る際に、ScrollView + Fragment
で実装するとパフォーマンスも可読性も落ちてしまう場合があります。
複雑な画面を実装するのに苦労している時は、EpoxyやGroupieなどのRecyclerView
を活用するライブラリを用いて、より簡潔に簡単に作って楽になりましょう!
(次はMvRxについて書こうと思います....)
AVPlayerについて調べた - iOS
AVPlayer周りのドキュメントを読んだので、
AVAsset
、AVPlayerItem
、AVPlayer
について書いてあること自分に分かりやすいように書き起こしました。
動画を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を用いて表示を行う。(AVPlayerViewはmacOSのみ)
AVPlayerLayerを用いた方法
AVPlayerLayerを用いて表示を行う。
AVPlayerViewControllerのようにコントローラのViewは無いので、自前で設定する必要がある。
Topics
生成方法
init(url: URL) init(playerItem: AVPlayerItem?)
再生管理
func play() func pause()
言わずもがな。
var rate: Float
再生速度。(rate = 0
⇔pause()
)
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)
監視を外す
参考
今までの自分とこれからの自分
今年もあと少しということで、今年何を得てどんな反省をしたのか、どのように生きていくのかを振り返ってみました。駄文になってしまいましたが、読んでいただければ幸いです。
今年何をしたのか
Googleカレンダーを見返してみたら、あの時頑張ってコード書いてたなとか辛かったなとか、色々な感情が出てきて泣きそうになりました。 年々涙腺が緩くなっているのを切実に感じています...
それぞれについていろいろエピソードを書いていたのですが、長くなってしまうので割愛して言いたいことだけまとめておきます。
インターンでiOSアプリを開発
去年の10月あたりから友人からフリーランスとして働いている方を紹介してもらい、その人の元でiOSアプリの開発を行なってきました。
業務としては、顧客から受け取った仕様やデザインをコードに落とし込みアプリにするという事を行なっています。
この1年で自分がどのようなコードが綺麗で読みやすいか、どんなコードはダメかという判断を少しですが出来るようになり、それがライブラリの選定にも繋がったことに成長を感じることができました。
ブログでアウトプットを始める
学んだ事をアウトプットして理解を深める。アウトプットするネタを探すために学ぶ。という俗に言うアウトプット駆動学習をはじめました。
あまり閲覧数は増えてはいないのですが、知識を整理する事で理解を深めることができたり、曖昧なところもより突っ込んで学ぶようになり、順調に効果を得られていると思います。
自分でインプットした知識をアウトプットする際に、その知識が正しいのか、より良い方法はないのかと振り返る機会が出来るので、より深いところに落とし込むことが出来ます。
Androidの勉強会にLTとして参加
アウトプット駆動学習を始めたことをきっかけに、ノリと勢いで勉強会のLT枠に参加させていただきました。
大勢の前で話すことが苦手だったのでその改善と、エンジニアの方とコネクションを作るという目的があったのですが、思った以上に成果があったので嬉しかったです。
特に今年はインターンでiOS漬けだったので、Androidの最新技術をpick upするという意味でもLTは良い機会でした。
就職活動
自分にとって就職活動はとても辛いものでした。
- 進路を決めるのが遅くなってしまいあまり余裕を持って行えなかった
- 自分の行動理由を深掘りして相手に伝えるということが苦手だった
- そもそも行動理由の深掘りを疎かにしていて自分の中で固まっていなかった
- 相手にわかりやすく自分のことを伝えることが苦手だった
これらが辛かった理由です。自分は 行動理由 ≧ 技術力
と考えていたのですが、これは圧倒的に間違いで 行動理由 >>>> 技術力
であり、
そのことに気付かず、技術力を磨いては人事面接で落とされる。という沼にはまっていました。
なんとか内定をいただくことは出来ましたが、行動理由についてもっと考える時間を取っていれば就職活動も違った内容になっていたと思います。
今までの自分
今年は主に技術の深掘りや深掘る方法を学び、得た知識のアウトプットを行うことに注力してきました。
その事自体はとても良い事で、身になった事なのですが、どうしてそれをやる必要があったのか?と聞かれると返答に困ってしまいます。
こうして一年の振り返りをしてみると、以下の点が問題だと気づきました。
- 自分が感覚的に必要だと思うものをただ闇雲に行なっていた
- "新しい技術を試した"というツイートや記事を見るたび自分もやらなくては、と焦っていた
- 具体的な目標を設定していなかったため達成度が分からない
- 優先順位が
学ぶ時間 > 振り返る時間
- 全てにおいて時間がかかり過ぎている
これでは自分が何をどうして行なったのか、何に向かって進んでいるのか、どれだけ進めたのか
ということが曖昧になってしまい、自信が持てず心理的安全性が欠けてしまいます。
実際に自分は自信が全くなく、そのことも起因して面接でも上手く話せなかったのだと思います。
これからの自分
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
として実装していたものをReactive
のExtension
として記述することをRx連携と呼ばせていただきます。
どうしてRx連携するの?
- プロダクトがRxをゴリゴリ使っているから
- コードが綺麗になる
プロダクトがRxSwiftをゴリゴリ使っている
自分が開発しているアプリではアーキテクチャがMVVMで、ViewとViewModelとのつなぎにRxSwift(RxCocoa)
を使っています。
そのため、RxSwiftで用意されていない場合は自分で連携させる必要があります。
コードが綺麗になる
コードの量が減るかどうかはプロダクトによりますが、Rxを使うと圧倒的に簡潔に分かり易くかけると思います。
どれくらい綺麗になるか、サンプルコードを書いてみたので比較してみてください。
Rx導入前
Rx導入後
一番メリットとして感じるのは、今まで分離して関数を用意していたのがclosure
として記述できることだと思います。
結果的にUIViewController
から煩わしいDelegateなどのコードが減るので、見通しもよくなります。
どうやって実装するの?
自作Viewを作るときに作成したDelegate
を使って、DelegateProxy
(Proxy: 代理)を作成し、それを使ってReactive
のExtension
としてDelegate
の中身を実装します。
今回は、内部にUITextField
を持っているFilledTextField
という自作Viewを使って、Rx連携する手順を紹介します。
コードはGithubのgist上げているので参考にしてください。
例として自作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の実装
Reactive
のExtension
として実装していきます。
DelegateProxy
のmethodInvoked(_:)
を使って、Observableに変換します。
params
はDelegateの引数を指します。
完成!!
終わりに
一番FatになりがちなUIViewController
のコードを綺麗に書くことができ、コードの可読性も向上できました。
RxSwiftを使いこなすことは難しいですが、上手く使えるようになればコードを綺麗に書けるようになると思います。
使い倒してどんどんコードを綺麗にしていきましょう!
SwiftyXMLParser + Moya + RxSwift 使ってみた
iOSアプリを開発中にXMLを返すAPIを使うことになったので、XML形式のものをどうマッピングするかを忘れないように記事にしました。
なんでSwiftyXMLParserを使うの?
ではなぜXMLParserを使わないかというと、
- 使い勝手が悪い
- Rxと組み合わせて使うとなると色々大変
だからです。
サードパーティのXMLParser Libraryは何個かありますが、今回は
- Moya (Alamoifre) と一緒に使う
- Rxをゴリゴリ使っているプロジェクト
という状況だったのと、Star数を鑑みてSwiftyXMLParserを選択しました。
SwiftyXMLParserの使い方
導入の仕方、それぞれの要素へのアクセスの仕方については、READMEを参考にしてください。
ここでは、READMEに書いていない実際に使うときにはどうするかを書いていきます。
Modelへのマッピング
例としてradikoの番組を取得するAPIを使ってみます。(最近ではXML形式のレスポンスを返すAPI少なくて、探すの大変でした....)
<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()
を使うことで、綺麗に書くことができます。
完成!