【swift】Optional Bindingについて

前回swiftでのnilの取り扱いについてまとめました。

今回はnilを取り扱う時に便利な記法のOptional Bindingについてまとめます。

Optional Binding

必要になるケース

例えば、関数の中には返り値がOptionalなものがあります。
配列からindexの値を取得するfindなどがそうです。

var array: [String] = ["a", "b", "c"]
find(array, 'c') // 2
find(array, 'd') // nil

そこで、findの返り値を定数に代入してみます。

let index: NSNumber = find(array, 'd') // nil

これはコンパイルエラーになります。 定数indexはoptionalな定数ではないので、nilは代入できません。

let index: NSNumber? = find(array, 'd') // nil

Optionalにすればnilを代入できるようになるので、コンパイルが通るようになります。
次は、このindexの値をもとに配列から要素を削除してみます。

array.removeAtIndex(index)

これはコンパイルエラーになります。
indexはオプショナルな定数なので!をつける必要があります。

array.removeAtIndex(index!)

これは、コンパイルは通りますが、indexnilなので、ランタイムエラーが発生します。

つまり

indexの値がnilじゃ無いときに、配列から要素を削除したいのです。

これを、Optional Bindingを使用しないとこんな感じになると思います。

var array: [String] = ["a", "b", "c"]
let index: NSNumber? = find(array, "d")

if index != nil {
  array.removeAtIndex(index!)
}

いちいちindexを定義してnil判定するのはとても面倒です。

そこでOptional Bindingを使用します。

var array: [String] = ["a", "b", "c"]
if let index: NSNumber = find(array, "d") {
  array.removeAtIndex(index)
}

定数の定義とnil判定を同時に行うことができます。

ifのブロックの中は値がnilじゃない時に実行されるため、定数はoptionalではありません。
そのため、!も必要無くなります。

以上、Optional Bindingまとめでした。

【swift】nilとoptionalな型の取り扱い

nilの扱い

swiftでは通常、変数や定数にnilは代入できません。

var hoge: String = nil
let fuga: String = nil

これはコンパイルエラーになります。

nilを代入するためには、optionalであることを宣言するために型に?をつけます。

var hoge: String? = nil
let fuga: String?  // 代入しなくても初期値はnil

メソッドなどを使用する際

変数や定数をoptionalな型として宣言するということは、

「必ずしも値が入っているとは限らない(nilである可能性がある)」

ということです。

その状態でメソッドを叩こうとすれば、当然nilだった場合は実行できません。

let str: String?
str.uppercaseString // strはnilの可能性があるのでコンパイルエラーになる

コンパイルエラーにならないようにするためには、変数や定数の後ろに!をつけるとコンパイルできるようになります。

let str: String?
str!.uppercaseString // コンパイルは通るようになるが実行時にエラーになる

ただ、このケースだとコンパイルは通りますが、strはあくまでもnilなので実行時にエラーになります。

この場合strnilでないことを事前にチェックする必要があります。

let str: String?
if str != nil {
  str!.uppercaseString // strがnilではないのでメソッドを実行することができる
}

この辺りの記述をもっとスマートに書くために、Optional BindingやOptional Chainingなどの記法がswiftでは提供されているのですが、それについてはまた別途まとめます。

【swift】InterfaceBuilderを使用してViewControllerを組み立てる

objective-cと変わらないと思ってたらちょこっとだけ違いました。

新規ファイル作成から「Cocoa Touch Class」を選択して

f:id:w6500:20141007223339p:plain

「Also create XIB file」にチェックを入れれば、ViewControllerのファイルと一緒にxibファイルを作成されます。

f:id:w6500:20141007223644p:plain

objective-cであれば、initWithNibNameで初期化しなくてもInterfaceBuilderで作成した画面が適用されていたのですが、swiftの場合はnibNameは必ず指定しないとダメっぽい?

self.window?.rootViewController = TestViewController(nibName: "TestViewController", bundle: nil)

【swift】変数・定数・関数を定義する

最近swiftをちょっと勉強しだしたので、ちょっとずつメモを取っていこうと思います。

変数

var hoge: String = "hoge" // 型を宣言する
var hoge = "hoge" // 宣言しなくても自動的に判定もする
var hoge = "hoge", fuga = "fuga" // カンマで連続で定義することもできる
var hoge, fuga: Int // 複数の変数を型宣言することもできる

型宣言は省略することのメリットよりもデメリットの方が大きいので、必ず宣言するようにする。

定数

let HOGE = "hoge"

関数定義

// 返り値がある場合
func say() -> String {
  return "hello."
}

// 返り値が無い場合
func say() {
  println("hello")
}

// 複数返り値がある場合
func say() -> (Int, Int, Int) {
  return (1, 1, 1)  
}

GoogleAnalyticsのトラッキングのコールバックを設定する

〇〇ページに遷移するためのボタンのクリック率とかを図るためによく、ボタンのクリックイベントを計測したりします。

でも、このページ遷移とトラッキングAPIを同時に叩くと、やっぱりうまくトラッキング出来ないこともチラホラあるみたいです。

Analyticsのページを見ても、明らかに少ないよなーこの数字っていう時が結構あります。

なので、トラッキングAPIが終わってからページ遷移したいんです。

こういう要望のために、Analyticsはちゃんとコールバックを設定できる様になっています。

hitCallbackを使う

ga.jsanalytics.jsも基本的には使い方は一緒で、hitCallbackというkeyで関数を指定します。

// ga.js
_gaq.push(['_trackPageview', {
  page: '/index.html',
  hitCallback: function() { alert('complete!!'); }
}]);

// analytics.js
ga('send', 'pageview', {
  page: '/index.html',
  hitCallback: function() { alert('complete!!'); }
});

pageviewのAPIが完了してからhitCallbackで設定した関数が実行されます。

注意点

GoogleAnalyticsのjsが読み込めないと正常に動作しないため、コールバックでページ遷移をする処理を書いていると、ページ遷移しないボタンが出来上がってうので注意が必要。

【Ruby】ActiveRecordのAssociationにメソッドを追加する

ActiveRecordhas_manyとかbelongs_toとかのAssociationはブロックを渡してメソッドを追加することができます。

class User
  has_many :blogs do
    # ステータスがopenのものを取得する
    def open
      where(status: 'open')
    end
  end
end

こんな感じで、ブロックの内部で関数を定義するとその関数をメソッドチェーンで実行できるようになります。

User.first.blogs.open

複数のモデルで使い回したい処理がある場合は、moduleにまとめてextendオプションでメソッドを追加することも可能です。

module Common
  def open
    where(status: 'open')
  end
end
class User
  has_many :blogs, extend: Common
end

配列にして、複数のmoduleも渡せます。

class User
  has_many :blogs, extend: [Common, Common2]
end

便利ー!!

【Rails】belongs_toで紐付けたモデルの存在チェック

belongs_toで紐付けたモデルが本当にデータとして存在するかどうかをチェックする。

class Blog
  has_many :comments
end
class Comment
  belongs_to :blog
end

例えば、このようなモデルがあった時、Blogモデル経由でCommentを作成した場合は、すでにBlogは存在しているので、comment.blog_idには確実に存在しているBlogのIDが入ります。

Blog.first.comments.create
# 存在しているブログからコメントを作成する

ですが、Blogモデル経由ではなくCommentを直接作成するとき、Blogと紐付けるためにはBlogのIDを外側から指定しないといけません。

Comment.create(blog_id: 1)
# コメントの作成時にblog_idを指定する

この方法で作成する時、特に設定しないままだと存在しないBlogのIDも指定できてしまいます。

Comment.create(blog_id: 999)
# IDが999のBlogは存在しないが、Commentは作成できてしまう

参照先の指定したIDのデータが存在しない場合にエラーを投げるにはValidateを設定する必要があります。

class Comment
  belongs_to :blog

  validates_presence_of :blog
end

validates_presence_ofで対象を指定すれば、参照先のデータが存在しない場合はエラーになり、データは作成されなくなります。