これはなに
Timezoneを変更する方法
- macosのtimezone設定は、environmentのTZでは有効にならないので注意
- これを知らずにいろいろ試して3時間ぐらい時間を無駄にした 😭
まとめ
- macosのtimezone設定方法は、
sudo systemsetup -settimezone "$TIMEZONE"
だぞ!
Only Build pull requests
という設定を有効にするのが推奨されているgem "fastlane", "2.94.0" gem "danger", "5.5.13" gem "danger-swiftlint", "0.16.0"
bundle exec danger init
するとDangerfileができる# RPの差分範囲外に対する指摘はすべて無視 github.dismiss_out_of_range_messages # -------------------- # swiftlint # -------------------- swiftlint.config_file = '.swiftlint.yml' swiftlint.lint_files inline_mode: true # -------------------- # pr title # -------------------- warn('このPRは作業中です') if github.pr_title.include?("WIP") || github.pr_title.include?("wip") # -------------------- # base branch # -------------------- is_to_master = github.branch_for_base == 'master' is_to_release = github.branch_for_base.include?("release") is_from_release = github.branch_for_head.include?("release") is_from_development = github.branch_for_head.include?("dev") || github.branch_for_head.include?("development") warn('master へマージできるのは release branch のみ(緊急時はOK)') if is_to_master && !is_from_release warn('release へマージできるのは dev branch のみ(緊急時はOK)') if is_to_release && !is_from_development # -------------------- # milestone # -------------------- warn('このPRにマイルストーンを設定してください') if github.pr_json["milestone"].nil? # -------------------- # assignee # -------------------- warn('このPRにアサインしてください') if github.pr_json["assignee"].nil?
danger( danger_id: "unit-tests", dangerfile: "tests/MyOtherDangerFile", github_api_token: ENV["GITHUB_API_TOKEN"], verbose: true )
For now, rebuilding is the only suggestion I have while our engineers look into this further.
https://discuss.circleci.com/t/circle-pull-request-not-being-set/14409/11discuss.circleci.com
https://discuss.circleci.com/t/trigger-new-build-on-pr/4219discuss.circleci.com
Only Build pull requests
を有効にしてPR作成時のスキップを回避しよう!!具体的には言えないのでざっくりと説明したい
ここからが本題で理由は2つある。
転職はしたくないけど別のことはしたい。そう考える人は多いと思う。 大きな会社だと部門の移動とかで解消できる悩みかもしれない。 でも、今の本業だとそれは不可能なので副業という選択を取るしかなかった。
リモートで開発したいのは自分が将来、東京以外の場所で働く可能性があるから。 地元が田舎で親の体調によっては将来、実家に戻らざるをえない状況になるかもしれない。 そのリスクが問題になる前に対処できるスキルを持ってないと心配だなと思った。
でも、本業でリモート開発するには色々と越えないといけないハードルが多い。 そこで副業で出来ないかなと思ってたら幸運にもリモートで副業ができるようになった。
少し前に connpass で副業に関するイベントがあって参加した。
engineer-parallel-work.connpass.com
このイベントでは副業中のエンジニアが副業に関する知見をLTで発表してた。 内容をまとめると以下のような感じだった。
Webサービス等で副業を開始した人がいなかったのは、この市場が発展途上だからかもしれない。 またイベントのLTで発表できるような人たちなので、そこもバイアスがかかっているだろう。
でも、副業をお願いしたい会社側も見ず知らずのエンジニアに頼むよりは、知人経由での紹介の方が安心なのでやっぱり知人経由だと思う。 そして、自分も知人経由で副業を開始できたので間違いではないと思う。
特に、「周囲に副業したいことをアピールし続けること」 は一番大事というか、これをしないと始まらないので副業したい人は行動しよう。
最初は副業のメリットとデメリットで書こうと思ったけど、明確にメリットとデメリットに分けられないのでやめた。 メリットとデメリットが混在するというか、その狭間で右往左往するような感じ。 要は、読み手の立場によってどちらにも取れるので知見という言葉で濁すことにした。
当たり前すぎるが、これが面白いから副業してる。
本業は自分が書いたコードが多いし環境にも慣れている。 すると、慣れたことでやり続けてしまって成長の機会が減る。
自力で自分の殻を破って成長できる人は素晴らしいが、僕は環境を変えて半強制的に殻を破らざるをえない場所に身を置く方を選びたかった。 それなら転職すればいいのではと思うかもしれないが、そこまでの気持ちが今はないのは既に書いた。
あと、本業と副業で別々の仕事をすると、お互いを比較することで色んな改善策がポツポツと頭に浮かんでくる。 何かと何かを比較してそこに生まれる差分でいろんな気づきを得られるのも大きいと感じている。
もちろん、GitHub上のOSSを見ることで似たような体験を得ることは十分可能なんですが。
副業を始めるのが面倒だと思う理由は、確定申告とか請求書とかがイマイチよくわからないからだと思う。 自分もここが心配だったが、知人の助けにより今のところは問題なさそうである。
要するに、ネットバンクで口座を開設してfreeeで開業届けを出して請求書もfreeeで作成するでOKだった。 まだ確定申告という難関を突破したことはないが、とにかく本業以外の収入を計算しやすいようにひとまとめにしておくことが大切で、あとは詳しい人に聞けばなんとかなる。
今までfreeeを使ったことなかったけど、色々便利ですごいなと思ったし、開業届けや確定申告などを自分事として考えられるようになったのは嬉しい。 学ぶ喜びを得ている。
いつから稼働して、何時に完了して、何をやったのかをわかりやすく伝えないと、副業先の会社の人は心配になる。 当たり前だけど大切なことである。
稼働の開始と終了はslackでやり、開始時に今日の作業内容を簡単に書く。終了時には作業の進捗を書く。 作業の進捗や結果がわかりやすいように、WIPつけたりPRにはスクショなども貼る。
大きめの修正や仕様に大きな変更がありそうなとき、相手側の要望とその対応をまとめてissueにする。 正確にissueにまとめられてると、リモートでもコミュニケーションは比較的円滑にすすむ。 自分以外の人も副業でリモートしてるので、他のメンバーもお互いの作業が把握しやすくなる。
リモートだからとか関係なく、普通にやるべき事だけどその大切さを改めて実感してる。
最後は承認欲求が満たされるてやつだ。
副業を必要としている会社は人手が足りてないので、不具合とか色んな問題が放置されがち。 ほんの数分でなおせそうな不具合とかも残ってるので、すぐに対応すると喜んでもらえる。 当たり前すぎるけどやってて楽しい。
今年のiOSDCの開催が発表されたので、発表する内容を準備しないと・・・。
@tarunonさんが発表中にツイートしてくれた。5分なので説明を端折ったが、確かに聞いてた人は疑問に思うところだった。@tarunonさんに感謝。
たぶん、私の発表内容だと思って返信します。説明不足でしたが、こういう制約付けてtextViewがスクロールします。https://t.co/p93RLvg5m7
— ストクロ (@kurotyann9696) 2018年3月13日
webviewでtextViewを試したことないので、今度試してみます!
— ストクロ (@kurotyann9696) 2018年3月13日
なお、今回の発表のサンプルアプリは複数のレイアウトパターンを確認したかったのでサンプルではあるけど、結構いろいろ作り込んでる。iOSのレイアウトをコードで書く人、storyboardの人、両方からのツッコミを期待してる。
また、実はこのテーマにする前の構想では「コードでレイアウトする際の利点・欠点」について発表する予定だった。でも、先日の TRY! SWIFT AFTER TALKS の1日目で @SatoshiN21さんが言いたいことの9割を言ってくれたのでお蔵入りにした。
先程の登壇資料をこちらにアップしました!
— satoshin21⏱ (@satoshin21) 2018年3月8日
ご清聴有難うございました#tryswift_aftertalks
「World of No Interface Builder」https://t.co/0ckNdp944o
なぜ、9割かと言うとレイアウトはライブラリを使わず、標準のNSLayoutAnchorで書く方が今後の開発にはプラスになると個人的に思っているから。
Cartography や SnapKit や TinyConstraints など優れたAutoLayoutのDSLがあることは知ってる。でも、AutoLayoutそのものの学習コストがそこまで低くないにも関わらず、DSLになると新メンバーの参入障壁になると思った。もちろん、慣れの問題だし短く書けることに越したことはない。
それ以外は同じような理由でコードでレイアウトを書いてる。
potatotipsは発表時間が5分と短い。だから、自己紹介の後すぐに結論をまとめたスライドを出した。これは正解だったと思う。でも、早口で噛んだりちょっと時間オーバーしたのが悔しい。potatotipsでは、これまでに有名なiOSエンジニアがたくさん発表してるので結構プレッシャーだった。
優れた発表者は総じて明瞭で簡潔なスライドできっちり時間内に説明してた。今回のpotatotipsでは@ra1028fe5さんの発表が良かった。*1 *2
やっとprocessing終わってました
— Ryo Aoyama (@ra1028fe5) 2018年3月13日
200倍高速化は極端なケースですが配列パフォーマンスについてのtipsの資料です🙇https://t.co/PhITPzQTKF#potatotips
自分の発表は勉強会のハッシュタグを追っていると、勉強になるとツイートしてくれた人がいたので安心した。前回の発表よりも反応があったので嬉しい。一歩前進した。
でも相変わらず、資料づくりは大変だ。前回に比べて発表時間が半分の5分で、しかもスライドのテーマとか細いところは前回の発表で固まったので内容だけ考えればよいはずだった。それでもサンプルアプリの作成、公式ドキュメントの下調べ、社内のiOSエンジニアにレビューして欲しいので発表前日には完成させる等、やること多い。やって無駄なことは一つもないけど、5分の裏側はいろいろ大変だった。発表者の全員がそうだと思う。
発表資料の作成もAutoLayoutと同じで慣れの問題だろうか・・・。
発表てリスキーだなと思う。良い発表ができれば人の役に立ち有名になるだろうけど、間違った情報を発表をすると悪いイメージを与えてしまう。しかも、その場での訂正が難しいのでブログやQiitaよりも影響が大きい。社外だと自分のバックグラウンドを知らない人ばかりなので、共有していない情報が多過ぎてあらぬ誤解を与えるかもしれない。
だから可能な限り正しい情報を発表するため、資料作成には非常に時間をかける。何度もドキュメントを見て、本当に正しいのかテストを繰り返す。これだとブログやQiitaで書いた方が、間違ったとしても訂正して後で知らせれるかもしれないし、コメントや編集リクエストも来るかもしれない。時間や場所に縛られない。発表で緊張して精神をすり減らす必要もない。なぜ、みんなは知らない人の前で発表したがるのだろうか。
大げさに書いたけど、そんなことを自分はどう感じるのか確かめたくて、実際に社外で発表してみた。結果は可もなく不可もなく。達成感はあった。今回発表した内容についてさらに詳しくなった。今回の発表内容がどれぐらい役に立ったのかわからないので成果はそれだけだ。それだけと言うと、言い過ぎな気もするが。もしかしたら、自分が気づいていない成功や失敗があったのかもしれない。
発表のハードルを上げ過ぎている!少しぐらい間違ってもいいのでは?という意見もあると思う。でも、間違った情報を流すことは避けたいし、少しでも役に立つ内容にしたい。そんなモヤモヤを糧にして技術力を上げていく方法をとるのもいいのかもしれない。既にどこかの誰かが発表駆動学習とか言ってそうだ。
./TDDForSwift/Source
に、テストは ./TDDForSwiftTests
に配置した// ① 金額に数値を掛けて金額を算出する $5 * 2 = $10
// ② 異なる2つの通貨を足し、為替レートに基づいて換算された金額を算出する $5 + 10CHF = $10 (レートが USD:CHF = 2:1 の場合)
git log --oneline --reverse --pretty=format:"%s"
でコミットメッセージを章ごとに表示第1章 MoneyTestを作成 第1章 Dollarクラスを作成 第1章 Dollarクラスにイニシャライザ作成 第1章 Dollarクラスにtimesメソッド作成 第1章 Dollarクラスにamountプロパティを作成 第1章 テストを通すためにamountの初期値を10にする 第1章 重複の削除のためにamountの値を具体的にする 第1章 Dollarのイニシャライザでamountプロパティの初期値を設定する 第1章 timesの重複を削除するために5をamountに変更 第1章 timesの2のベタがきをmultiplierに変更 第1章 *=演算子をつかって重複を削除
_
をつけて呼び出し元で省略させている第2章 Dollarの副作用でオブジェクトの状態が変更されてしまうが理想のテストを書く 第2章 もしもtimesメソッドから新しいオブジェクト返るなら 第2章 timesから新しいDollarオブジェクトを返せるような実装に変更する 第2章 timesからDollarオブジェクトを返す
第3章 equalsメソッドのテストを作成 第3章 equalsメソッドを作成してとりあえずtrueを返す 第3章 2つ以上の実例を作るために「$5 != $6」のテスト作成
as?
)にするが、できるだけ書籍どおりにしたくて暗黙的にアンラップ(as!
)させた第4章 Dollar同士を比較するようにしてテストの意図を明確にする 第4章 もう一方も同じようにDollar同士を比較する 第4章 一時変数のproductは不要なので削除してインライン化する 第4章 amountプロパティはprivateにして自分自身しか参照しなくて良くなった 第4章(番外編) Javaのequalsに相当する、Equatableの「==(lhs: Self, rhs: Self) -> Bool」を実装してテストを通す
第5章 ひとまずDollarのテストをコピーして、Francのテストを作成 第5章 DollarクラスをコピーしてFrancクラスを作成
第6章 親クラスとしてMoneyクラスを作成 第6章 親クラスのMoneyにamountプロパティを引き上げる 第6章 Francの等価性のテストを忘れてたので追加する 第6章 FrancクラスもMoneyクラスを継承する 第6章 親のMoneyクラスにあるのでamountプロパティを削除 第6章 equalsメソッドのキャスト型をMoneyに変更する 第6章 Francクラスのequalsメソッドも親クラスのMoneyクラスに引き上げる
第7章 DollarとFrancは別の通貨であるというテストを追加 第7章 金額だけでなく、オブジェクトの型も同じなら等価とする
第8章 timesメソッドの返値をDollarとFrancで同じにする 第8章 DollarのFactory MethodをMoneyに作成する 第8章 Dollarクラスへの参照を減らすために型宣言を親クラスのMoneyに変更する 第8章 DollarのFactory Methodの返り値の型をMoneyに変更する 第8章 Dollarを参照している箇所をFactory Methodに置き換える 第8章 Dollarと同じくFrancもFactory Methodを作成してFrancの参照をなくす
fatalError("Abstract")
で対応第9章 通貨(Currency)のテストを追加する 第9章 通貨名をサブクラスに格納する - Swiftではクラス内に同名で同型のプロパティとメソッドは定義できないので_を接頭辞につける 第9章 currencyを親クラスのMoneyに引き上げる 第9章 Francのイニシャライザで通貨名を渡せるようにする 第9章 Francのイニシャライザを呼び出している箇所のエラーを解消する 第9章 自身のイニシャライザを呼ばずに、親クラスのFactory Methodを呼ぶ 第9章 FrancのFactory Methodから通貨名を渡すように変更する 第9章 イニシャライザのcurrencyをプロパティに代入する 第9章 Francと同じ変更をDollarにも行う 第9章 サブクラスのイニシャライザを親クラスに引き上げる
第10章 timesメソッドで返すクラスを自身のイニシャライザを使って行う 第10章 ベタ打ちではなく、currency変数に置き換える 第10章 Franc型とMoney型の区別が必要なのか判断するためにtimesでMoneyを生成して返すように変更してみる 第10章 Moneyのtimesを抽象メソッドではなく具象メソッドに変更する - Swiftの場合、XCTAssertEqual failed: ("Optional(TDDForSwift.Franc)") is not equal to ("Optional(TDDForSwift.Money)") のエラーになる 第10章 エラーメッセージを少しマシなものにするためにデバック出力を編集する - SwiftはCustomStringConvertibleのdescriptionで、JavaのtoStringと同じ処理を実現できる 第10章 エラーになる前の状態に戻す 第10章 FrancとMoneyが等価であることをテストする 第10章 equalsメソッドでクラスではなく、通貨名を判定するように変更する 第10章 再びFrancのtimesメソッドでMoneyを返すように変更してテストを通す 第10章 Francと同じようにDollarもtimesでMoneyを生成して返すように変更する 第10章 timesメソッドを親クラスのMoneyに引き上げる
toString
を実装する第11章 Factory Methodでサブクラスを初期化する必要はなくなったのでMoneyで初期化する 第11章 Dollarクラスを参照するコードはないので削除できる 第11章 等価性比較の過剰なテストは削除する 第11章 Francクラスの動作を確認するだけのテストとなったため、Francクラスと同時に削除する 第11章 掛け算の振る舞いをクラスごとにテストする必要はないので、不要な方の掛け算の振る舞いテストを削除する
第12章 足し算のテストを追加 第12章 plusメソッドを実装する 第12章 為替レートをもとに銀行が通過を換算(reduced)するようなテストを作成する 第12章 銀行を初期化する 第12章 2つの和を表現するExpression(式)を作成する 第12章 5ドルを作成して、$5 + $5 のテストを完成させる 第12章 Expressionを作成する - SwiftはprotocolでJavaのinterfaceを表現する 第12章 plusメソッドはExpressionを返すように変更する 第12章 MoneyをExpressionに準拠させる 第12章 Bankクラスを作成する 第12章 空のreduceメソッドをBankに実装する 第12章 ひとまずテストを通すためにreduceで10ドルを返す
第13章 足し算のaugend(被加算数)とaddend(加数)のテストを書く 第13章 Sumクラスを作成する 第13章 plusメソッドでSumを返す 第13章 Sumクラスにイニシャライザを定義する 第13章 SumクラスにExpressionを準拠させる 第13章 Sumのイニシャライザでプロパティに値を代入してテストを通す 第13章 Bankのreduceのメソッドで足し算の結果をテストする 第13章 BankのreduceメソッドでSumのプロパティを足し合わせた値を使ってMoneyを生成して返す 第13章 BankのreduceをSumクラスへ移動する 第13章 BankのreduceメソッドにMoneyを渡したときのテストと、Moneyにキャストする処理をreduceメソッドに追加する 第13章 Moneyにもreduceメソッドを定義する 第13章 MoneyにもSumにもreduceメソッドがあるので、Expressionにreduceを定義する - Javaはinterfaceのメソッドはpublicになるが、Swiftはデフォルトのinternal(同モジュール内まで公開)で良い 第13章 Expressionにreduceを引き上げたのでキャストや型チャックが不要になったので削除する
if let as? 型
でSwiftぽくやる_
をつけたが、Swiftはキーワード引数を表現できる第14章 2フランを1ドルに換算するテストを書く 第14章 ビルド通すためにBankにaddRateメソッドを空で実装する 第14章 フランをドルに換算するテストを通すために、Moneyのreduceメソッドでレートを計算する 第14章 為替レートをBankで管理できるようにreduceの引数にBankを追加する 第14章 Bankで為替レートの計算を行うrateメソッドを定義する 第14章 為替レートの計算をBankに任せる 第14章 配列の中身を等価だと扱ってくれるのかテストする - Swiftは等価だと判断するが、Javaは等価だと判断しない 第14章 先ほどのテストは削除して、Pairクラスを作成する 第14章 Pairクラスをハッシュのキーとして使うために必要なメソッド定義する - SwiftはHashableに準拠すれば辞書型(ハッシュ)のキーに使用できるクラスになる 第14章 Bankで為替レートを格納するローカル変数を定義する 第14章 為替レート設定時にレートを格納する 第14章 為替レートを検索されたときに対象のレートを返す - Swiftの辞書型(ハッシュ)から値を取り出すとき、値は必ずオプショナル(?)なので暗黙的アンラップ(!)する 第14章 同じ通貨を換算した場合にエラーになるので、同じ通貨を為替レートから検索する場合のテストを追加する 第14章 同じ通貨の場合は為替レートは等倍なので1を返す
第15章 異なる通貨を足し算して、どちらかの通貨に換算して結果を出すテストを書く 第15章 ひとまずビルドを通すために、Money型で宣言するがテストは通らない 第15章 Sumクラスで被加算数(augend)と加数(addend)を換算してテストを通す 第15章 被加算数と加数の型をExpressionに変更する 第15章 さらにイニシャライザの引数の型もExpressionに変更する 第15章 Moneyのplusメソッドの引数もExpressionに変更できる 第15章 Moneyのtimesメソッドの返り値の型もExpressionに変更できる 第15章 テストケースもMoneyのplusメソッドに渡す型をExpressionに変更する 第15章 fiveBucksの型もExpressionに変更する 第15章 Expressionにplusメソッド引き上げる 第15章 Expressionに準拠しているSumにもplusメソッドを定義する - 書籍どおりnilを返したいが、オプショナル型になると別メソッドと認識されるので、addedをとりあえず返す 第15章(番外編) 強制キャストしてMoneyにしないとテストが通らない - Swiftの==演算子はEquatableに準拠する型しか比較できず、Expressionはプロトコルなためできない
第16章 Sumクラスのplusメソッドのテストを書く 第16章 SumクラスのplusメソッドからSumを生成して返す 第16章 Sumクラスのtimesメソッドのテストを書く 第16章 SumクラスのtimesメソッドもSumを生成して返す 第16章 被加算数と加数はExpression型にしたので、Expressionにもtimesメソッドを引き上げる
この記事はiOS2 Advent Calendar 2017の5日目の記事です。
今年のiOSDCで注目を集めた iOSDesignPatternSamples を参考にしてiOSのデザインパターンを勉強してる。このiOSDesignPatternSamplesは、GithubKit というライブラリを使って開発されている。
今回は、このGithubKitが面白かったので自分も真似して作ってみたという話。しかし、ただ真似するだけではコピペになるし学びが薄いので、APIをQiitaに変更して開発してみた。
結論から先に言うと、とても勉強になったし、@marty-suzukiさんには感謝しかない。
具体的に良かったのは、「SwiftやiOSの新機能をさっと試すのに適当なサイズのサンプルアプリが欲しいとき、◯◯Kitというライブラリをフレームワーク化しておくとサンプルアプリを作りやすい」 ことに気づけたところ。
今までは個人で開発したアプリで新機能を試してたが、一度リリースしてしまうと既存ユーザーへの影響やデザインパターンとの調整を考えて、新機能を試すのが面倒になり何もしないことが多かった。
かといって、ゼロからサンプル用のアプリを作るのも面倒で、サンプルに適した形にするのもまた面倒だ。
結局、なにも試してない状態になってた。要するに、サボってた。
でも、この〇〇Kitとかを作るとcarthageでプロジェクトに取り込んでささっと適当なサイズのアプリにして新機能を試せていいかなと思い始めてる。
もちろん、試したい機能によって下準備とか別に必要だけど、それもフレームワーク化して 「新機能をすぐに試せる環境作りをしておくのが大切なんだ」 と実感した。
本題に入る。GithubKitはSwift3の時代に開発されてたようで、Swift4の機能は使われていない。
というわけで、QiitaKitForSampleではSwift4の新機能の Codable
を使ってAPIのリクエスト、レスポンス、モデルを書き換えてみた。
まずは、リクエストの部分。ResponseTypeをCodableにしてリクエストして返ってきたデータをレスポンスにデコードする static func decode(with data: Data, response: HTTPURLResponse?) throws -> Response<ResponseType>
を定義した。
public protocol Requestable { associatedtype ResponseType: Codable static var baseURL: URL { get } static var notFoundText: String { get } var allHTTPHeaderFields: [String: String]? { get } var endpoint: URL { get } var path: String { get } var method: HttpMethod { get } var body: [String: Any] { get } static func decode(with data: Data, response: HTTPURLResponse?) throws -> Response<ResponseType> }
次にレスポンスではリクエストの static func decode(with data: Data, response: HTTPURLResponse?) throws -> Response<ResponseType>
で処理するレスポンスのデコードメソッドを定義した。
singleはその名のとおりネストのないJSON(ex: {}
)の場合、unkeyedContainerは配列(ex: {},{}
)の場合に使う感じを想定して定義した。
ちなみに、.formatted(DateFormatter.ISO8601)
は .iso8601
でもOKだが、iOS9~11の対応を想定して開発したので独自で定義したDateFormatterを呼んで渡してる。
public struct Response<T: Codable> { public let totalCount: Int public let values: [T] init(single data: Data, response: HTTPURLResponse?) throws { let strTotalCount: String = response?.allHeaderFields["Total-Count"] as? String ?? "" self.totalCount = Int(strTotalCount) ?? 0 let decoder = JSONDecoder() decoder.dateDecodingStrategy = .formatted(DateFormatter.ISO8601) self.values = [try decoder.decode(T.self, from: data)] } init(unkeyedContainer data: Data, response: HTTPURLResponse?) throws { let strTotalCount: String = response?.allHeaderFields["Total-Count"] as? String ?? "" self.totalCount = Int(strTotalCount) ?? 0 let decoder = JSONDecoder() decoder.dateDecodingStrategy = .formatted(DateFormatter.ISO8601) self.values = try decoder.decode([T].self, from: data) } }
そして、レスポンスとなるモデルはスネークケースのキー名のみ、独自で定義すればOKなので簡潔になった。
public struct User: Codable { public let id: String public let name: String? public let profileImageUrl: URL public let followeesCount: Int public let followersCount: Int public let itemsCount: Int public let websiteUrl: String? public let location: String? public let organization: String? public let description: String? private enum CodingKeys: String, CodingKey { case id case name case profileImageUrl = "profile_image_url" case followeesCount = "followees_count" case followersCount = "followers_count" case itemsCount = "items_count" case websiteUrl = "website_url" case location case organization case description } }
最後に、リクエストプロトコル(Requestable)とレスポンス(Response<T: Codable>)とモデル(User: Codable)を組み合わせて、フォローしているユーザーの一覧を返すリクエストは下記のようになった。
このUserFolloweeRequestを見るだけで、どのようなリクエストでレスポンスとして何が取得できるのか、はっきりするのが良いですね。
public struct UserFolloweeRequest: Requestable { public typealias ResponseType = User public static let notFoundText: String = "フォロー中のユーザーがいません" public let endpoint: URL public let path: String = "users/%@/followees" public let method: HttpMethod = .get public let body: [String: Any] = [:] public init(page: Int, perPage: Int, userId: String) { let basePathURL: URL = UserFolloweeRequest.baseURL.appendingPathComponent(String(format: path, userId)) var components: URLComponents? = URLComponents(url: basePathURL, resolvingAgainstBaseURL: true) components?.queryItems = [URLQueryItem(name: "page", value: String(page)), URLQueryItem(name: "per_page", value: String(perPage))] self.endpoint = components?.url ?? basePathURL } // MARK: - ResponseType decode public static func decode(with data: Data, response: HTTPURLResponse?) throws -> Response<User> { return try .init(unkeyedContainer: data, response: response) } }
Codableを使うことで、Swift3のときにResponseTypeに準拠させていたJsonをdecodeするためのプロトコル(JsonDecodableやJsonDecodeError)は不要となり、Codableが全てを処理してくれるようになった。
Codableへの移行が簡単だったのは、GithubKitの設計が良かったからだと思う。
まだまだ発見がたくさんあったけども、それはまた別の機会にする。
当初の目的は、iOSDesignPatternSamplesでiOSのデザインパターンを学ぶことだったので。
本家のiOSDesignPatternSamplesはGitHubKitなので、QiitaKitをつかってiOSDesignPatternSamplesの理解を深めていきたい。
ただコードを眺めたり、コピペするだけではやっぱり理解できないですね。
しかし、年内に終えるつもりだったのに間に合わない気がしてたぞ!!!!!!