Quickでテストする
Swiftのテストフレームワークとして有名なQuickを使ってみます。
インストール
Carthageに
github "Quick/Nimble" github "Quick/Quick"
を記述し
carthage update
でインストールできます。CocoaPods,Git submodulesでもインストール可能です。
Nimble
はMatcherでQuickで使われています。
テストを書いてみる
簡単な例ですが、こちらのようにRSpec風に書くことができます。
describe("sample"){ it("1 + 1 = 2"){ expect(1+1).to(equal(2)) } }
非同期処理をテストする場合は以下のように書きます。
describe("API TEST") { it("search swift"){ var isSuccess = false APIClient.search("swift"){result in switch result { case .Success: isSuccess = true case .Error: break } } expect(isSuccess).toEventually(beTrue(), timeout: 5, pollInterval: 1, description: "") } }
toEventually
を使用することでタイムアウト時間、pollingの間隔を指定できます。
またwaitUntil
を使用する方法もあります。wailUntil
はデフォルトのタイムアウト時間が1秒になっています。
it("search swift2"){ var isSuccess = false waitUntil{ done in APIClient.search("swift"){result in switch result { case .Success: isSuccess = true case .Error: break } expect(isSuccess).to(beTrue()) done() } } }
ReSwiftを使ってみる
ReSwiftというRedux-likeなswiftライブラリがあります。面白そうなので使ってみました。
ReSwiftの基本概念
ReSwiftではState,Store,Action,Reducerの4つの概念があります。
State
いわゆる状態です。stateはActionによってその状態を変化させる事ができます。
Store
アプリケーション全体のStateを管理します。Actionを受け取るとReducerにそれを渡してStateを更新し、Stateのサブスクライバーに対して変化を通知します。
Action
Stateをどう変化させるかを宣言します。
Reducer
StateとActionから新しいstateを作成します。
試してみる
習うより慣れろで試しに使ってみましょう。Qiitaのapiを使って検索するだけという簡単なアプリを作ります。こちらのサンプルを参考にしています。 完成したものはこんな感じです。コードはこちらにあります。
Stateを作る
まずStateを作ってみます。QiitaAPIState
はQiitaのapiを叩いた結果のsarchResults
という状態を持ちます。sarchResults
はqiitaの記事のモデルPost
の配列となっています。Result型になっていますが以降無視します。
struct QiitaAPIState { var sarchResults: Result<[Post]>? } protocol HasQiitaAPIState { var qiitaAPIState: QiitaAPIState { get set } }
AppState
はアプリ全体のStateを実装します。先ほどのHasQiitaAPIState
をここで適合させます。アプリが他にもStateを持つ場合はそれぞれStateを作ってAppState
に実装していきます。
struct AppState: StateType,HasNavigationState,HasQiitaAPIState,HasQiitaSceneState { var navigationState = NavigationState() var qiitaAPIState = QiitaAPIState() var qiitaSceneState = SearchQiitaScene.State() init() { } }
Actionを作る
QiitaAPIState
をどのように変化させるかというActionを作成します。ここではPost
がどう変わるかを宣言します。
struct SetPostSearchResult:Action { let result:Result<[Post]> init(_ result:Result<[Post]>) { self.result = result } }
Reducerを作る
ReducerはActionのSetPostSearchResult
を使ってStateのQiitaAPIState
を変化させます。handleAction
内でActionがSetPostSearchResult
であればsetPostSearchResult
が実行されます。ここでsarchResults
が更新されます。
struct QiitaAPIReducer:Reducer{ typealias ReducerStateType = HasQiitaAPIState func handleAction(state: ReducerStateType, action: Action) -> ReducerStateType { switch action { case let action as SetPostSearchResult: return setPostSearchResult(state, result: action.result) default: return state } } func setPostSearchResult(var state:ReducerStateType,result:Result<[Post]>)->ReducerStateType { state.qiitaAPIState.sarchResults = result return state } }
Storeを作る
StoreはReducerとStateを保持します。先ほど作成したAppState
,QiitaAPIReducer
が初期化時に渡されています。Storeはアプリに一つだけ?になるようです。
let mainStore:MainStore = MainStore(reducer:CombinedReducer([NavigationReducer(),QiitaAPIReducer(),SearchQiitaScene._Reducer()]), appState: AppState(), middleware: [loggingMiddleware])
その他部品を作る
これでRedux的な部分は作成しましたのでこれらをUIViewController等の慣れ親しんだ所に流し込んでいきます。Stateの変化の通知を受け取るためにStoreSubscriber
に適合させ、subscribeします。
class ViewController:UIViewController,StoreSubscriber { let store:Store = mainStore override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) store.subscribe(self) } }
subscribeするとnewState
でStateの変化を受け取ることができます。
func newState(state:AppState){ if let searchResults = state.qiitaAPIState.sarchResults { switch searchResults { case .Success(let posts): self.posts = posts self.tableView.reloadData() case .Error(let error): print(error) } } }
最後にActionを発行する関数searchPosts
を追加します。ActionCreatorはその名の通りActionを作成するための処理を記述します。
検索結果が返ってくるとstore.dispatch()
でstoreへActionを渡します。ここでは検索結果の配列です。正常に動作してれば配列に検索結果の要素が入っているでしょう。
storeはこれを受け取るとReducerへStateとActionを渡します。この時点のStateは以前の検索結果です。
ReducerはActionで宣言された変化をStateへ適用し新たなStateを発行します。つまり以前の検索結果を新たな検索結果に置き換える事になります。
この新たなStateがサブスクライバーに通知されます。これが先ほどのnewState()
です。
struct QiitaAPIActionCreator{ func searchPosts(query:String)->ActionCreator { return {state,store in if query.isEmpty { store.dispatch(SetPostSearchResult(Result(value: []))) return nil } APIClient.search(query){result in store.dispatch(SetPostSearchResult(result)) } return nil } } }
最後に
Reduxという概念が何かもわからないまま使ってみました。
気になる点といえばstateが更新されると全てのサブスクライバーのnewState()
が実行される点でしょうか。
他にはStateが増えると複数のStoreが必要になるのか?という疑問がありますが、まずはReduxについて調べてみます。
Terminalから翻訳しよう
変数名やメソッド名をどうしようかと悩んでgoogle翻訳等を使うことがあると思います。
ですがブラウザに移動するのは面倒です。ターミナルで翻訳できないかと思い、探したところ便利なものを見つけました。
それがこちらです。
soimort/translate-shell · GitHub
Homebrewでインストールできます。
brew install https://www.soimort.org/translate-shell/translate-shell.rb
使い方は簡単でtransコマンドの後に翻訳したい言葉を入力するだけです。
$ trans 翻訳 翻訳 (Hon'yaku) translation Definitions of 翻訳 [ 日本語 -> English ] noun translation 翻訳, 訳書, 翻訳物, トランスレーション deciphering 解読, 翻訳
英語から日本語にするには以下のようにtrans :jaとすればokです。
$ trans :ja translation translation /transˈlāSHən,tranz-/ 翻訳 (Hon'yaku) Definitions of translation [ English -> 日本語 ] noun 翻訳 translation, deciphering 訳書 translation 翻訳物 translation トランスレーション
2015年振り返り
2015年を振り返る。
ぱっと思いつくトピックとそれについて一言をまとめる。
プログラミング
学生として
就活
就職先がきまった。来年4月からは社会人としてがんばるぞい。
研究
自分の興味あることを深く調べるという事がどれほど楽しく大変かよく分かった。
知れば知るほど分からない事が増えていく。
プライベート
家庭教師
中学生に数学を教えた。約10歳下の子とたくさん話してジェネレーションギャップを感じた。
身内ではない中学生とがっつり話す機会はこれからはなさそう。純粋に楽しかった。
旅行
京都大阪に一人でいった。
自由に行動できる身軽さはありがたいが、感情を共有できないことは物足りないか。
伏見稲荷大社の千本鳥居に夜行った。怖い。これに尽きる。
ビジネスホテル
いろいろあって宿泊する機会が多かった。
ビジネスホテル12件、カプセルホテル3件で日数で言うと一ヶ月ほど。
やはり家が落ち着くという結論に達した。
来年は
- アウトプットしていく。
- 外出する。
名言アドベントカレンダー
名言アドベントカレンダー23日目の記事です。
紹介する名言はこちらです。
練習は嘘をつかないって言葉があるけど、頭を使って練習しないと普通に嘘つくよ。
— ダルビッシュ有(Yu Darvish) (@faridyu) 2010, 6月 11
Twitterなのでダルビッシュ選手は軽い気持ちで投稿したかもしれません。
ですが非常に重い言葉だと思います。
ただ長時間やる事が目的の練習や疲れて練習した感がでるだけの練習(スポーツの場合の脳筋的な?)ではなく何が目的でなぜこの練習をするのか、何を意識しながら練習すれば良いのかを考えてやろうという事でしょうか。
「努力は嘘をつかない」や「努力は必ず報われる」といった努力論とダルビッシュ選手の努力論。
どうなんですかねぇ。