Realm Mobile Platformを触ってみた
Realm Mobile Platformがリリースされた
これまでのクライアント独立のローカルDB機能に加えて
リアルタイムの同期、その場合に発生しうるコンフリクトの解決、イベントハンドリング等が出来るようになった
Realm Mobile Platform: リアルタイムの同期と、Coreのオープンソース化
イメージ的にはFirebaseのリアルタイム同期型データベースと同じようなものっぽいけど MBaasのようなクラウドサービス提供型ではないのでサーバは自前で運用する必要がある
デモアプリを動かしてみる
まずはrealm-mobile-platformのmacOS bundleをダウンロード
Realm Mobile Platform: Getting Started
解凍したファイル中のstart-object-server.command
を実行するとrealm-object-serverが起動し
ブラウザでlocalhost:8090/#startupが表示される
初回アクセス時にemailとpassを設定する
demo/RealmTasksの中のRealmTasks.appを起動して適当に動かしてみる
最初にusernameとpasswordを聞かれるので先ほど設定したemailとpassを入力し、TODOリストを幾つか登録してみる
ここで、同じく解凍したディレクトリの中にあるRealm Browserを開く
起動したらConnect to Object Server を選択
ServerURLにrealm://127.0.0.1:9080
を指定
さらに
In order to connect as admin we need an admin access token. In the terminal running the server, look for a line starting with Your admin access token is:. Copy the long token key string
realm-object-serverを走らせてるターミナル上に記載されてるアクセストークンを
設定しろと書いてあるので確認してコピペしてAdmin Access Tokenに指定する
起動すると、先ほどデモアプリで入力したデータが登録されている
これはアプリ内のrealmファイルではなくサーバ側に連携されたデータになる
さらにiOS版のアプリもコードがあるのでシミュレータで起動してみる
ちなみに、ダウンロードしたディレクトリ内のiOSサンプルファイル群にPodfileが見つからなかったので
Githubから持ってきた改めてソースを取得し直した
が、pod installでエラーになる
とりあえずPodfileのgit@としているところをhttpsに変更したら回避できた
pod 'Realm', git: 'https://github.com/realm/realm-cocoa.git', branch: 'master', submodules: true
pod 'RealmSwift', git: 'https://github.com/realm/realm-cocoa.git', branch: 'master', submodules: true
先ほどのmac版のデモアプリと同様にログインしてアプリを起動するとmacアプリで入力したデータが表示される
さらに片方でデータを追加、削除等の操作をすると
するともう片方のアプリで即座に反映するのが確認できた
実装手順をみてみる
詳細はこちらで
The Realm Mobile Platform
まず、Realm Object Serverへの接続URLを定義する
let serverURL = NSURL(string: "http://my.realmServer.com:9080")!
次に認証情報を設定する(ドキュメントをみると独自のid / pass以外にgoogleやfacebook、icloud等の認証も使えるっぽい)
let usernameCredential = Credential.usernamePassword(username: "username", password: "password", actions: [.createAccount])
let googleCredential = Credential.google(token: "Google token")
let facebookCredential = Credential.facebook(token: "Facebook token")
let iCloudCredential = Credential.iCloud(token: "iCloud token")
そして認証処理を行い成功した場合userインスタンスを取得する
User.authenticate(with: credential, server: serverURL) { user, error in
if let user = user {
// can now open a synchronized Realm with this user
} else if let error = error {
// handle error
}
}
最後に、serverURLとuserインスタンスを使ってRealmのconfigurationを作成、realmインスタンスを作成する
// Create the configuration
let syncServerURL = URL(string: "realm://localhost:9080/~/userRealm")!
let config = Realm.Configuration(syncConfiguration: (user, syncServerURL))
// Open the remote Realm
let realm = try! Realm(configuration: config)
以降はこのrealmインスタンスを使えばデータの更新時にRealm Object Serverに連携されるようになる
ざっと実装方法を見ると単独で使用していた時とほとんど変わらずサーバ同期が簡単に可能になるようだった
その他雑感
コンフリクトについてはデフォルトでは以下のようなルールになるらしい
さらにルールのカスタマイズも出来るっぽい
- Deletes always wins
- Last update wins
- Inserts in lists are ordered by time
- 一方で削除された場合は常に削除が優先される
- 双方で更新がされた場合は最終更新が優先される
- 同じ位置にデータが登録された場合は時間順になる
あとは、大規模なサービスになった時にどの程度のサーバスペックが必要なのかとかスケールアウトはどうするのか
ちなみに、↓を見るとproxy moduleはNginx等に置き換えることも可能なようだった
Realm Object Server : Proxy
他にも気になるところは色々あるけど今回はとりあえずこの辺で。。。
xxdコマンドで画像データを16進数変換してiOSライブラリ内に画像データを内包する
iOSでstaticライブラリを開発していると画像等を同梱したい場合がある。
通常だとaファイルの他にbundleを作成する必要があるが、
これをaファイルの中に同梱する方法を調べた。
やり方としては、画像などのバイナリデータを一旦16進数情報に変換、
ライブラリのソース内にchar配列として保持し、
それを実行時にNSDataで読み込みUIImageに変換する感じになる。
バイナリデータを16進数に変換する
xxdコマンドでバイナリデータを16進数に変換できる。
例えばあるpngファイルをxxdコマンドにかけると以下のような出力になる。
$ xxd original.png
00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452 .PNG........IHDR
00000010: 0000 02ee 0000 0536 0802 0000 0045 b5a4 .......6.....E..
00000020: 5100 0000 0173 5247 4200 aece 1ce9 0000 Q....sRGB.......
00000030: 4000 4944 4154 7801 ecbd 0794 1dc7 79ef @.IDATx.......y.
00000040: 79f3 e4c1 2067 2232 8101 cc20 0966 5282 y... g"2... .fR.
00000050: a8b0 b244 59d2 932c 796d e959 b2fd deca ...DY..,ym.Y....
00000060: a215 8eed b59f c33b 4eeb f5f3 8ab6 f73c .......;N......<
00000070: 9f77 1cf6 59b6 ec5d db92 455a 6612 730e .w..Y..]..EZf.s.
00000080: 2041 82c8 3963 1027 cfdc b4bf 3b05 140b A..9c.'....;...
00000090: dd7d efdc 7bfb cef4 0cf0 ef83 33a8 aeae .}..{.......3...
さらに、pオプションをつけるとプレーンな16進数出力になる。
$ xxd -p original.png
89504e470d0a1a0a0000000d49484452000002ee00000536080200000045
b5a451000000017352474200aece1ce900004000494441547801ecbd0794
1dc779ef79f3e4c1206722328101cc2009665282a8b0b24459d2932c796d
e959b2fddecaa2158eedb59fc33b4eebf5f38ab6f73c9f771cf659b6ec5d
db92455a6612730e204182c839631027cfdcb4bf3b05140bdd7defdc7bfb
cef40cf0ef8333a8aeaefaaaead7b7bbfefd557575fc81071e88c7e3b132
5bb15834474863c3c4d8b027be8c9980e80a8506a46e5c94ad79e34cca92
08888008888008348c4054fd63c31a50bb218f9630bb2e8742a19048245c
c36e6f9e62c7ee938ed46e660e99ddd1542559e31e65d7e6750ba81cf658
a89cb8e147a32dbde1cd914111100111100111983c042aab82725db02797
逆に、16進数出力結果をバイナリ形式に変換することもできる。
その場合はrオプションをつける。
xxd original.png > original.hex
xxd -r original.hex > revert.png
オプション無しの出力結果の他に、-pの出力結果もrevertできる。
その場合はpオプションを付与する。
xxd -r -p original.hex > revert.png
画像データをCのchar配列形式で出力
ここからが本題で、xxdコマンドにiオプションを付与すると以下のように画像データをCのchar配列形式で出力できる。
$ xxd -i original.png
unsigned char original_png[] = {
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x02, 0xee, 0x00, 0x00, 0x05, 0x36,
// 中略
0x80, 0x10, 0x10, 0x02, 0x42, 0x20, 0x4f, 0x08, 0xfc, 0xff, 0x2c, 0x24,
0xa4, 0xb5, 0x8f, 0x74, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45,
0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
unsigned int original_png_len = 954870;
これをNSDataに食わせて、それをUIImageとして扱うことでソースから画像を生成することができる。
+ (UIImage *)revertHexImage
{
unsigned char original_png[] = {
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
// 中略
0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
unsigned int original_png_len = 954870;
// 画像データからNSDataを生成し、それを元にUIImageを生成する
NSData *data = [NSData dataWithBytes:original_png
length:original_png_len];
return [UIImage imageWithData:data];
}
今回は説明簡単にするためにメソッドの中にベタ書きしたけど、
実際には別ファイルに書き出してincludeするとかしたほうが良さそう。
LINE BOTを動かす環境を調べてみた
1万人限定に反応してアカウントを作ってしまったので、
とりあえずbotを動かしてみるところまでやってみた
今回のbot環境を構築するにあたってSSL対応と固定IPが必要という2点が悩みどころ
現状トライアルなので、できるだけ安く、できれば無料で環境構築をしたい
ということで、以下の二つを試してみた
AWS Lambda + API Gateway
API Gatewayはデフォルトでhttpsなので
SSLの点では良さそうと最初に思い試してみた
詳細な実装方法はこちらの記事が参考になる
AWS Lambda + API GatewayでLINE BOT APIを叩いてみた
ただし、API GatewayはIPを固定できないので時間が経つと変わってしまう
別途プロキシをEC2なりで立てる必要が出てくるので今回は諦めた
Heroku + fixie
最終的にこの形に落ち着いた
HerokuはSSLにも対応している
さらに、fixieというアドオンを使ってプロキシを挟むことでIPの固定化もできる
しかも最小構成なら無料という素晴らしさ
awsの世界でサーバーレスっていうのも捨てがたいけど、
herokuの場合はnodeとかpyton以外も使える自由度もあるし、
これが今のところ一番良さそうかなと思っている
以下、ruby(sinatra)+HerokuでのLine Bot環境構築手順のメモ
heroku環境を構築する
herokuを利用するのが初めての場合は以下からアカウントを作成
https://www.heroku.com/
続いてherokuをコマンドラインから扱うためのheroku toolbelt
をインストール
https://toolbelt.heroku.com/
上記リンクのgetting startに従ってログインから試してみる
$ heroku login
=> 途中でemailとpasswordを要求され、ログインする
とりあえずアプリをデプロイしてみる
$ mkdir firstsample && cd $_
$ heroku create
成功するとgitのURLとサイトURLが出力されるのでサイトURLへブラウザからアクセスしてみる
Heroku | Welcome to your new app!
みたいな文言が出るページが表示される
これでheroku上にアプリを公開する準備が整った
sinatraアプリを作成する
Gemfileを生成
bundle init
Gemfileに書きを追加
gem 'sinatra'
sinatraをインストール
bundle install --path vendor/bundle
シンプルなアプリをapp.rbに記述
# coding: utf-8
require 'sinatra'
get '/' do
'hello!'
end
ローカルで起動してみる
bundle exec ruby app.rb
ブラウザでアクセス、localhost:4567にアクセス、helloが表示されればOK
アプリをHerokuに公開する
herokuでsinatraを起動するためにProcfileを追加
# Procfile
web: bundle exec ruby app.rb -p $PORT
gitを設定
git init
herokuのリモートリポジトリを設定(heroku createした時に出力されたgitのURL)
heroku git:remote -a xxxxx
# 確認
git remote -v
=> set git remote heroku to https://git.heroku.com/xxxxx.git
gitignore設定
.DS_STORE
*.swp
/Gemfile.lock
/vendor/
/.bundle
作成したファイルをgitにコミット
git add
git commit -m "initial commit"
herokuのリモートリポジトリにpush
git push heroku master
成功すると以下のようなメッセージが表示される
~ 中略 ~
remote: https://xxxxx.herokuapp.com/ deployed to Heroku
~ 中略 ~
表示されたURLにアクセスして先ほどのhelloが表示されればデプロイ完了
fixieで固定IP対応(プロキシ)
herokuのアドオンを使用する場合は無料のアドオンしか使わなくても最初にクレジットカードの登録が必要になる
こんなエラーメッセージが出る
Please verify your account to install this add-on plan (please enter a credit card) For more information, see https://devcenter.heroku.com/categories/billing Verify now at https://heroku.com/verify
カード情報を登録後以下のコマンドを実行するとアドオンが追加されてIPアドレスが表示される
$ heroku addons:create fixie:tricycle
=> Your IP addresses are xx.xx.xx.xx, xx.xx.xx.xx
表示されたIPアドレスをLINE DevelopersのServer IP Whitelistに追加する
LINE BOTを実装する
最初にLine developersのbotのcallbackURL
に
herokuで作成したアプリのURLを設定する
サンプルはとりあえずオウム返し
# coding: utf-8
require 'sinatra'
require 'json'
require 'base64'
require 'net/https'
require 'uri'
post '/' do
channel_secret = "自分のChannel Secret"
http_request_body = request.body.read
hash = OpenSSL::HMAC::digest(OpenSSL::Digest::SHA256.new, channel_secret, http_request_body)
signature = Base64.strict_encode64(hash)
# requestヘッダを取得
http_headers = request.env.select { |k, v| k.start_with?('HTTP_')}
if signature != http_headers["X-LINE-CHANNELSIGNATURE"]
{status: :error}.to_json
end
params = JSON.parse http_request_body
from = params["result"][0]["content"]["from"]
text = params["result"][0]["content"]["text"]
# リクエストヘッダ
request_headers = {
"Content-Type" => "application/json",
"X-Line-ChannelID" => "自分のChannelID",
"X-Line-ChannelSecret" => channel_secret,
"X-Line-Trusted-User-With-ACL" => "自分のMID"
}
# リクエストボディ
request_params = {
"to" => [from],
"toChannel" => 1383378250, # 固定値
"eventType" => "138311608800106203", # 固定値
"content" => {
"contentType" => 1,
"toType" => 1,
"text" => text
}
}
# message send endpoint
uri = "https://trialbot-api.line.me/v1/events"
uri_parsed = URI.parse uri
# ref : https://devcenter.heroku.com/articles/fixie
_, username, password, host, port = ENV["FIXIE_URL"].gsub(/(:|\/|@)/,' ').squeeze(' ').split
# fixieのproxy情報とline botのendpoint情報をセット
http = Net::HTTP.new(uri_parsed.host, uri_parsed.port, host, port, username, password)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
http.post(uri_parsed.path, request_params.to_json, request_headers)
data = {status: 'OK'}
data.to_json
end
LINE Developers のBot Channelsページの下の方にある
QRを使ってbotをLINEで友達追加してbotに話しかけてみる
botからレスがくれば無事成功
気づいたらLine Botの説明というよりHerokuの使い方説明になってしまった。。。
swiftenvでswiftを導入する
swiftは2016/4現在、3系に向けて開発が進んでいて、
develompemtバージョンはどんどん最新のスナップショットが公開されている
swift package managerを試したりする場合はdevelopmentバージョンが必要だし
swiftのバージョンを簡単に切り替えられるswiftenvを導入してみた
(OSXではXcode7.3でtoolchainsから切り替えられるようになった)
swiftenvのインストール
Linux(Ubuntu)とOSXそれぞれで導入を試してみた
ubuntuの場合
今回はvagrantでubuntu環境を構築してその上でswiftを入れてみる
現在Ubuntuだと15.10と14.04向けのスナップショットがある
https://swift.org/download/#latest-development-snapshots
Ubuntu14.04のvagrant box
https://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box
Vagrantfileはこんな感じ
$ grep -v -e '\#' -e "^$" Vagrantfile
Vagrant.configure(2) do |config|
config.vm.box = "ubuntu1410"
config.vm.box_url = "https://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box"
end
パッケージの最新化
sudo apt-get update
swiftenvインストールするために必要なgitを先にインストール
sudo apt-get install git
SwiftのSystem Requirementsに従って以下をインストール
(これ入れておかないとswiftは入るけどビルドで失敗する)
https://github.com/apple/swift/blob/master/README.md
sudo apt-get install git cmake ninja-build clang python uuid-dev libicu-dev icu-devtools libbsd-dev libedit-dev libxml2-dev libsqlite3-dev swig libpython-dev libncurses5-dev pkg-config
swiftenv導入
https://github.com/kylef/swiftenv
git clone https://github.com/kylef/swiftenv.git ~/.swiftenv
echo 'export SWIFTENV_ROOT="$HOME/.swiftenv"' >> ~/.bash_profile
echo 'export PATH="$SWIFTENV_ROOT/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(swiftenv init -)"' >> ~/.bash_profile
source .bash_profile
OSXの場合
homebrewを使ってインストール
brew install kylef/formulae/swiftenv
echo 'eval "$(swiftenv init -)"' >> ~/.bash_profile
swiftのインストール
swiftenvのインストールが完了したら続いてswift自体のインストールをおこなう
swiftenv install swift-DEVELOPMENT-SNAPSHOT-2016-03-24-a
引数はsnapshotのURLを直接渡してもOK
swiftenv install https://swift.org/builds/swift-2.2-release/ubuntu1404/swift-2.2-RELEASE/swift-2.2-RELEASE-ubuntu14.04.tar.gz
installコマンドに渡すswiftスナップショットの情報はここで確認出来る
https://swift.org/download/#snapshots
swiftのバージョンを切り替えてみる
複数のswiftバージョンを無事導入できたら切り替えも試してみる
まず、導入済みのswiftバージョン一覧を確認する
$ swiftenv versions
* 2.2-RELEASE (set by /home/vagrant/.swiftenv/version)
DEVELOPMENT-SNAPSHOT-2016-03-24-a
swiftのバージョンを確認
$ swift -version
Swift version 2.2 (swift-2.2-RELEASE)
swiftのバージョンをdevelopment-snapshotに変更する
$ swiftenv global DEVELOPMENT-SNAPSHOT-2016-03-24-a
swiftのバージョンを確認
$ swift -version
Swift version 3.0-dev (LLVM b010debd0e, Clang 3e4d01d89b, Swift 7182c58cb2)
無事切り替わった
ちなみに、ヘルプを見ると以下のようになっており
$ swiftenv -h
version Displays the current active Swift version
versions Lists all installed Swift versions
global Sets the global version of Swift
local Sets the local application-specific version of Swift
install Installs a version of Swift
uninstall Uninstalls a specific Swift version
rehash Installs shims for Swift binaries
local
オプションを使用すると特定のディレクトリ配下のswiftバージョンのみ変更できる
swiftenv local xxxx
実行したディレクトリ直下に.swift-version
ファイルが生成され、
DEVELOPMENT-SNAPSHOT-2016-03-24-a
などの
snapshotバージョンが記録され、そのバージョンのswiftが動作することになる
try!Swiftに参加してきました
try! swift 2016に参加してきました
3/2-4の3日間、500人超の参加者が集まった
国内のswift関連としては非常に大規模なイベントだった
せっかくの貴重な体験だったので自分なりに感じたことをまとめてみた
全体的な感想
全体的な感想としては、各セッションのバリエーションが豊富で
swift自体(POPとか)、テスト周り、デザイナーとの協業、
アイコンのライブデザイン、tvOS、swiftコミュニティへの貢献について等々、
様々な分野の話が聞けて非常に有益なカンファレンスだった
POP(Protocol-Oriented Programing)については
かなり多くのセッションで扱われていたし、
抽象化、モジュール化を推し進めていく中でstoryboardは使わないという
選択をしている人も結構いるんだなぁという発見もあった
逆にデザイナーと協業していく上ではstoryboardは非常に有効という意見もあって
いろいろ考えさせられるところも多かった
どういうアプリを作るのか、どういう体制で開発するのか、
状況に合わせるべきって話だとは思うけど
本筋とずれるけど、同時通訳が素晴らしすぎた
技術系の翻訳って素人目にも難しいと思うのに、
ほとんどのセッションでちゃんと同時通訳された
サーバーサイドswift
個人的に一番興味があったのは
swiftのオープンソース化による他分野への取り組みというところだったが、
サーバサイドswift関連ではwebアプリケーションを動かす
フレームワークもIBMのKituraやPerfect等、既にいくつかリリースされているし
Caesar Wirthのセッションにあったvaporという、
rubyでいうところのsinatraのような軽量フレームワークも存在しているようだった
セッションで使ったデモコードが既に公開されていた https://github.com/cjwirth/trySwiftServer
swiftで機械学習
Diana Zmudaのセッションでは
swiftで機械学習プログラミングを行う試みをしていてとても興味深かった
swiftはコンパイル言語で高速なことや再帰処理や抽象化が行いやすいことから
現状の機械学習実装のファーストチョイス(python、C++等)候補の中に
割って入るだけのポテンシャルはあるのではないかという話だった
その他
自分は都合が合わず参加できなかったが最終日の打ち上げパーティは、
海外からの参加者と日本人参加者とのコミュニケーションを図る試みとして
バイリンガルな人を間にいれてより深い交流をする取り組みなどもしていたようで、
そういった意味でも素晴らしいカンファレンスだったのではないかと思う
あと、海外の人ポケモン好きすぎ
まとめ
自分自身、swiftの理解度が低い状態での参加だったので、
理解が追いつかないものもあったけど、
swiftの面白さとか可能性を体感できたのはよかった
来年の開催は現状未定みたいだけどぜひ開催してもらいたいし、
その時はまた参加したい