49hack

ひよっこエンジニアが魔法使いになるまで

swiftを一から勉強してTODOアプリをつくるまでの過程

仕事でしばらくwebで活動していたのですが、フリーランスになったことだし、アプリ開発をまた再開しようと思い、swiftを勉強してみることにしました。

また、今回swiftを勉強するにあたり、その過程もメモしていきました。
学習の過程って意外と忘れがちですよね 😪

最終成果物はgithubにおいてます 😃
paranishian/iOSToDoApp: iOS TODO app using Realm

まずはswiftの言語仕様を理解する

iOSアプリを開発していたのはObjective-C全盛期?の4年前くらいなので、swift自体初めて触るわけで、、、まずはswiftの言語仕様をおおまかに理解するために、公式docに目を通しました。

↓こちらはswift4には追いついていませんでしたが、英語での理解不足を補うために一緒に読みました。感謝 🙏

ちょっと手元で試してみたい、ってときはpaizaを使いました。web上で手軽に試せて便利 😆

ブラウザでプログラミング・実行ができる「オンライン実行環境」| paiza.IO

ここで理解したこと

  • クロージャーの構文
  • defer
  • where
  • try?
  • ??
  • error handling
  • tuple
  • guard/else
  • stride

このときの印象は、、、「swiftめっちゃ引数わかりにくい...!!! 😇 」 どれがkey??どれがvalue??どれが返却値??みたいな 🤣

TODOアプリをつくる

swiftの仕様はおおまかに理解できたので、なにかアプリをつくって学習するのが手っ取り早いなーと思い、仕様で迷うことが少なそうなTODOアプリを作ることにしました。
データを扱う上で、CoreDataかRealmかで迷いましたが、「使ったことがなくて触ってみたかった」という理由でRealmを採用しました。

まずはRealmのリファレンス通りにつくっていく

ありがたいことにRealmにTODOアプリのdocがあったのでそこを見ながら進めました。

ライブラリ管理ツールを決める

CocoaPodsかCarthageか迷いましたが、ライブラリ数ではまだまだCocoaPodsが優勢、との話を友人から聞いたのでCocoaPodsにしました。

ちょっとした変更と修正

Realmのdocではサーバ同期ありきで書かれているのですが、今回はローカルだけで使いたかったのでsetup部分を少し変更しました。
また、ドキュメントどおりだとどうしてもうまくいかない部分があったのでゴニョゴニョしました。 (if realm.objects(TaskList.self).count == 0の部分)

func setupRealm() {
    let config = Realm.Configuration(schemaVersion: 1)
    self.realm = try! Realm(configuration: config)
    func updateList() {
        // まだTaskListが存在しない場合は追加する
        if realm.objects(TaskList.self).count == 0 {
            try! realm.write {
                realm.add(TaskList())
            }
        }
        if let list = realm.objects(TaskList.self).first {
            items = list.items
        }
        tableView.reloadData()
    }
    updateList()
    // Notify us when Realm changes
    self.notificationToken = self.realm.observe { _,_ in
        updateList()
    }
}

ってなかんじでTODOアプリが完成! 🎉
(ほぼサンプルのままだけど)

paranishian/iOSToDoApp: iOS TODO app using Realm

とりあえず動くものが完成したので、あとは

  • TODOアプリに機能追加
  • 別のアプリつくる
  • fastlaneでbuild自動化

あたりかな〜。

それではまた 👋

開発用.devがhttpsに強制リダイレクトされて困った件

pow使ってて開発してたら、急にSafariChromehttpsに強制リダイレクトされるようになったので対応。 (Chromeはv63からこの仕様になったぽい)

対応

インストールし直せばOK。
(ver0.6.0から、トップレベルドメイン.devではなく.testがデフォルトになってる)

Pow User's Manual

Switch top-level domain from .dev to .test. Google owns .dev and enabled HSTS on it, breaking non-HTTPS sites. The .dev TLD is still enabled for backward compatibility, but .test is the new default.

$ curl get.pow.cx/uninstall.sh | sh
$ curl get.pow.cx | sh

powder使ってる場合はコマンドでもOK。 (やってることは同じ)

$ powder uninstall
$ powder install

参考

リーダブルコードを久しぶりに読み返してみた

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

だいぶ前に読んだけど、すっかり内容忘れてたので久しぶりに読み返してみた。
もっと意識しないとなーとおもったことをコメントとともにつらつらとまとめる。

理解しやすいコード

読みやすさの基本
コードは他の人が最短時間で理解できるように書かなければいけない。

理解しやすいコードとは、文字数が短いコードではなく、理解する時間が短いコード。
ここでいう「他の人」とは、将来の自分の事でもあるので、たとえチーム開発してなくても注意すること。

名前に情報を詰め込む

  • 明確な単語を選ぶ
    • 例えば、Getではなく、状況に応じてFetchDownloadなどを使う
  • 変数名に大切な情報を追加する
    • ミリ秒を表す変数名に_msをつける、など
  • スコープの大きな変数には長い名前をつける
    • 長い名前、というよりは明確な名前、かな
    • 1,2文字の短い変数名を使うのはスコープが数行のときだけにする

誤解されない名前

例えば、get()set()には軽量なメソッドが期待されていて、思わず頻繁に使用されてボトルネックになったりするのでメソッド名には注意する。

  • 上下の限界値: max_, min_
  • 包含的範囲: first, last
  • 包含/排他的範囲: begin, end

コメントは正確で簡潔に

入出力のコーナーケースに実例を使う。

// 実例: Strip("abba/a/ba", "ab")は"/a/"を返す
String Strip(String src, String chars)

もちろん、テストを書けるのであればテストに入れたほうがいい。

制御フローを読みやすくする

条件式の並び順

if (length >= 10)if (10 <= length)
だと前者のほうが読みやすい。

white (bytes_received < bytes_expected)
または、
white (bytes_expected > bytes_received)
これも、前者のほうが読みやすい。

左側 右側
「調査対象」の式。変化する 「比較対象」の式。あまり変化しない。

要は、英語の用法に合わせる。
(「もし君の身長が170cm以下なら」と「もし170cmが君の身長以上なら」)

短いコードを書く

身近なライブラリに親しむ

プログラマというのは、既存のライブラリで問題を解決できることを知らないことが多い。
たまには標準ライブラリのすべての関数、モジュール、型の名前を15分かけて読んでみよう。

車輪の再発明はしない。これはとても重要。

Railsを使っているとこれは特に顕著な気がする。

最近だと、prev_weekとかいう便利メソッドを知った。(active_support)

Time.zone.today.prev_week(:sunday) # 先週の日曜日
=> Sun, 10 Dec 2017

テストと読みやすさ

テストについては、rspecdescribecontextを使えば文脈が読みやすいテストコードが書けるようになってるはず。

入力値を単純にする

テストには最もキレイで単純な値を選ぶ。

たとえば、
CheckScoresBeforeAfter("-5, 1, 4, -99998.7", "4, 3, 1")
のようなテストだと、-99998.7の主張が激しすぎてテスト内容に集中できない。

ここでは「マイナスの値を検証したい」とすると、

CheckScoresBeforeAfter("1, 2, -3, 1", "3, 2, 1")

のようにしてテストの効果を変えずに値だけ単純にできる。

テストに優しい開発

コードにはテストしやすいものとそうでないものがある。
テストしやすいコードには明確なインタフェースがある。
あとでテストをするつもりでコードを書くと、 テストしやすいようにコードを設計するようになる
このようにコードを書いていけば、いいコードを書けるようになる。

テスト容易性の低いコード

テスト容易性の高いコード

  • クラスが小さい。あるいは内部状態を持たない。
  • クラスや関数が1つのことをしている。
  • クラスは他のクラスにあまり依存していない。高度に疎結合化されている。
  • 関数は単純でインタフェースが明確である。

テスト容易性は設計の問題/利点にも直結してくるので意識する。

テスト駆動開発」については、t_wadaさんの本を読んで勉強する。

テスト駆動開発

テスト駆動開発

既存のコードを読みやすくする前にやること

既存のコードを読みやすくしたい衝動にかられても、とりあえずはガマンすること。
まずは、これから自分が触るコードを読みやすいコードにしていく。
それから、他の人のコードについても、同じく読みやすいコードにしていく。

ただ、たとえば変数名など(冒頭にある_msつける/つけないとか)細かい議論になりがちなものは、チームでコーディングルールを作ってから着手したほうがいい。

ひとまず開発チーム全員でこの本読んで認識合わせられると幸せになれそう。