Rails5 で WebSocket を ActionCable でなく Firebase でやった話

Ruby on Rails Advent Calendar 12日目です!

私は仕事では iOS を Swift を書いていて、個人開発でサーバサイド APIRails で書いています。

そこで WebSocket を使いたいと思い ActionCable でやろうとしてたのですが、 Firebase でもいけるんじゃねと思いやってみたらかなり良かったため、その話をします。

ただ書いている途中で知ったのですが、悲しいですが現状この方法は Firebase から推奨されておらず後で使えなくなる可能性がありますので、利用しない方が良さそうです。。。 😢

f:id:star__hoshi:20161211184034p:plain

要件: タイムラインを形成したい

Twitter を想像してもらえれば良いです。
ユーザが何かを投稿して、それをみんなで見る、そのタイムラインを形成したいという要件でした。

ただし今回は、そのタイムラインに即時性はあまり重要でなく、それよりも断続的に投稿され続ける情報をリアルタイムで眺められるようにしたい、というのが実際にやりたかったことです。

仕組み

ユーザ(クライアント)が POST
    ↓
Rails が受け、 POST 内容を DB に書き込む
    ↓
DB に書き込んだ情報を Firebase に投げる
    ↓
Firebase が受け、クライアントに反映
    ↓
クライアントが Firebase から情報を受け画面に表示

という流れでやっています。
ユーザが Post するのは Rails サーバに対してですが、タイムラインを表示するためにクライアントは Firebase から情報を取得する、という流れになっています。

この流れのメリット

実装の簡略化

Rails 側は WebSocket 周りを何も気にすることがなく、普通に Rest API を作成し、その処理の一部に Firebase に情報を横流しする、というフローが増えるだけです。

クライアント実装が楽に

クライアント側は Firebase SDK を使えば良いため Socket 周りの実装を自力でやる必要がありません。
オフライン時の処理や再接続なども Firebase さんがいい感じにやってくれています。

また、Firebase SDKiOS/Android/JS など幅広く対応されており知見も共有できます。

お金も安い

Firebase Pricing
100 人まで同時接続、データベースは 1 GB まで無料で利用できます。

有料になったとしても $25 per month で 10,000 人 まで同時接続数を増やせますし、同時接続の初期制限値 10,000 を解除できるよう努力しております。 と言っているため今後もっと増えるのではないでしょうか。

スタートアップなどではまず このサービスには価値があるのか というのを検証したいと思うので、ある程度までは無料でまかなえるのは良いのではないでしょうか。

実装

Firebase の設定

Firebase Analytics を iOS / Android アプリに導入する手順のまとめ などを参考に、 Project を作成しましょう。

作成したら Project の作成 -> サービスアカウント -> データベースのシークレット からシークレットを取得します。 このシークレットが冒頭に述べた非推奨となっているところです。

秘密鍵json で管理する方法に変わっているようですので、その鍵で通信できれば同様の方法でいけると思いますが、未検証です。。

Rails 側の設定

gem 'firebase' を Gemfile に書き、 bundle install しましょう。

そしたら、 firebase に送信したい場所で以下を書きます。

  def create
    # DB 登録処理など

    base_uri = 'https://project-xxxxxxxxxx.firebaseio.com/'
    secret_key = 'yyyyyyyyyyyyyyyy'
    firebase = Firebase::Client.new(base_uri, secret_key)
    timeline = {title: "timeline"}
    firebase.push('timeline', timeline)
    render :create, status: :created
  end

わかりやすく create メソッドの中でやっていますが、本来ならば worker などをを使い非同期でやるべき処理です。

結果

f:id:star__hoshi:20161211184252p:plain

データを反映することができました。

最後に

データベースのシークレットを使う方法が非推奨となっており、大変残念な記事となってしまいました 😢。
が、 WebSocket の一つの手段として Firebase は有用だと思います。

特に、サービス立ち上げに時間をかけたくない時など、要件があれば検討してみてください。

さよなら So-net、今までありがとう

この記事は interlinkアフィリエイトなど全く利用していない、 PR でも何でもない個人の日記です。

最近の回線速度

Testing download speed........................................
Download: 0.56 Mbit/s
Testing upload speed..................................................
Upload: 13.44 Mbit/s

この数字本当にふざけてるし、通信速度制限かよという感じでアニメもろくに見れず、画像の表示にも時間がかかり、ひたすら頭にきていたので試しにプロバイダを interlink に変えてみた。

interlink は 2 ヶ月無料で違約金もなし、まずサービスを体験してみてよかったら継続してほしい、もし辞めたくなったらいつでも辞めてね、という健全なサービス体系である。

interlink 自体は ソフトバンク光はクソ - 日報2016-09-12 r7kamura さよなら、エキサイト - 日報 などで知った。

変えた結果

Testing download speed........................................
Download: 32.01 Mbit/s
Testing upload speed..................................................
Upload: 33.31 Mbit/s

何も文句はありません、今までみたいに動画が読み込めないということもないだろうし、快適なインターネット生活を送れるだろうと思う。

プロバイダを乗り換えてまだ 1 時間しか経っていないため今後不満などでてくるかもしれないが、不満が出たらまたブログに書く。

愚痴

ISP 並びに携帯キャリアなどの通信業界は、顧客と真摯に向かい合ってサービス提供してほしい。

  • キャッシュバックで客を釣るが受取期限がある
  • 契約月以外で解約すると違約金が発生する
  • 初月無料オプションなどをつけさせ、解約忘れで料金を取る

など、こんなことで金を儲けようとしないでほしい。

インターネットを契約したい人は大概がインターネットに接続したいだけで、そこにウイルスソフトやら動画視聴サービスやらのオプションなどは求めていない。

そんなことより繋がらない電話窓口、わけのわからない FAQ 、使い勝手の悪い管理画面をなんとかしてくれ。

土管屋は土管屋に徹してほしい、私からは以上です。

転職しようか迷っているキミへ

転職 Advent Calendar 2016 17 日目です!
この記事を公開しているのは12/8なので完全にフライング、今日読んでもらいたい相手がいるので公開しますが、問題あれば消します。

記事を書くきっかけ

友人とメシを食べて、転職するかどうするか〜みたいな話をしていた。
自分は今年の10月に転職したばかりなので、迷っているのは話し相手の方である。

いろいろ話した後に歩きながら「転職活動それ自体のメリット」など話せば良かったなと思ったので、相手に伝えるとともに自分の思いを公開する。

転職活動のメリット

転職というと職場環境が変わり最高 という話もあるが、それとは別に 転職活動という行為それ自体 が自分自身にとって大変良かった。

転職活動をすると、自分の市場価値を図ることができる。

  • 現時点の自分の価値は果たしてどれだけのものなのか?
  • 自分が培ってきたスキルや仕事への姿勢などは、他社ではどのように評価されるのか?

という 現実 を知る良い機会になる。

自分の経験

私は2014年にとある会社に新卒入社して、しばらく働いてこりゃ厳しいと思って、2015年2月くらいに転職活動をした。
厳しい理由は 転職することにした に書いてある。

転職活動では、個人開発でアプリをいくつか出していて、ダウンロードも結構あったしユーザ評価も良かったのでそこらへんを武器にいけるかな〜と思ってやっていた。
でも残念ながらその時は自分を求めてくれる会社にはめぐり合えず、5社くらい面接受けたけど全部落ちた。

(そのあと会社で「スマホアプリの開発案件あるんだけどやらないか」という話があり、それならもうちょっと頑張るかと思って転職活動は辞め仕事を頑張ることにした。)

転職活動で得たモノ

転職活動としては失敗だったけど自分としては得るものがたくさんあって、ざっくり以下のようなことを得ることができた。

  1. 個人でアプリを作っていて、DL が n 万あるのでまず話は聞いてくれる
    • 個人で n 万DLとかは割とインパクトがあり、話のきっかけとしても言いやすい
  2. ただし、今の自分は客観的に見て求められる人間ではないという現実
    • 個人開発のインパクトはあるが、アーキテクチャとかは何もなかったのでボロボロ
    • 業務について聞かれても、業務で得たものは他社ではあまり役に立たないことが多かった
      • その会社 での業務には必要な情報だが、 その会社以外 では役に立たない知識が多かった
  3. 自分のことを理解してもらえたのかという不安
    • 話終わった後に、こういうこともやってたし話せばよかった など思ったりする
    • アウトプットがなく実績も示せない

上記の現実を突きつけられたあとは、汎用的なスキルやアーキテクチャなどを学んだり Qiita やブログにノウハウを投稿したり GitHub にソースをプッシュしたりと、そこらへんを念頭に置き勉強などをした。

そして2016年5月にまた転職活動をしたのだが、いろいろやっていたおかげかいい感じに転職先が決まり、とある会社に勤めている。

何が言いたいかというと

2015年に転職活動に失敗していなければ今の会社に転職できていたか怪しい。

転職するかは置いておいて 自分の現時点での立ち位置自分が客観的に求められる人材なのか というのを確かめるために転職活動をするのは良いことだと思う。
転職活動をするだけなら損はない。(可処分時間は使うことになるけど)

その結果内定などが出たら、現職との比較や他の会社も見て、自分がどうしたいか考えれば良いと思う。

これを読んでいるキミはエンジニアではないかもしれないし、アウトプットなどはピンとこないと思うけど、今後どういう人生を歩むかの良い機会になると思う。

とりあえず気軽にその一歩を踏み出してはどうでしょうか。

RSS フィードがないサイトでも RSS を購読したい

RSS フィードのないサイト

例えば、 https://realm.io/jp/news/ などは RSS フィードを吐いていないようで、メールにて購読しろという感じらしい。
(英語版は RSS あるっぽい)

でも購読したい

Page2Feed API | EDGE を使ってみたら、結構行けそうだった。

f:id:star__hoshi:20161126190827p:plain

こんな感じで RSS 吐いてくれて、 feedly に登録できたので問題なさそう。

RSS はもう旧世代のツールなんですかね、私は情報収集は RSS + Twitter という感じなので、どこも RSS 吐いてほしい。

ReactNative でアプリ作ろうと思ったが途中でやめた話

最近 ReactNative 熱いし、 Web エンジニアがネイティブアプリサクサク作れたら俺たちネイティブアプリエンジニアの仕事なくなるじゃん、と思って ReactNative でアプリ作ってみるか、という気持ちになった。

Showcase 見ると Facebook, Instagram, Airbnb などそうそうたるアプリが作られてるし、今後は ReactNative がネイティブアプリのデファクトスタンダードになっていくのでは?という説も否定できない。

作り始める前の ReactNative のイメージとしては、 React がわかれば iOS / Android アプリが作れて、細かいことは ReactNative が吸収してくれて開発者は重要なところを書いていけばいい、という印象を持っていた。

作ろうと思ったのはリストを並べてタップしたら情報が表示される感じの、よくある RSS リーダみたいなやつ。

最初は Deco IDE 入れて Getting Started で感触をつかむことから始めた。

最初思ったことは LiveReload が最高で、 Cmd + S したら画面が再描画されるの革命的だった、 Xcode の Cmd + R して数十秒待ってる世界と比べるとやばい。

書き方としては React と全く一緒で、

<TabBarIOS>
  <TabBarIOS.Item>
    ...
  </TabBarIOS.Item>
</TabBarIOS>

みたいに書くと TabBar が表示されていい感じに書ける。
Xcode で書くと Storyboard で TabBar 設置して RootController 設定して〜みたいになる。
初心者からすると Storyboard の設定とかわからんし、慣れないとわかりにくいが、コードでかける ReactNative の方が直感的でええやん、と思った。

ただ、書いているうちにだんだんつらくなってきて、

  • コードの補完を全然してくれない
  • シンタックスエラーを吐いてくれない
  • なんかよくわからんが上手くいかない

などで嫌になってきた。

上二つは IDE 頑張ってくれという感じなのだが、「なんかよくわからんが上手くいかない」のは完全に俺が悪くて、

<View>
  <NavigatorIOS
    …
  />
</View>

というように NavigatorIOS を View で囲んでしまっていたのが原因だった。
完全に俺が悪いけど、気がつくまでに 1 時間くらいかかってしまった。
ただ、開発進んでいくと似たようなことでまたハマるんだろうな〜という気もする。

それとは別に、 ListView をタップした時や画面遷移時の背景とかちゃんと自分で指定しないといけないっぽくて、いろいろと自分で頑張って書かないといけない感じがした。

ReactNative を始める前は、細かいことはいい感じにやってくれると思っていたので、そこらへんの自分の期待値とのギャップがあり、最初「やるぞ!」となってた気持ちが萎えてしまった。

開始から辞めるまで 3 時間だったので、深いところまで行きつけてないし、Xcode での開発よりいいところもあったけど、今後ずっとこれ書いていくのはやだなあ、と思ってしまった。

ReactNative それ自体に関しては、ロジックは iOS / Android 共用できる強さがあるし、軽く触った感じ Native 部分は隠蔽されているので Web エンジニアでも全然書いていけると思う。

2016年10月時点の自分の印象としては、iOS / Android ネイティブで書いていけるなら、それぞれちゃんと書いたほうが良いのでは、という感じです。

最高の同期

2014年入社である、最高の同期から送別された。
最高の同期からピカチュウグッズをたくさんいただいた、大変ありがたい。

メモ、クッキー、キーホルダー?

f:id:star__hoshi:20160930005400j:plain

Tシャツ

細かいピカチュウが 1 匹のピカチュウを作っている T シャツ。

f:id:star__hoshi:20160930005601j:plain

この中に一匹、メタモンがいる! とのこと、多分しっぽの先にいたこいつだ。
探すのに 5 分以上かかった。

f:id:star__hoshi:20160930002040j:plain


中途で入るということは、そこには同期というのは存在しないということなので、少し寂しいものがある。
同じ部門内の同期とはちょいちょい雑談をしたり他部署の裏話を聞いたりしていたが、気軽にそういう話ができないというのは少し悲しい。

もうすぐ弊社が御社になるが、同期は同期なままなので、ちょいちょいメシでも食えればなと思う。

新しい環境でもがんばるぞい。

送別会があった

弊社の方々に送別された。
自分のような自己中のゴミにもかかわらず、送別会はたくさんの人に送っていただき、大変嬉しかった。

弊社最高など書かれたピカチュウのシールをいただき、これまた大変嬉しい。
転職先の Mac に貼っていくぞ 💪

f:id:star__hoshi:20160913222915j:plain

また、スターほし と書かれたボールペンもいただいた、大変嬉しい。
これまた転職先ならびに私生活で使っていこうと思う。

f:id:star__hoshi:20160913222846j:plain

写真は載せないが、上記以外にも色紙などいただき、ありがたいお言葉などが書かれており大変嬉しい。

先日の退職エントリでは文句もいろいろ書いたが、いい面もあり、皆さん優しく人間としてちゃんとしている。
やはり弊社は最高である。

転職先でも頑張れ、という声をかけていただけたし、どれだけやれるかわからんが自分がやれることは精一杯やろうと思う。

各位にはお世話になりました、ありがとうございました。
弊社最高!!!!!!!!!!!!!!

転職することにした

新卒から 2 年半勤めた会社を辞め、転職することにした。
せっかくなので、なぜ転職したか、どう転職活動をしたかなど書き記しておく。

弊社

入社してから 1 年は Java で Web サービスの運用などをし、2 年目以降は Cordova でハイブリッドアプリ 開発を主にやっていた。

ハイブリッドアプリの方は割と学ぶことが多くて、 Angular やら TypeScript やらでワイワイやっていた。
一緒に働いていた先輩たちが非常に頼りになる人で、開発からビジネスまでいろいろ教えて貰いとても良い経験になった。

そのままアプリを作り続けるのも悪くなかったが、いろいろ人生について考えた時に弊社で働くより別の会社でやっていきたいと思い転職活動をした。

転職理由として一番大きいのは大企業病みたいなやつで、申請とか承認とか調整とかそんな感じのことばっかりに労力持っていかれて、俺がしていることは何の意味があるのかわからなくなってしまった。
会社としても技術力高めていこう💪 ではなく、上流行程やマネジメントをやっていきましょうという感じで、自分も徐々にマネジメントの割合も増えてきて開発の時間が確保できなくなっていった。

弊社のセキュリティ規準も納得できない点が多かった。
SaaS 使うためにセキュリティに問題がないか申請が必要で、「申請には半年くらいかかるし、通るかもやってみないとわからないけど、本当にやる?」と言われ、いやそんな申請するなら会社辞めるわって感じで転職活動始めた。

セキュリティ意識が高いのは大変良いことだが、時代の変化についていくことを拒否しているように見ええたし、「クラウドは危険!やめよう!」という会社じゃなくて「クラウド便利!どんどん業務改善していこう!」という方が性に合っている。

仕事内容は新規事業とかやってて弊社の中では割と楽しいことをしているポジションだったが、新規事業なのに上記理由で全然スピード感とか出なくて新規事業やる会社じゃないと思った。

また、個人的にスマホアプリを作るのが楽しくて仕事でもアプリを作りたかったのだが、いかんせん弊社がスマホアプリにあまり力を入れておらず、スマホアプリを作ってるのが自分しかいない(しかもハイブリッドアプリである)。
社内の人とアプリについて話しても「web で良くね?」みたいな人多くてスマホアプリについて話せる人も 2, 3 人しかいなかった。
自分が抜けるので弊社にスマホアプリエンジニアが不在になるが、まあそもそも力を入れていないので大したことではないだろう。

転職活動

転職活動は Wantedly を使い、プロフィールを書いてオファーをくれた会社に話を聞きに行っていた。
転職の動機が現職に対する不満だったため、どういう事業は特に希望がなく、自分から話を聞きに行くことはせずオファーを待った。

Wantedly に今までやってきたこと、これから何をしたいかなどを記入したら結構たくさんの会社からオファーをいただけて嬉しかった。

GitHub にあげてるコードとか Qiita に投稿した記事とかを見てもらってからオファーをもらっていたので話がサクサク進み、面接行ったらその場で内定みたいなところもあった。

内定もらったのは社外で書いたコードやプロダクトを見てもらってなので、仕事以外でやってきたことが無駄じゃなかったんだな、と思ってとてもホッとした。

転職先

次行く会社は教育系のベンチャーiOS アプリがっつりやってくれという感じで楽しみである。
学生 / 学校の先生が使うアプリなどを作っていくので、世のため人のためになる感があり良い。

他にも内定はあったのだが、「一緒に会社もサービスもチームも作っていきましょう」と言われて、はい!と返事した。
エンジニアのリーダーの人とめっちゃ話して(トータル 8 時間くらいは話した気がする)、この人がリーダーやってるなら大丈夫だ、と思ってその会社に決めた。

最後に

次の会社は 10 月 16 日付で入社になるので、あと一ヶ月で弊社が弊社でなくなる。

上記にいろいろと不満な点を書いたが、離職率は非常に低いし同僚も皆いい人で、給料もこれといって不満はないし、自分とは音楽性が合わなかっただけで一般的に見て良い会社だと思う。

弊社を辞めたあとも弊社のサービスは使い続ける予定だし、これからも頑張っていって欲しい。
俺もがんばるぞい。

Swiftの文法などのメモ

最近Swiftの勉強をしていて、詳細!Swiftを読みながらメモを書いた。
他のSwift本と読み比べはしてないので比較は出来ないけど、サンプル数多いし説明もわかりやすいので良いと思う。

以下のメモはブログに公開はしてるけど、自分が後で見返す際に書いたメモなので、他の誰が見てもよくわからないと思う。

数値の3桁区切り

  • 数字はアンダースコア付けれる(可読性向上)
    • 122_100_100 は 122100100と評価される

タプル

  • 復数の値を扱える。型がバラバラでも良い。
    • 配列と似ているが、タプルは追加や削除したりなどは出来ない。
    • 変数の上書きは可能
let product = ("Swift",2014,10.2)
var guest:(String, String, Int) = ("ほしかわ","star",22)
guest = ("star","hoshi",11)
println(guest)
println(guest.0)
println(guest.1)
  • jsonみたいな書き方もできるみたいだ
let tapleLabel = (price:100,tax:8)
let seikyugaku = tapleLabel.price + tapleLabel.tax
println(seikyugaku)

演算子

  • trueとfalseのトグル
flower.hidden = !flower.hidden
  • 代入演算子にはいろいろある
    • /=, %=, &&=, ||=

条件分岐

  • switchでタプル使うとき、変数として値を受け取れる
let size = (4,10)
  switch size{
    case(0,0):
      println("0,0")
    case(5...10,5...10):
      println("規定サイズ")
    case(5...10,let height):
      println("高さ異常\(height)")
    case(let width,5...10):
      println("横異常\(width)")
    default:
      println("どっちも異常")
  }
  • switch で where 使える

for文

  • for in で辞書型を扱うとき、いい感じに扱える
let pokemon = [1:"ふしぎだね",2:"ふしぎそう",3:"ふしぎばな",4:"ひとかげ"]
for (k,v) in pokemon{
  println(v)
}
for pk in pokemon.keys{
  println(pk)
}
for pv in pokemon.values{
  println(pv)
}
  • for文に名前を付けてcontinueとかbreak出来る
xloop:for x in 1...10{
  yloop:for y in 1...10{
        if(x < y){
          print("\n")
            continue xloop
        }
        print((x,y))
      }
}

ストリング

  • 文字を繰り返すとか出来る
let stars = String(count:10,repeatedValue:Character("☆"))
println(stars)

配列

  • 繰り返しの配列とか作れる
var zeroList = [Double](count:10,repeatedValue:0.0)
println(zeroList)
  • ソート
    • sortは破壊的
    • sortedは非破壊的
  • 抽出
    • filterってかけて便利っぽい
let fil = zeroList.filter {$0 >= 5}
println(fil)

let nums = [10,20,30,40,35]
let num0to10 = filter(nums,{(num30:Int) -> Bool in
  return (num30 >= 20) && (num30 < 35)
})
println(num0to10)
  • すべての値で演算を行う
let plusOnes = nums.map{$0+1}
println(plusOnes)

辞書

  • 空の配列を作る
let sizeTable:[String:Any] = [:]
println(sizeTable)

let initDict = [String:Any]()
println(initDict)

オプショナル

  • OptionalValueがnilだったら別の値を使いたい、というとき
    • 以下の例はcountがnilなので2を使う
var count:Int?
let value = 250 * (count ?? 2)
println(value)
var str:String? = "Swift"
if let mes = str{
  println(mes + "world")
}else{
  println("hello world")
}
  • オプショナルチェインニング
    • ドットシンタックスでアクセスするときに、該当の値がnilでもエラーを回避する
class Player {
  var magic:Magic? = Magic()
}

class Magic {
  var spell:String = "puipui"
}

---------
var user:Player = Player()
var spell = user.magic?.spell as String!
println(spell)

関数

  • 可変長引数的なやつ
func sum(numbers:Double...) -> Double{
  var total:Double = 0
  for num in numbers{
    total += num
  }
  return total
}

println(sum(1,2,3,4,3,3,4,3,3))
  • 引数に初期値を設定する
    • 引数3つあって、そのうち1つだけ初期値なしとかも出来るけど、呼ぶときはフォーマット合わせる必要ある。
func customer(who:String = "客") -> String{
  return who + "様こんちは"
}
println(customer(who:"ほしかわ"))
println(customer())
  • 関数の引数は基本的にletだけど、varと明示すれば書き換えられる
func price(var yen:Int) -> Int{
  let tax = 1.08
  yen = Int(floor(Double(yen)*tax))
  return yen
}

println(price(100))
  • 外部引数名
    • 関数を使うときの引数を指定させる
    • 外部引数名と引数名が同じの場合、#つけるといける
func bmi(weight kg:Double, height cm:Double)->Double{
  if cm == 0 {return -1}
  var result = kg/pow(cm*0.01,2)
  result = round(result*10)/10.0
  return result
}
func kngk(#tnk:UInt,ks:UInt) -> UInt{
  return tnk*ks
}

println(bmi(weight:55,height:170))
println(kngk(tnk:250,ks:3))
  • 引数や戻り値が配列の関数
    • Arrayのように型を指定する
func arrayByInt(#array:Array<Int>, num:Int) -> Array<Int>{
  var result = Array<Int>()
  for value in array{
    result.append(value*num)
  }
  return result
}

println(arrayByInt(array:[1,2,5,6,4,6],num:3))
func calc(#a:Int, b:Int) -> Int{
  return a + b;
}
func calc(#c:Int, d:Int) -> Int{
  return c + d;
}
func calc(#a:Int, b:Int, c:Int) -> Int{
  return a + b + c;
}

println(calc(a:1,b:2))
println(calc(c:1,d:2))
println(calc(a:1,b:2,c:3))
  • ジェネリックな関数
    • 引数の方を()のように指定することで、型を未定のまま定義できる
func makeArray<T>(items:T ...) -> [T]{
  var array = [T]()
  for item in items {
    array += [item]
  }
  return array
}

println(makeArray(3,5,3,3,5,"fa"))
println(makeArray("23213","ffa","lfajf"))
  • 関数の引数を関数にする
    • 引数の関数の引数の型とかもかく
    • 引数なのか関数なのかわけがわからなくなってきた
    • 型をたくさん書くのだるい
func helloUser(user:String) -> String {
  return("\(user)さん、はろー")
}
func command(someFunc:String -> String,user:String) -> String{
  let msg = someFunc(user)
    return msg
}

println(command(helloUser,user:"hoshikawa"))
  • returnで関数を返すことも出来る
    • 戻り値の型をちゃんと書くこと、それ以外はJSと似てる
    • なのでソースコードは書かない

クロージャ

// クロージャ
let closureNum = [1,4,0,5,2,4]
let array1 = map(closureNum, {(let v:Int) -> Int in
  return v * 2
})
println(array1)
  • 型推論が適用されるので型宣言を省略出来る
  • 戻し値の式だけでreturnも省略できる
let array3 = map(closureNum, {v in v * 2})
println(array1)
let array4 = map(closureNum){$0 * 2}
println(array4)
//クロージャ
func dicMap(var dic:Dictionary<String,Int>, closure:(String, Int) -> (String, Int))
  -> Dictionary<String,Int>{
    for (key,value) in dic{
      let (theKey,newValue) = closure(key,value)
        dic[theKey] = newValue
    }
    return dic
}

// dicMapを使う
let abcDic = ["a":3,"b":6,"c":1]
let result = dicMap(abcDic){(key:String,value:Int)->(String,Int) in
  return (key,value*2)
}
println(result)

Class

class MyClass{
  let msg:String
  let name:String?

  init(msg:String = "hello"){
    self.msg = msg
  }
  init(msg:String,name:String){
    self.msg = msg
      self.name = name
  }

  func hello(){
    var helloMsg:String
    if let user = name {
      helloMsg = user+"さん。"+msg
    } else{
      helloMsg = msg
    }
    println(helloMsg)
  }
}

----------------

let myObj = MyClass(msg:"MyClass")
myObj.hello()
let myObj2 = MyClass(msg:"MyClass",name:"starhoshi")
myObj2.hello()
  • convenience init
    • initを呼ぶinitを作れる。引数をみると何が呼ばれるのか、違いがわかる
class MyClass{
  let msg:String
  let name:String

  init(msg:String,name:String){
    self.msg = msg
    self.name = name
  }

  convenience init(msg:String = "hello"){
    self.init(msg:msg,name:"匿名")
  }

  func hello(){
    var helloMsg = name + "さん。" + msg
    println(helloMsg)
  }
}

-----------------

let myObj = MyClass(msg:"hello!")
myObj.hello()
let myObj2 = MyClass(msg:"hello!",name:"starhoshi")
myObj2.hello()
let myObj3 = MyClass()
myObj3.hello()
class MyGame{
  class let version:String = "1.0.0"
  class var userCount:UInt= 3
}
class MyClass{
  class func GoodMorning() -> String{
    return "GoodMorning"
  }
}

-------------------

let myObj4 = MyClass.GoodMorning()
println(myObj4)
  • Computedプロパティ
    • ↔Storedプロパティ
    • Computedプロパティそのものは値を保持していない
    • 参照に対してはget,設定に対してはsetメソッドを使う
      • computedプロパティを使う側からすれば、Storedプロパティと区別はつかない
class MyClass{
    var radius:Double = 1.0
    var area:Double{
        get{
            return radius * radius * M_PI
        }
        set(menseki){
            radius = sqrt(menseki / M_PI)
        }
    }
}

---------------

var myCircle = MyClass()
println(myCircle.radius)
println(myCircle.area)

myCircle.area *= 2
println(myCircle.radius)
println(myCircle.area)
  • プロパティオブザーバー
    • プロパティに値がセットされたことをwillSetとdidSetで監視できる
class MyClass{
    init(){
        level = 0
    }

    var times = 0;
    var level:Int{
        willSet{
            if level != newValue{
                println("\(level) -> \(newValue)")
            }
        }
        didSet{
            if oldValue != level{
                ++times
                println("\(times)回目の更新")
            }
        }
    }
}

----------------
var thePlayer = MyClass()
thePlayer.level = 10
thePlayer.level = 10
thePlayer.level = 15
//0 -> 10
//1回目の更新
//10 -> 15
//2回目の更新
  • lazyプロパティ
    • lazy var みたいにStoredプロパティにlazy付けうと遅延評価になる
    • 参照されるまで初期化されない
class MyClass(){
    lazy var home = HomeClass()
    func who(){
        println(home.owner)
    }
}

class HomeClass{
    var owner = "yoshiyuki oshige"
}

--------------------
var home = MyClass()
home.who()
classここから下は長くなるためソースはなし
  • アクセス権
    • クラス・プロパティ・メソッドにはアクセス権を設定できる
    • internal
      • 他のファイルからでもアクセス可能(デフォルト)
    • private
      • 同一ファイル内でのみアクセス可能
      • ptivare(set)とするとリードオンリーになる
    • public
      • どこからでもアクセス可能(一般には使用しない)
  • メソッドのオーバーライド
  • final - 継承やオーバーライドを禁止する
    • final class Human{}
    • final func method()
  • プロトコル
    • javaでいうinterface
  • extension
    • 既存のクラスを書き換えたり継承することなく機能を拡張できる
    • Stringとかintも拡張出来る
      • js のプロトタイプ拡張みたいな感じか

列挙型 enum

  • enumとcaseで記述する
    • 型推論が使えるので、.Sみたいに代入できる
    • enumで参照する値を作るためにはswitch caseでできる
enum MensSize{
    case S
    case M
    case L
    case XL
}
enum WomansSize{
    case S,M,L,XL
}
func packing(size:WomansSize) -> String {
  var stuff:String
    switch size {
      case .S:
        stuff = ".S"
      case .M:
        stuff = ".M"
      case .L:
        stuff = ".L"
      case .XL:
        stuff = ".XL"
    }
  return stuff
}
-----------------
println(MensSize.L)
println(WomansSize.S)
var mySize = MensSize.M
mySize = .S
println(packing(.M))
  • enumに値を定義する
    • 普通にイコールでいける
    • Intで最初だけ数値指定した場合、あとのは1ずつ増えていく
enum Direction:Int{
    case forward = 1
    case backword
    case right
    case left
}
-------------------
println(Direction.backword.rawValue)
  • enumに関数を定義する
enum Prize:Int{
    case Gold = 1,Silver,Bronze,Four,Five
    func description() -> String{
        switch self{
        case .Gold:
            return "優勝"
        case .Silver:
            return "準優勝"
        case .Bronze:
            return "じゅんじゅん優勝"
        case .Four, .Five:
            return "入賞"
        }
    }
---------------------
let myRank = Prize.Bronze
println(myRank.description())
}
  • enumで型を列挙する
  • enumの中でenumを使う
    • アクセスするにはswitch case使わないとだめ?
enum Pattern {
    case Monotone(PColor)
    case Border(c1:PColor,c2:PColor)
    enum PColor:String{
        case red = "red"
        case green = "green"
        case blue = "blue"
    }
}
----------------------------
let shirt1 = Pattern.Border(c1: .red, c2: .green)
switch shirt1 {
case .Border(let c1, let c2):
    println(c1.rawValue,c2.rawValue)
}

struct 構造体

クラスと同じような機能を持ち、書式もほぼ同じ。
だが継承をしないのでスーパークラスを設定する書式はない。

  • structでは初期値設定できる
//初期値なし
struct ColorBox{
  var width:Int
  var height:Int
  var color:String
}
// 初期値有り
struct WhiteBox{
  var width:Int = 100
  var height:Int = 100
  var color:String = "white"
}
--------------------
var redBox = ColorBox(width: 100, height: 100, color: "red")
var theBox = WhiteBox()
println(theBox.width)
  • classと同じくイニシャライザを作れる
    • init()
  • classは参照型だが、structは値型
    • structはコピーを作成する
  • 関数とかもclassと同じく作れる
    • だが構造体自身のプロパティを変更する際に関数を使ってできない
    • mutating をつける
      • mutatingを付けたメソッド内でプロパティの変更を行う
struct Line{
    var p1:Point
    var p2:Point
    mutating func move(#h:Int,v:Int){
        p1.h += h
        p1.v += v
        p2.h += h
        p1.v += v
    }
}
------------------------
theLine.move(h:50,v:20)
let p1 = theLine.p1
let p2 = theLine.p2
println(p1.h,p1.v)
println(p2.h,p2.v)
  • subscript
    • 構造体の要素に[]を使ってアクセスできる
    • structa[1], structB["a",2]みたいにアクセスする
struct Stock {
  var name:String
    var data:[String:Int] = [:]
    init(name:String){
      self.name = name
    }
  subscript(color:String,size:Double) -> Int{
    get {
      let key = color + size.description
        if let value = data[key]{
          return value
        }else{
          return 0
        }
    }
    set{
      let key = color + size.description
        data[key] = newValue
    }

  }
}
------------------------------
var addixStock = Stock(name: "addix")
addixStock["green",24.5] = 4
addixStock["green",24.0] = 1
addixStock["green",24.5] = 7
addixStock["green",24.0] += 1
println(addixStock["green",24.0])
println(addixStock["green",24.5])

TitaniumでFailed to start "com.apple.syslog_relay"

Titanium Mobileでアプリ実機転送しようとしたら

[ERROR] :  Failed to start "com.apple.syslog_relay" service (0xe800007f)
[ERROR] :  Project failed to build after 27s 199ms

って言われた。

iPhoneのLightningケーブル抜いてもっかいビルドしたら直った。
よかった。

参考: [TIMOB-17595] iOS: The app fails to install on iOS device after a second iOS device is also plugged in - Appcelerator JIRA