読者です 読者をやめる 読者になる 読者になる

ボクココ

熱海で開発するブログ

iOS Swift プッシュ通知を受け取ってからViewControllerに情報を渡す

iOS

久々の iOS のトピック。今回はiOSで初めてプッシュ通知(Push Notification)を利用したので、それのメモ。

プッシュ通知について

サーバーの任意のタイミングで情報を端末に送信する仕組み。これぞアプリのできること、として欠かせない機能だ。何か最新ニュースが出たりとか、リアルタイムで情報を送りたいときによく使われる。てか人気アプリで使ってないところはないくらいな機能だ。

Webアプリでは任意のタイミングでブラウザに情報を渡す仕組みを実装するには、やや特殊なことをしないと難しい。(Web Socket や Polling などを利用するだろう)

設定手順

  1. Apple Developer にて 証明書を作成
  2. Amazon SNSで作成した証明書をアップロード
  3. サーバーにてAmazonSNSの通知を発行
  4. iOSアプリで通知を受け取る

Apple Developer で証明書作成

iOSはコードは簡単だけど、Apple Developer での登録がめんどくさい。まずはPush Notification を利用できるようにするために証明書を作成する必要がある。

AppIDでプッシュ通知したいアプリを作成し、 ここのPush Notifications の項目をオンにして作成。そんで作成したAppIDを選択し、Edit から CreateCertificate を選択。キーチェーンアクセスで作成したcertSigningRequest ファイルをアップロードし、証明書を作成する。

その証明書をキーチェーンアクセスへドラッグアンドドロップして、それをディスクに書き出す。これがp12形式にして書き出す。これに合わせてプロビジョニングファイルも作成しておこう。

Amazon SNS でのプッシュ

Amazon SNS を使えば、 iOS, Androidの区別をほとんどきにすることなく、プッシュ通知を送ることが可能だ。しかも何万通もプッシュ通知送っても料金は非常に低価格。これは使わない手はない。

http://d2wwfe3odivqm9.cloudfront.net/wp-content/uploads/2014/05/Amazon_SNS-320x320.png

Application で Apple Development を選択し、 p12 ファイルをアップロード。その後Load Credential from file を押すと作成ボタンが押せるようになる。作成したApp Arn をサーバーから参照できるようにしよう。今回はAWS_SNS_APP_ARNとして環境変数に保存する。

サーバーにてAmazon SNSの通知を発行

今回は Ruby を例に、発行方法を記述。bundler にて gem "aws-sdk"sdkをインストール。

config/initializers/aws.rb

Aws.config.update({
  region: ENV['AWS_REGION'],
  credentials: Aws::Credentials.new(ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY'])
})

app/controllers/samples_controller.rb

      sns = Aws::SNS::Client.new

    response = client.create_platform_endpoint(
      platform_application_arn: ENV['AWS_SNS_APP_ARN'],
      token: device_uuid
    )
    target_arn = response[:endpoint_arn]

      message = {
        "APNS" => {
          "aps" => { "content-available" => true },
          "other_data" => obj
        }.to_json,
        "APNS_SANDBOX" => {
          "aps" => { "content-available" => true },
          "other_data" => obj
        }.to_json
      }
      push_parameters = {
        target_arn: target_arn,
        message_structure: "json",
        message: message.to_json
      }
      resp = sns.publish(push_parameters)

objに送りたいデータ、target_arn に送り先のTargetArnを選択しよう。target arn は iOSアプリから送信されたトークンを元に作成する。今回の変数でいうdevice_uuidだ。

iOSアプリで通知を受け取る

これは簡単。 AppDelegate にて任意のメソッドを実装するだけだ。JSON解析のためにSwiftyJSONを利用している。

class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        
        // 通知を許可
        var types: UIUserNotificationType = UIUserNotificationType.Badge |
            UIUserNotificationType.Alert |
            UIUserNotificationType.Sound
        var settings: UIUserNotificationSettings = UIUserNotificationSettings( forTypes: types, categories: nil )
        
        application.registerUserNotificationSettings( settings )
        application.registerForRemoteNotifications()

        return true
    }

    // Device Token を取得
    func application( application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData ) {
        var characterSet: NSCharacterSet = NSCharacterSet( charactersInString: "<>" )
        
        var deviceTokenString: String = ( deviceToken.description as NSString )
            .stringByTrimmingCharactersInSet( characterSet )
            .stringByReplacingOccurrencesOfString( " ", withString: "" ) as String
        
        println( deviceTokenString )
        // このトークンをサーバーに送って保存
    }

    // プッシュを受け取ったとき
  func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
        let json = JSON(userInfo)
        
            let notification = NSNotification(
                name:"my_push",
                object: nil,
                userInfo:[
                    "title": json["other_data", "title"].stringValue,
                ]

            NSNotificationCenter.defaultCenter().postNotification(notification)
        }

        completionHandler(.NoData)
    }
}

NSNotificationCenterからmy_pushという名の通知がアプリ内に飛ぶので、それをViewController側でキャッチしてあげます。

class ViewController: UIViewController {
    override func viewDidAppear(animated: Bool) {
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "handlePushNotification:", name: "my_push", object: nil)
        
    }

    
    override func viewDidDisappear(animated: Bool) {
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
    
    // observer fired
    func handlePushNotification(notification: NSNotification) {
       // 通知を受け取ったときの処理
    }

}

終わりに

Androidは設定は簡単だけど、実装が面倒。 iOSは設定が面倒だけど実装が簡単。そんな印象。iOSではデフォルトでAndroidでいうEventBusのような仕組み(NSNotificationCenter)を提供してくれてるのがありがたい。乱用するとカオスになりかねないが、こういうときには役立つ機能だと思う。

ただ通知する中身が空だったり間違ったりすると通知が来なかったりするパターンがあるので、送るデータがちゃんと入っているかを確認したほうがいい。これにかなりつまずいた。

Heroku Postgres にあるデータのダンプとインポート

最近は運用系の記事が多くなってきたな。。メモ書き。

Heroku にあるデータをローカルに持っていきたいとか、テスト環境のpostgresにデータを入れたいとか、そういったことをやってみる。Heroku コマンドの pg:backuppg:restore はテーブル全体なので、個別だとpsql, pg_dump をリモートで接続して対応していくことになる。

接続情報の取得

Heroku Postgres は直接psqlコマンドでデータをいじることが可能だ。まずはその接続情報を取得する。

$ heroku pg:credentials --app APP_NAME HEROKU_POSTGRESQL_****

Connection info string:
   "dbname=****** host=*****.compute-1.amazonaws.com port=5432 user=****** password=**** sslmode=require"
Connection URL:
    postgres://*****:****@****.compute-1.amazonaws.com:5432/****

HEROKU_POSTGRESQL_***の名前はHeroku Postgresのアドオンのページ内に書いてある。

データをダンプ

以下のようなコマンドを叩くことで取得できる。

$ pg_dump --no-acl --no-owner -h [host ip].compute-1.amazonaws.com -U [user name] -t [table name] --data-only [database name] > table.dump

pg_dump: server version: 9.4.1; pg_dump version: 9.3.1 のような警告が出たら、 brew upgrade postgresql でアップデートしたらいけた。

データをインポート

idとか制約入っている場合とかごっそり書き換えるのでまずはテーブル削除

$ echo "truncate [table];" | heroku pg:psql [DATABASE] -app YOUR_APP

そのあとデータをインポートしよう。

$ heroku pg:psql --app YOURAPP DATABASE < table.dump

Heroku の DBバックアップを定期的に行う

最近はHerokuのアドオンがまた増えてきていて、そういう新しいアドオンをチェックするのも一興だ。最近はCIツーリなんかもアドオンで無料でできたりする。無料だと月100件までだけど、本番環境ならそれでも十分な気がする。

今回はアドオンの中でも標準的な Heroku Postgres のお話。基本的にはドキュメントを読めばいいのだが、いかんせん英語なもので苦手な方には中々苦労することだろう。

Heroku Postgres は無料で2つバックアップを保存することができる。2つ以上バックアップした場合は古い順から消えていく仕組み。ただデフォルトでは自分でコマンド叩くなりWebインタフェースからバックアップしたりとマニュアルなことが必要。これを自動化させよう。

これもドキュメントに普通に書いてあるので、その通りにやろう。

heroku pg:backups schedule --at '02:00 Asia/Tokyo' DATABASE_URL

これで毎晩朝2時にバックアップがスケジューリングされた。これで問題が起きた時はバックアップからデータを復旧させられる。

Heroku Postgres のTIPs

Dataclips

面白いこととして、 Dataclips 機能というものがある。これはデータのレポート機能といってもいい。ユーザー数など、定期的に情報を取得したいもので、他の人でも把握したいようなデータの場合、簡単なSQLを書いておくことでURLにアクセスする度に最新データが取ってこれる仕組みだ。

これで例えばビジネス側の人に今ユーザー数どんくらい?って言われた時にDataclipsのURLを渡すだけで最新情報が取ってこれるようになる。

もちろんcountだけじゃなくて実際のデータもインポートできるので、よくあるSQLアクセスの定期的なものは Dataclipに保存しておくと便利。

DaaS

Heroku Postgres は Database as a Service だ。これは別にHerokuアプリじゃなきゃ使えないというわけではなく、例えば EC2やその他のサーバーのアプリのDBをHeroku Postgres にすることも可能であるということだ。てことでHerokuの2つのアプリが1つのPostgres を参照することも可能。

この事実を案外知らなかった人は多いのではないだろうか。これらをうまく使えば運用は劇的に楽になるはずだ。ちなみにHeroku postgres を使えば1万行まで無料で使える!

もう自前でDBを持つ時代ではないことは明らかだ。

Sass をより効率よく使うための機能4つ厳選

Sass といえば Rails でデフォルトで搭載されている, CSSを描きやすくするためのライブラリだ。「CSSをネスト構造で記述できる」というだけでSassの80%以上使いこなしていると言えるのだと思っているけど、残りの数%について、特に便利なものを調べたのでまとめてみる。

親要素の参照

hover とか、そういうのをネストの中に含めたい時。 &を使って表現できる。

a {
  text-decoration: none;
  &:hover { text-decoration: underline; }
}

変数

よく使う色の値とかを変数にまとめられる。毎回、直にRGB指定する必要がなくなる。 $を要素にすれば代入、値にすれば参照となる。

$width: 5em;

#main {
  width: $width;
}

CSS拡張

ありがちな "btn btn-primary" のような指定を、"btn-primary"だけでよくする。@extendを使って共通部分をインクルードできる。

.btn {
  border: 1px #f00;
  padding: 12px 4px;
  background-color: gray;
}
.btn-primary {
  @extend .btn;
  background-color: blue;
}

関数的な使い方

opacity とか、ブラウザ互換のためにそれぞれを書かなくてもよくなる

@mixin opacity($percent: 0.6) {
  filter:alpha(opacity=$percent * 100);
  -moz-opacity: $percent;
  opacity: $percent;
}

a:hover {
  text-decoration: none;
  @include opacity();
}

おわりに

実際は for とか if とかも書けちゃって、もはや一つの言語のようになっている。ただそれらはCSSではそこまで使用することもないかと思うので、ネスト構造としてだけ使っていたSassユーザーの方はとりあえずこの4つを使いこなせるようになれればさらに効率よくCSSが書けるのではないだろうか。

そのほかの機能は本家ドキュメントを参照してほしい。

AWS Summit に参加して感じた新しいWEBシステムのかたち

AWS

http://www.awssummit.tokyo/images/header_logo.png

6月3日の AWS Summit Day2 に参加した。

AWS Summit Tokyo 2015 - クラウドで、未来を「今」に。- 2015年6月2~3日 グランドプリンスホテル新高輪にて開催

今までのAWS Summit といえば、どちらかというと大企業が AWS を導入を検討する、事例を知るのに集まる場としてスーツ姿な方々が集まるようなカンファレンスなイメージだったが、今回から デベロッパーカンファレンス という開発者向けのイベントも同時開催だったため、こちらに参加した。まぁとはいえそこでも割と大きめな企業が事例の紹介とかが多かった。ぶっちゃけ事例話は自分的には興味がない。そういう企業のスタイルを真似る必要はないと思っている。起きた目の前の問題に対して自分で調べて解決すればいいだけの話なのだから。事例の発表はたいてい自社のサービスのプロモーションのことと、起きた問題、経緯の話くらいがほとんどで、最終的に We're Hiring で終わるのよね。

その話はさておき、発表の中でAWSの中の方々による発表で2つの新しいシステムのかたちを感じることができた。今回はそのレポートをする。

2 Tier Architecture

AWSAPIサーバーレスなサービスを実現することができる。従来のAPIサーバーといえば、自前でサーバーを立ててリクエストをHTTPで受け取り、DBとやりとりして最終的にレスポンスを返すものが基本だ。これを最新のAWS技術を使えば、必要なくなる時代が来るのかもしれない。

2 Tier Architecture で必要なのは、Amazon Cognito, Amazon S3, Amazon DynamoDB を中心としたサービスだ。各AWSを容易に扱うための AWS SDK を使って開発することが前提となる。どんなことができるか?

まず Amazon Cognito で各種AWSにアクセスするためのクレデンシャルを得る。Amazon Cognito にて、S3 や Dynamo DBにブラウザやアプリからアクセスするための権限をIAMで設定し、それとひも付けたCognito ID を取得する。CognitoID通じて直接各種AWSにアクセスすることが可能となる。

AWS SDKを利用してユーザー共通のDBをそれぞれのクライアントからCRUD操作できたり、画像アップロードなどが簡単に行える。DBを軽く扱うだけのサービスならこれだけでAPIサーバ不要なサービスを構築することができる。自分の認識では以上が 2 Tier Architecture と呼ばれるものだと認識している。

もちろんちょっと踏み込んだサービスなら、サーバ側で処理させるといったことも必要になってくると思う。そんな時に出てくる大注目のサービス、 Amazon Lambda がある。

Amazon Lambda

去年からちょいちょい話題になっているAmazon Lambda、何ができるかというと、

  • 各種AWS(S3, Cognito, Kinesiss, DynamoDBなど)のイベントを受け取り、
  • 任意のタイミングに応じて独自の Node.js コードを実行させる

というのが基本。Node.js コードとあるが、もう割となんでもできる。Node.js サーバがあるようなものだ。ライブラリはnpmで取ってきてそれごとアップロードすればOK。コードをS3に置いてそれをLambdaに参照させることも可能とのことだ。Lambda上で他のAWSを呼び出したり、外のAPIを呼び出したりできる。ただ Lambdaファンクションは一度実行したら基本それで終わりなので、データはDynamoDBなどの外部に保存する必要がある。

どんなことができるのか?

  • S3 アップロードを検知して画像をリサイズするLambdaファンクションを呼び出す
  • S3 にテキストをアップロードしたタイミングで管理者にメール送信
  • DynamoDBに入ったデータを適宜取得して、値の整形をする
  • Kinesiss にデータが入ったタイミングでそれらを集計してDynamoDBに保存
  • Cognito にユーザーが登録した時点で通知

などなど。可能性を感じるサービスだ。

ただ、ここまではLambdaの概要を読めば誰でも知ってたこと。ここからが新しい発見だった。 Amazon Lambda はカスタムイベントを登録することができる。何を意味するかというと、API サーバーとしての役割をAmazon Lambda が担うことができるということだ。

モバイルからでもWebからでも、それぞれ任意のタイミングでLambda ファンクションを実行(Invoke)することができる。実行した結果をレスポンスで受け取りたいなら同期処理、送るだけでいいなら非同期処理の二つの実行タイプが用意されている。同期処理にすれば完全にAPIサーバーとして利用出来る。

ちなみに今年の夏、晴れてAmazon LambdaのTokyoリージョンが開設されるらしい。これにより、S3でtokyoリージョンで運用していたサービスもLambdaを利用することができるようになる。

Lambdaを利用するメリット

  • サーバーを構築する必要がない
  • スケールを考慮する必要がない
  • コードが動いた時間分だけの課金なのでお得。しかも無料枠つき
  • Android, iOS, JavaScriptAWS SDKによりネットワーク処理を簡単に記述できる
  • 今までリクエスト受けたらキューに溜めてたような処理をLambdaに肩代わりさせられる

感じたデメリット

  • デバッグがいまのとこ面倒(アップロードして、Amazon CroudWatch で見る、実際に送ってレスポンスを見るなどくらい)
  • Node.js しかサポートしてない (今後Javaが出るみたい?順次対応予定の模様)

今後の新しい開発スタイル

ちょっとしたWebサービスならS3にHTML/CSS/JavaScript/画像を置いて、Amazon Cognito 経由でデータをアップロード。任意のタイミングで Lambdaを動かしてちょっとしたサーバーサイドの実行もさせてDynamoDBにデータを保存。

こんな開発スタイルがもう既にできるし、簡単にできる時代になっている。うまく運用すればサーバーサイドプログラムを持つ動的なWebサービスを超低価格で実現できるようになるだろう。

そんな新しい可能性を感じることのできた、AWS Summit であった。

成果をあげるプログラマーを目指して

今までは自社サービスばかりを開発・運用してきたのであまり気にしたことはなかったけど、起業してから「こんなの作ってくれない?」的な受託めいた話が増えてきた。

そこで私の考えるプログラマーの成果そして受託開発についての見解を書いてみようと思う。

書いたコードが自分の資産となるか

今、自分の書いたコードが、将来どの程度リターンとして戻って来るのか? ここを考える必要があると思う。そのリターンはお金でもあるし自分の技術やサービスの成長などが挙げられる。

その点、自社サービスは書いたコード全てが今後に活きる。改善すればするほど、サービスは(きっと)よくなるため今書いたコードが将来にわたって意味をなしてくるし、定期的にお金も生み出せる。

これこそが受託の決定的な問題だと考える。自分の書いたコードが相手に渡ってしまうのだ。自分のかけた時間が全て相手に渡ってしまう。この「作って終わり」のパターン。これではその場限りの受託費用というお金だけが入ってきて、他は何も残らなくなる。これがプログラマーにとっては致命的に痛い。

それを防ぐために、最近は 納品のない受託開発 (納品せず改善を繰り返す開発パートナー的立ち位置)とかが話題になっている。この考えはとても良いと思う。なぜなら自分の書いたコードが今後も自分にとって価値のあるものとして続いていくからだ。

ただ目の前のお金のためにそれを飲まなければならないこともあるだろう。そんな時は、納品で終わりスタイルだとしてもコードを断片化して次に活かせるように共通化を心がけたり、ノウハウだけでもちょこちょこ貯めることを意識する必要がある。自社サービスや納品のない受託開発でもこれらは必要だが、それ以上に必要になってくると思う。そうしないと自分に残るものが"何もない"からだ。"何もない"というのは大抵の受託開発ってのは開発スキルはそんなに必要ないというのも一因としてある。

言われたのをやるだけのプログラマー

ちょっと話題はそれるが、受動的なプログラマーはたぶんこの仕事に向いていないかもしれない。技術は必要になったら勉強する、言われたらとりあえずハイといって後で苦しむ、目の前の課題を乗り越えるのに必死で今後を全く見ていない。プログラマーが辛いと言われる根幹はここにある気がしている。

そこを乗り越えて自分からモノを作り、勉強し、将来を見る。それができたらこの仕事は最高に素晴らしいものになるはずだ。そんな野心あふれるプログラマーを目指して、引き続き頑張っていこうと思う。

サービス改善することの難しさと大切さ

素晴らしいサービスとそうでないサービスの違いはなんだろう?

みんな当たり前のようにいいサービスを作ろうと目指しているけども、なかなかできないことが多い。それの決定的な理由が、"修正が大変だから" の一言に集約されると思う。

修正することの難しさ

一度作ったものを直すことは、新しく作るよりも難しい。しかも修正する対象が、今まで自分が作ってこなかったシステムならなおさらだ。 修正が怖いから、修正すると大変だから、修正することを諦めて、機能追加する道を選ぶ。そっちの方が簡単だからだ。

その行き着く先は虚しい。 本当に使ってくれるかわからない機能が大量にあってごちゃごちゃした使いにくいサービスが出来上がる。引き継ぎを繰り返すプロダクトは基本的にこうなる運命にある。

それを打ち砕くには、修正できる能力が必要だ。その能力を得るためには、既存のシステムを深く理解し、一部を修正したことで起きる影響などがすぐ把握できるようになる必要がある。これが1から作った人でないと本当にきつい。どんなプロでもこれは難しい。ひどい作りのシステムなら尚更で、そういうコードがむしろ多い。そんな場合は作り直したりすることもあるだろう。

あなたはそのシステムの根本を修正できるか

たくさんのユーザーフィードバックを得たとき、その修正を無理と思うか、直せるか。ここの違いでサービスの良し悪しは決まる。そこに必要なのは技術力の高さなのか? きっとそうじゃない、必要なのはシステムを理解するための根気と、その根気を生むためのサービス愛だ。

それができないようなら、自分が1からサービスを作って修正できる環境にするしかない。

これができないところが多いと思う。1から作ったプロダクトでさえ修正はモノによっては難しいと思う。あなたの環境では大規模な修正ができるだろうか?ぜひ考えてみてほしい。できない時点で、あなたのサービスは良いものにはならないだろう。