Swift3 例外メモ(try、try!、try?、do-catch)
try?
の仕組みとか曖昧になってしまったのでメモ。
例外のスロー
- 例外をスローする関数は
throws
を宣言(書く位置は引数宣言の直後) - スローする例外は
Error
プロトコルを実装している必要がある
enum MyError: Error { case notFound case fail(String) } func fatalError() throws -> String { throw MyError.fail("fatal error.") }
呼び出し元
すべての例外の補足
do { try throws宣言された関数呼び出し } catch { ... }
といった構文で例外をキャッチthrows
宣言された関数を呼び出すときには、頭にtry
、try!
、try?
のいずれかをつけるcatch { ... }
またはcatch let error { ... }
ですべての例外をキャッチ(switch
のdefault
みたいな)enum
のようにエラーの種類から網羅性はチェックされないので、網羅するにはすべての例外を補足するcatch
が必須
func catchAllErrors() { do { try fatalError() // `throws`宣言された関数を呼ぶには`try`が必要 } catch MyError.notFound { // エラーの種類を記述 // ... } catch MyError.fail(let message) { // enumの関連値はパターンマッチで取得できる // ... } catch let error { // enumと違って網羅するにはこれが必須 // ... } }
部分的に例外を補足
- すべての例外を補足しない場合は、関数の宣言に
throws
を記述する必要がある
func catchSomeErrors() throws { // 例外を網羅できていないので`throws`宣言が必須 do { try fatalError() } catch MyError.notFound { // ... } }
例外を強制的に無視する(try!)
try!
を使うと例外を無視できる(do-catch
が不要)- ただし例外が発生したときにはクラッシュする(強制アンラップと同じ挙動)
func forceCall() { try! notError() // 例外を無視する(が、発生したときはクラッシュする) }
例外を安全的に無視する(try?)
try?
を使うと、try!
と同じように例外を安全に無視できるtry!
と違い、例外が発生した場合でも単純に無視される- 呼び出した関数に戻り値がある場合は、戻り値が
nil
となる(失敗した、というニュアンスか)
func safeCall() { try? fatalError() // 戻り値がない場合はセーフな呼び出し let s1 = try? fatalError() // => nil let s2 = try? someString() // => some string }
まとめ
個人的にこんな感じで考えたらよいのかなという指針。
- 発生した例外に興味がなければ、
try?
を使って例外を無視する - 発生した例外に興味がある場合は、
do { try xxx() } catch { ... }
を使って補足 - 入力値が完全に固定など、確実に例外が発生しないと断言できる場合は
try!
を例外的に使う(かも)
個人的にはEither
あるいはResult
のほうが好きなのだけれど、シンタックスシュガーがよく出来ているので場合によっては例外の使用を検討しても良いのかも。
Vagrantfile: Ubuntu14.04 で Swift製Webフレームワーク「Vapor」を使える状態にする
Swift製のWebフレームワークである「Vapor」をUbuntuに入れてみた。
github.com
せっかくなのでVagrantfileを書いてみた。(たぶんこれが一番楽だと思います)
ちなみにVagrantについてはドットインストールの動画が分かりやすいです。
http://dotinstall.com/lessons/basic_vagrant
環境
- Virtualbox: 5.1.12
- Vagrant: 1.9.1
- Mac: macOS Sierra
- Ubuntu: 14.04
手順
作業ディレクトリ作成
$ mkdir ubuntu-server # 適当なディレクトリを作成 $ cd ubuntu-server $ curl -O https://gist.githubusercontent.com/YusukeHosonuma/54a2f4f294e87cf0dbc87b7420b5ae1c/raw/c181f41b05f2a7dddbe452dd5119b5dcbd857a96/Vagrantfile
VM起動+ssh接続
$ vagrant up $ vagrant ssh
Vaporプロジェクト作成・ビルド
$ vapor new hello
$ cd hello/
$ vapor build
ビルドがわりと長い・・・依存関係の解決に時間かかってる?
実行
$ vapor run serve Running hello... No preparations. Server 'default' starting at 0.0.0.0:8080
Tomcatと同じ8080ポートで起動されている模様。
動作確認
ホストマシン(Macとか)で以下のURLをブラウザで表示。 http://192.168.33.19:8080
真っ白な画面の中央に以下のロゴが表示されればOK。
簡単でしたね。
ブログデザインの変更
以前から表示が重いのが気になっていた+2017年になったので変更してみましたぞい。
Vagrant + CentOS7 で、RustのWebフレームワークRocketを発射する
Linuxとか環境面詳しくないのでメモ。
Rocketは最近出たRustのWebフレームワーク。
https://rocket.rs
環境
VirtualBox: 5.0.30
Vagrant: 1.9.1
CentOS: 7.1
Rust: 1.16.0-nightly
Rocket: 0.1.2
ちなみにこれを書いている時点で最新のCentOSは7.3だったけれど、vagrant up
で立ち上げたら途中で止まってしまったので断念。
VagrantでCentOS7を立ち上げる
まずは発射台を準備。
$ mkdir launch-pad $ cd launch-pad
Boxイメージは、Chef社がOSSとして公開してる「Bento」を使う。
(弁当箱とは誰がうまいことを)
https://atlas.hashicorp.com/bento
$ vagrant init bento/centos-7.1 A `Vagrantfile` has been placed in this directory. You are now ready to `vagrant up` your first virtual environment! Please read the comments in the Vagrantfile as well as documentation on `vagrantup.com` for more information on using Vagrant.
Vagrantfileは以下のようにしてプライベートネットワークとして接続できるようにしておく。
Vagrant.configure("2") do |config| config.vm.box = "bento/centos-7.1" config.vm.network "private_network", ip: "192.168.33.11", auto_config: false end
原因はよく分かっていないのだけれど、auto_config: false
を設定しておかないと、立ち上げたVM上で正しくIPアドレスが振り当てられなかった。
VMイメージを起動。(Boxの取得から始まるので時間かかるよ!)
$ vagrant up Bringing machine 'default' up with 'virtualbox' provider... ...
SSHで接続。
$ vagrant ssh -bash: warning: setlocale: LC_CTYPE: cannot change locale (UTF-8): No such file or directory [vagrant@localhost ~]$
基本設定
ファイアウォールを切ってしまう。(仮想環境なので)
$ sudo systemctl stop firewalld
$ sudo systemctl disable firewalld
SELinuxが無効か確認。今回は無効になっているのでそのまま。
$ getenforce Permissive
Rustのインストール
Rocketでは構文拡張を利用するため、NigthlyなRustが必須。(2016/12/30時点)
Rust1.14.0で公式ツールとなったrustup
をインストールする。
https://rustup.rs
$ curl https://sh.rustup.rs -sSf | sh
info: downloading installer
...
選択肢が表示されるが、とりあえずデフォルトの1)を選択。
Current installation options: default host triple: x86_64-unknown-linux-gnu default toolchain: stable modify PATH variable: yes 1) Proceed with installation (default) 2) Customize installation 3) Cancel installation
Rust is installed now. Great!
To get started you need Cargo's bin directory in your PATH environment variable. Next time you log in this will be done automatically.
To configure your current shell run source $HOME/.cargo/env
パスを設定してくださいと言われるので、.bash_profile に記述しておく。
# Rust source $HOME/.cargo/env
再読込して現在のシェルに反映させておく。これでRustが使えるようになった。
$ source ~/.bash_profile $ rustc --version rustc 1.14.0 (e8a012324 2016-12-16)
そしてRustを最新版(Nightly)に変更。
$ rustup default nightly $ rustc --version rustc 1.16.0-nightly (4ecc85beb 2016-12-28)
Hello, world
Rocket用のプロジェクトを作成して、そこに移動。
$ cargo new hello-rocket --bin Created binary (application) `hello-rocket` project $ cd hello-rocket
Cargo.toml の依存ライブラリに以下を追加。
[dependencies] rocket = "0.1.2" rocket_codegen = "0.1.2"
src/main.rs を以下のように編集。このあたりは公式ドキュメントどおり。
#![feature(plugin)] #![plugin(rocket_codegen)] extern crate rocket; #[get("/")] fn index() -> &'static str { "Hello, world!" } fn main() { rocket::ignite().mount("/", routes![index]).launch(); }
そしてビルド・実行・・・するがエラーになる。
$ cargo run ... error: aborting due to previous error error: Could not compile `unicase`. To learn more, run the command again with --verbose.
どうやらgccが入っていないのが原因らしいのでインストール。
$ sudo yum -y install gcc
...
今度こそロケットを打ち上げられた。
$ cargo run Finished debug [unoptimized + debuginfo] target(s) in 79.19 secs Running `target/debug/hello-rocket` 🔧 Configured for development. => listening: localhost:8000 => logging: Normal 🛰 Mounting '/': => GET / 🚀 Rocket has launched from http://localhost:8000...
$ curl localhost:8000
Hello, world!
80ポートで起動する
ここからはあんまり正攻法じゃない。 ので、とりあえずこの方法でうまくいった程度の話。
Rocketのドキュメントを見ると、Productionで実行するには以下のようにすればよいと書いてある。
$ sudo ROCKET_ENV=staging cargo run sudo: cargo: command not found
が、エラーとなる。
これは環境変数PATHがsudo時に引き継がれないのが問題らしい。
$ env | grep PATH PATH=/home/vagrant/.cargo/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/vagrant/.local/bin:/home/vagrant/bin $ sudo env | grep PATH PATH=/sbin:/bin:/usr/sbin:/usr/bin
で、調べるといろいろと情報が出てくる。
CentOS で sudo 時に実行ユーザーのPATHを引き継ぐ http://qiita.com/ogwmtnr/items/3ec2fab50d069a3cf335
sudoで環境変数を引き継ぎたい http://mikio.github.io/article/2012/03/10_sudo.html
が、何を試しても一向にcargoのパスが引き継がれない。
仕方ないのでルートユーザーでもrustupを入れてしまう。(同様なので方法は割愛)
$ su -
# rustupのインストールと設定
ちなみにパスワードは「vagrant」。
そしてそのままルートユーザーで以下を実行すると、80ポートで起動する。
# ROCKET_ENV=staging cargo run 🔧 Configured for staging. => listening: 0.0.0.0:80 => logging: Normal 🛰 Mounting '/': => GET / 🚀 Rocket has launched from http://0.0.0.0:80...
ホストマシンからブラウザでアクセスしてみるとちゃんと動いている。 http://192.168.33.13
GET /: => Matched: GET / => Outcome: Succcess => Response succeeded. GET /favicon.ico: => Error: No matching routes for GET /favicon.ico. => Warning: Responding with 404 Not Found catcher. => Response succeeded.
ちなみにデフォルトの8000ポートだと以下のように指定してもアクセスできなかった。
http://192.168.33.13:8000/
ファイアウォールを切ったので問題ないと思ったのだが、何か設定がマズイのかもしれない。
終わり
Linuxとか環境構築とかあんまり経験ないので思ったよりもずっとしんどかった。 まぁしかしVagrantとか楽でいいなと思った。
とりあえず以下は解決したい課題。
- ルートユーザで実行しちゃってる。(本番環境なら大問題)
- 8000ポートでホストマシンから接続できるように。
- Linuxとか詳しくなる。
iOSオールスターズ2に参加してきた感想
帰ってブログ書くまでがイベントって言われたので書くよ! eventdots.jp
ブログなので当然ですけれど、私が思ったことをそのまま書いてます。 (とりあえずファーストインプレッションをダンプしてきます)
あ、登壇者の皆様、本当に貴重な情報をありがとうございました!
RxSwift in Practice
- 状態をストリームとして表現するのは良いと思った。
- しかし、やっぱ殆どのケースでは大げさ=オーバースペック感は否めない。
- インジケータ非表示にするの忘れる、くらいであればテストで十分感あり(発表でもあったけど)。
- やはり状態がたくさんある状態で、最新のViewに更新される必要がある、みたいなケースが活きるのではないか?
- コードレビューをランダムに回すのは良いアイディアだと思った。
- 学習コストが高い、っていうのは素直に認めなきゃと思う。
VC「もしかして...」Model「私たち...」「「入れ替わってるー!?」」を前前前世から防ぐ方法
- ギャグ満載。
- Fat VC=「肥満体質な」VCって表現は分かりやすかった。(コードの臭い匂い的な意味とも通じる)
- クリーンアーキテクチャは大げさ過ぎる気がしたけど、ViewController/Presenterまわりの実装の仕方はありだと思った。
- 「君の名は」を見ておくべきだった?
Type-safe URL Routing in Swift
- サーバサイドの話かと思ったけれど、URLスキーム起動とかでiOSも該当する。なるほど。
- ApplicativeFunctorでパース処理を書けるのは良いと思った。(JSONパーサとかでも活用されてるけど)
- 途中から全く分からん状態!
- 関数型もっと勉強しよう・・・
Using PDF in iOS
- 濃い・・・。
- でも実際にPDFを扱う必要が出た場合に、間違いなく参考になる貴重な情報。
Xcode8で開発はどうかわったのか
- 変わってない。
- Visual DebuggingでAutoLayoutの制約が確認できるようになったのは良いと思った。
- FPS Performance Gauge、いずれ使ってみたい。
- プロファイラも進化したなぁ、と思う。
これから始めるProtocol Buffers導入
- Googleが考えた、シリアライズフォーマット。
- バイナリで高速(サイズがJSONの半分以下に)
- .protoファイルからSwiftコードを生成できる
- ありだとは思うけれど、本当にパフォーマンスが重要な場面以外では導入は控えて、JSONにすべきだと思った
DIを前提にしたiOSアプリの設計
- DIコンテナを使うべきか、というとそこまでは判断できないけど、DI設計はありだと思った。
- 依存性注入を意識したコードスタイルになっていれば、DIコンテナを導入しなくても、テストなどは相当楽になるはず。
- Storyboardからインスタンス生成されるとプロパティインジェクションになっちゃうのは確かに。(個人的にもコンストラクタインジェクション派)
Swiftらしい表現を目指そう
- すごく分かりやすかった。
- イニシャライザは文章っぽく書かなくてもいいってのは知らなかった。
- プロトコル(Haskellで言えば型クラス)を意識したコードを書くのは、もうちょっと日頃意識したいと思った。
おわり
- 楽しかった!
- 途中でピザの匂いで食欲そそられた。
- イベント会場キレイだった。
- 関数型はもっと勉強していくよ!