株式会社LIFULLのブログ - TECH PLAY

TECH PLAY

株式会社LIFULL

株式会社LIFULL の技術ブログ

全668件

大坪と申します。先日見つけた動画の内容が興味深かったので、自分が理解できたところだけ紹介します。 Airbnb Design Talk with Braden Kowitz 12.12.12 ... スピーカーはBraden Kowitz氏。現在はGoogle Ventureのデザインパートナーであり、以前はGMail, Google Spreadsheetsのデザインにも携わったとのこと。このプレゼンテーションの中でKowitz氏は「デザインプロセスにおいて、失敗することの重要性」を説いています。 プレゼンの最初に「失敗したデザインの例」として悪名高いiMacの円形マウスの写真を出します。確かに人目をひくデザインをしていますが、「そもそもどちらが上なのか」わからず困った経験をしたのは私だけではないと信じたい。Kowitz氏の表現を借りれば 「カーソルを上に動かそうとマウスを操作すると、カーソルはとんでもない方向に飛んで行く」 製品でした。実際この後にAppleがデザインしたマウスは、仮に丸みを帯びた形であっても持っただけで方向がわかるようになっています。つまりこれは明らかな「失敗したデザイン」だったわけです。(私はApple原理主義者ですが、このことを認めるくらいの柔軟性は持ち合わせています) さてKowitz氏が最初に取り組んだデザインの仕事は失敗に終わったそうです。彼は一人でコンセプトをつめ、「まだ人に見せられる段階じゃない」と途中のフィードバックも拒み、そして3ヶ月後に「これが成果です」とやったところ結果を誰も理解できなかった。なぜこのようなことが起こるのか? 氏は"Design Blindness”という言葉を用いて理由を説明します。デザインを行う人間は「これは行ける」というある種の確信をもって作業を始めるわけですが、その瞬間から自分のデザインを客観視することができなくなる。それ故Critique Early & Often:早い段階から、できるだけ頻繁に「批評」してもらうことが必要になる、と。 しかし自分の仕事について「批評を受ける」ことは勇気のいることです。特に「まだできていない」作品に対してあれこれ文句を付けられるのは楽しくない。しかし良いデザインをするためには、デザイナーは「失敗」に対してオープンな姿勢を取る必要がある。ここから氏は3つのトピックについて語ります。 まず「楽器の練習」 ピアノに関して本を何冊か読めば、カーネギーホールでピアノを演奏できるか、といえばそんなことはない。もちろん最初にインストラクションは受けるのですが、自分でやってみると指はうまく動かない。何度も繰り返しているうちにようやく目標としているレベルの演奏ができる。このプロセスは「ピアノの失敗」ではなく「ピアノの練習」と呼ばれます。 それ故「デザインの失敗」ではなく「デザインの練習(Practice)」と呼ぶべきではないかと氏は提案します。「失敗」は学ぶことの一部であり、それ無しに上達-あるいはデザインが良くなることはありえない、と。 2番めは「軍艦」。軍艦がどうしたって? 氏が取り上げたのはGmail。Gmailは軍艦のように複雑で巨大なプログラムです。この画面にChat機能を入れようとした際にどんな「デザインの失敗」があったか。最初のデザインはGoogle社内ではとても好評。次に「普通の人」にテストしてもらった時何が起こったか?テストを行う側が最初のメッセージを送ったところ、被験者は 「どうやったら入力した文字が送信されるのかわからず、返答の文字をタイプした後固まってしまった」 そうです。「慣れれば」エンターキー(リターンキー)を押した瞬間文字が送信される、という方法は効率がよいのですが、初めて使う人間にとってみると「送信ボタンはどこ?」となってしまう。よし。では「エンターキーを押すとメッセージが送信されます」という表示をつけよう。 改良されたデザインでテストを行ったところ、結果はまた「失敗」でした。チャットで呼びかけがあったときにポップアップウィンドウを開くようにしたのですが、ユーザはそれを「またウザい広告か」と内容を確認せず反射的にウィンドウを閉じてしまう。 Googleのデザイナーが間抜けばかりということはまずありえない。ではなぜ「良いデザイナー」が取り組んでいながら、これほど失敗を繰り返すのか。氏が得た結論は「デザイナーは普通ではない」から。 ここで氏は「この中でスマートフォン2つ持っている人?」と呼びかけます。おそらく会場の何人かが手を挙げたらしい。しかしスマートフォン所有率が5割とか6割とか言っているときに2台もっているなんてのは明らかに「おかしい」わけです。しかしその「おかしいデザイナー」は「普通の人」の感じ方、見方を推し量らなくてはならない。 つまりデザインというのは「 軍艦ゲーム 」(懐かしい!)のようなもの。D-3とか座標を言うと相手は「おっと命中だ」とか教えてはくれる。しかし結局相手の軍艦がどこにあるかは分からない。つまりデザインにおいて「失敗」は避けがたい要素なわけです。 3番目に挙げられたのは「ユーザがどんどん変化している」こと。今日Webサイトを使うユーザは2年前のユーザと同じではない。具体的に挙げられたのは 「チェックアウト時(?)に押して貰う黄色いボタンをどこに配置すればユーザは戸惑わないか」 というデザイン上の問題。以前同様の画面を設計した際ユーザスタディを行ったことがあったので、その経験から最も良いと思われる位置にボタンを配置しました。しかし視線のヒートマップをとってみるとうまく機能しない。逆に以前の経験では「悪いデザイン」と思っていた配置が一番よい成績を収めた。 つまりこの2年の間に、ユーザが普段使うサイトのデザインが変化し、それにともないユーザがどういうデザインをすれば戸惑わないか、という「正解」も変化してしまった、と氏は言います。この事を "PEOPLE ARE MOVING TARGET" 人々は動いているターゲットだ というフレーズで表現する。 例えば一番目に挙げた「Gmailのチャット機能」の経験をそのまま今生かせるだろうか。氏は懐疑的です。多分今なら「チャット」といえばFacebookメッセンジャーとか(日本ならLINE)にユーザは慣れている。つまりチャット機能に期待するものが変わってしまっているのです。 それ故仮に経験がある良いデザイナーであっても失敗は避けられない。従って失敗を恐れ3ヶ月間努力してBig Surpriseを食らうよりは、Little Surprise:小さな驚き をたくさんもらったほうがいい、と主張してプレゼンテーションを締めくくりました。 このあとの質疑応答にいくつか興味深いものがあったので紹介しておきます。 「どのタイミングでレビューをするべきか?」という質問に対しては、例えば2週間単位とかで定期的に行うのがいいのではないか、と回答しています。初期の段階ではほとんどみせるものが無いわけですが、その際にはユーザのニーズを探り、簡単なスケッチを提示する。デザインが進むにつれユーザにみせるものが出来上がっていくわけです。 面白い指摘と思ったのは「プログラムを開発する際にはテストを繰り返し、デバッグすることが常識になっている。なぜデザインではそれができないのだろう」という質問でした。考えてみれば近年ユニットテストとか、Continuous Integrationというようにプログラム開発においてテストの比重は高まっています。これに対しては「デザインにおいては、まだテストがプロセスの一つとして認知されていないからだろう」との回答。 こうした「デザインの途中でフィードバックを受ける」ことの重要性は誰もが頭ではわかっている。しかしなぜそれをしないかといえば「ボロカスにけなされるのではないか」という恐れからではないか。こうした指摘に対する答えは 「レビューの仕方はずいぶん様々で、極端な例だと主観だけで相手を貶しまくり、批判されたデザイナーは帰って枕を涙で濡らすようなものもある。その逆の極端な例は皆が妙に優しく思った事をちゃんと言わないものだ。ところがその中庸で非常にうまく”批評”をしている例もある。どうやってそうした方法を身につけたのか、と聞いた所”学校で習った”とのことだった。従って良いレビューのやり方は学習によって身に付けることができる」 いやこっちが聞きたいのはその「良いレビューの仕方」なのだが、、と思うわけですが、少なくとも正しいレビューの方法は「天性のものではなく、学べるもの」というのは期待が持てるメッセージでは有ります。 いや、それでもレビューを受けて凹むのは怖い、どうすれば「最初の恐怖」を乗り越えてレビューを受けられるようになるか、という質問に対しては 「たくさん数をこなすこと。そうすればレビューで批評されても自分はそこから何かを学んでよりよいデザインができる、と確信をもてるようになる」 と返答がありました。デザイナーであってもなくても批評されるのは心理的に堪えるものです。ボロカスに言われると「ああ、この世の終わりだ。どこか人里離れた洞窟にでもこもろうか」と思うわけですが、そうした経験を何度かつみかさね、それでも自分の首がつながっている、という自信を持てれば、レビューを受けることに積極的になれるかもしれません。 質問にもありましたが、確かにプログラムを書くとき「自動テスト」を恐れる人はあまりいない。(恐れたほうが良い人がいるのも確かですが)しかしきっと遠い昔には「俺が書いたプログラムにはバグがないから、テストは不要だ」と真面目な顔で主張していた人もいたのでしょう。 同じようにデザインもテストを習慣づけるべきではないか。ではテストを行うとして、誰に聞けばいいのか。間違った相手に間違ったタイミングでテストすると恐ろしい事が起こる、、とか悩みは次の段階に行くわけです。 このビデオを見ていて、ある人が書かれていた体験談を思い出しました。 最後の学年のアート&ビジネスというクラスでした。 3ヶ月くらいかけて完成させる課題で、◯△□の基本的なシェイプを使って最終的には何かプロダクトのモックアップを作るみたいな感じだったと思います。 (中略) そして、締め切りの日の授業。みんなは自信作を早く見せてくてニコニコしてた中、先生は言いました。 「はい。みんな課題持って来ましたか?では、机の上に出して、紙の人はそのまま破り捨てなさい。立体物の人は壊してゴミ箱へ捨てなさい。」 生徒全員しばらく唖然とした状態で沈黙。その後、泣き出す人、すごい剣幕で怒り出す人、教室から出ていっちゃう人、多くの生徒はそのショックをそれぞれに表現していました。 (中略) そして、怒っている生徒に向けて先生はこう言いました。 「(中略)みんなプロのデザイナーとしてこの先の人生食っていこうと思っているなら、こんなことは日々起こること。これでショックを受けてやる気をなくしているなら、クリエイティブな職種に向かないから違う道に進んだほうがいい。クライアントの中にはアイデアや作品を見ることもなく破り捨てる人もいる。わたしもそんなこと毎日のように経験してるぞ。」 引用元: 「傷つかない技術」を体験した授業(現在リンク切れ) 少なくとも見てもらえるのはマシなほう。であれば「これは失敗ではなく、Practice」と思い切る訓練をするべきなのかもしれません。以下に引用する言葉は、デザインに限らず広く必要な心構えと思います。 多くのまだ経験の浅いクリエイターの人は、作品や仕事にダメだしされてるのに、人格を否定されたような気分に陥って、自分を否定されたように思って、まだ人に認められるような作品を作る前に辞めてしまったり、上司やクライアントと喧嘩したり、病んでしまったりってあると思います。 引用元:同上 などとエンジニアの端くれである私が、デザインプロセスについて長々書いているのは、そろそろ作ってきたものを社内でレビューしてもらわなくちゃいけないのでその心理的プレッシャーから逃れるため、ではもちろんありません。
こんにちは、上津原です。 2014/2/25 19:00より、NoSQL CouchbaseのMeetUpを、弊社ネクストで開催することになりました! http://couchbasejpcommunity.doorkeeper.jp/events/8914 アジェンダ Opening: Viber案件の事例紹介!by Eric Gold from Couchbase ( http://www.couchbase.com/presentations/couchbase-tlv-2014-couchbase-at-viber ) 1) Couchbase Server 2.5の解説 ( http://docs.couchbase.com/couchbase-manual-2.5/cb-release-notes/ ) 2) 事例紹介1 - Wizcorp( http://www.wizcorp.jp/ja/ ) 3) 事例紹介2 - サイバーエージェント( http://www.cyberagent.co.jp/ ) 4) Couchbase Serverの新クエリN1QLについて ( http://www.couchbase.com/communities/jp/n1ql ) ※N1QLとは?:SQLライクなCouchbase社が独自に開発したクエリ言語。 Couchbase ServerはNoSQLデータベースですが、N1QLを使用することによりSQLライクな検索が可能となります。 お気軽にお越しください 今回のMeetUpは、CouchbaseServerの事例紹介がメインとなります。 ちょっとNoSQLが気になるという人や、これからCouchbaseを触ってみたい、検討しているという人などもぜひぜひお越しください! 今回は、Couchbase社のEric Gold氏もいらっしゃいます。 参加申し込みは以下からお願い致します。 http://couchbasejpcommunity.doorkeeper.jp/events/8914
というわけで「難しいサンプルはわかんないから、肝心な点だけ書きます」がモットーのSimple Example第3弾は「ビューを回転させる」です。なんのことだ、と問われれば「下の動画を観てください」と答えましょう。 Rotateというボタンを押すたびに、上半分の画像がくるくる回りながら入れ替わるわけです。こういうことやりたいことありますよね?ね?ね?(ウザい) このように3Dの効果をつけながら、画面を丸ごとTransitionさせる、というサンプルもあるのですが、ここでは「画面の一部のビューだけ入れ替える」方法について説明します。ソースは github に置きました。 起動すると表示される画面を作っているのはViewControllerクラスです。でもって上半分のくるくる回るところはTransitionViewというクラスにまとめてあります。"Rotate"というボタンを押すたび、TransitionViewに対して [_transitionView changeViewType:@"cube" subtype:kCATransitionFromLeft duration:1.0]; とmethodが呼ばれるわけです。ではTransitionViewの中では何をしているか。 呼ばれるのはこのコードです。実際にViewを入れ替えているのは [ self exchangeSubviewAtIndex:secondIndex withSubviewAtIndex:firstIndex]; なのですが、それに対してLayerにanimationをつけています。やっていることはこれだけ。 ここでAnimationには様々なパラメータを指定することができます。CATransition *animationに渡しているtype,subtypeを変えることにより様々な視覚効果を得ることができる。設定可能なパラメータ(Stackoverflowからのコピーですが)はViewController.mの-(void) rotate:(id)senderに列挙しておきましたので、あれこれ変更して効果を確認してみてください。 もちろん「プログラムを使う側」からすれば、ここらへん全部別々のボタンにしておいたほうがいいのですが、そうすると読む時にボタンのコードだらけになり「あれ、どこに肝心な部分があるの?」とわからなくなる。ですからあえてボタンを設定していないのでした。手抜きではないのです(半分真顔)
ネクストでレコメンドエンジン開発をしてる古川です。 solrにおいて、複数フィールド値を組み合わせたソートを 実現する方法について紹介します。 実現方法としては、 function query を組み合わせて実現 独自のfunction query を作成して実現 独自のsearch component を作成して実現 という三つの方法があり、上から下に 実装方法: 簡単 → 大変 実行速度: 遅い → 早い 応用範囲: 狭い → 広い という特徴があります。 昨年リリースした、 「HOME'S へやくる!」 では、 2の方法で、たとえ指定した条件にすべて合致しなくても、指定した条件に、 近い順に物件リストを返すということを実現しています。 今回は、まず、1. function query を組み合わせによる実現方法を 紹介したいと思います。 以下、solr4.6.1 をベースに説明しますが、他のバージョンでも 特に問題ないと思います。 準備 solr 環境を作成 wget http://archive.apache.org/dist/lucene/solr/4.6.1/solr-4.6.1.tgz tar xvzf solr-4.6.1 テスト用スキーマ作成 ./solr-4.6.1/example/solr/collection1/conf/schema.xml を以下の内容に書き換えます。 <? xml version = "1.0" encoding = "UTF-8" ?> <schema name = "nextblog" version = "1.5" > <fields> <field name = "id" type = "string" indexed = "true" stored = "true" required = "true" multiValued = "false" /> <field name = "x" type = "float" indexed = "true" stored = "true" required = "false" multiValued = "false" /> <field name = "y" type = "float" indexed = "true" stored = "true" required = "false" multiValued = "false" /> <field name = "_version_" type = "long" indexed = "true" stored = "true" /> <field name = "text" type = "text_general" indexed = "true" stored = "false" multiValued = "true" /> </fields> <copyField source = "id" dest = "text" /> <uniqueKey> id </uniqueKey> <solrQueryParser defaultOperator = "AND" /> <types> <fieldType name = "string" class = "solr.StrField" sortMissingLast = "true" /> <fieldType name = "float" class = "solr.TrieFloatField" precisionStep = "0" positionIncrementGap = "0" /> <fieldType name = "long" class = "solr.TrieLongField" precisionStep = "0" positionIncrementGap = "0" /> <fieldType name = "text_general" class = "solr.TextField" positionIncrementGap = "100" > <analyzer type = "index" > <tokenizer class = "solr.StandardTokenizerFactory" /> <filter class = "solr.StopFilterFactory" ignoreCase = "true" words = "stopwords.txt" /> <filter class = "solr.LowerCaseFilterFactory" /> </analyzer> <analyzer type = "query" > <tokenizer class = "solr.StandardTokenizerFactory" /> <filter class = "solr.StopFilterFactory" ignoreCase = "true" words = "stopwords.txt" /> <filter class = "solr.SynonymFilterFactory" synonyms = "synonyms.txt" ignoreCase = "true" expand = "true" /> <filter class = "solr.LowerCaseFilterFactory" /> </analyzer> </fieldType> </types> </schema> solr 起動 cd ./solr-4.6.1/example java -jar start.jar & ブラウザを起動して、 http://localhost:8983/solr/ で管理画面が開くことを確認します。 データインポート sample01.csv という名前で以下の内容のファイルを作成します。 id,x,y homes1,1,4 moneymo1,2,5 lococom1,3,6 curl コマンドでcsvファイルをsolrにインポートします。 curl 'http://localhost:8983/solr/update/csv' --data-binary @sample01.csv -H 'Content-type:text/plain; charset=utf-8' curl 'http://localhost:8983/solr/update?commit=true' function query 実行 さて、ここからが本番です。solr では、 こちら にあるように様々なfunction query がデフォルトで実装されています。 例えば、x * y の値をフィールド値を取得したい場合には、 http://localhost:8983/solr/select/?q=*:*&fl=id,x,y,product(x,y) のように実行すると、スキーマに存在しないx * yの値を取得することができます。 <result name = "response" numFound = "3" start = "0" > <doc> <str name = "id" > homes1 </str> <float name = "x" > 1.0 </float> <float name = "y" > 4.0 </float> <float name = "product(x,y)" > 4.0 </float> </doc> <doc> <str name = "id" > moneymo1 </str> <float name = "x" > 2.0 </float> <float name = "y" > 5.0 </float> <float name = "product(x,y)" > 10.0 </float> </doc> <doc> <str name = "id" > lococom1 </str> <float name = "x" > 3.0 </float> <float name = "y" > 6.0 </float> <float name = "product(x,y)" > 18.0 </float> </doc> </result> さらに、function queryを、ソート引数に指定することで、 関数値に従ったソート順でドキュメントを取得できます。 http://localhost:8983/solr/select/?q=*:*&fl=id,x,y,product(x,y)&sort=product(x,y) decs <result name = "response" numFound = "3" start = "0" > <doc> <str name = "id" > lococom1 </str> <float name = "x" > 3.0 </float> <float name = "y" > 6.0 </float> <float name = "product(x,y)" > 18.0 </float> </doc> <doc> <str name = "id" > moneymo1 </str> <float name = "x" > 2.0 </float> <float name = "y" > 5.0 </float> <float name = "product(x,y)" > 10.0 </float> </doc> <doc> <str name = "id" > homes1 </str> <float name = "x" > 1.0 </float> <float name = "y" > 4.0 </float> <float name = "product(x,y)" > 4.0 </float> </doc> </result> function query 同士を組み合わせて、デフォルトには存在しない 関数を実現することができます。 http://172.20.12.206:8983/solr/select/?q=*:*&fl=id,x,y,if(sub(x,2),if(sub(x,1),5,1),10) インデントがないので分かりづらいですが、javascript で書くと、 以下のような関数を実行していることになります。 function (x) { if (x == 2) return 10; else if (x==1) return 5; else return 1; } 検索結果のフィールド値をみると、確かに期待した結果を得られています。 <result name = "response" numFound = "3" start = "0" > <doc> <str name = "id" > homes1 </str> <float name = "x" > 1.0 </float> <float name = "y" > 4.0 </float> <long name = "if(sub(x,2),if(sub(x,1),5,1),10)" > 1 </long> </doc> <doc> <str name = "id" > moneymo1 </str> <float name = "x" > 2.0 </float> <float name = "y" > 5.0 </float> <long name = "if(sub(x,2),if(sub(x,1),5,1),10)" > 10 </long> </doc> <doc> <str name = "id" > lococom1 </str> <float name = "x" > 3.0 </float> <float name = "y" > 6.0 </float> <long name = "if(sub(x,2),if(sub(x,1),5,1),10)" > 5 </long> </doc> </result> 注意 function query では、フィールド値が存在しない場合、そのフィールド値は、 0が入っているものとして計算されてしまいます。 欠損値のあるデータを追加して試してみます。 sample02.csv id,x,y homes2,1,4 moneymo2,2,5 lococom2,,6 curl 'http://localhost:8983/solr/update/csv' --data-binary @sample02.csv -H 'Content-type:text/plain; charset=utf-8' curl 'http://localhost:8983/solr/update?commit=true' http://localhost:8983/solr/select/?q=*:*&fl=id,x,y,product(x,y)&sort=product(x,y) asc <result name = "response" numFound = "6" start = "0" > <doc> <str name = "id" > lococom2 </str> <float name = "y" > 6.0 </float> <float name = "product(x,y)" > 0.0 </float> </doc> <doc> <str name = "id" > homes1 </str> <float name = "x" > 1.0 </float> <float name = "y" > 4.0 </float> <float name = "product(x,y)" > 4.0 </float> </doc> <doc> <str name = "id" > homes2 </str> <float name = "x" > 1.0 </float> <float name = "y" > 4.0 </float> <float name = "product(x,y)" > 4.0 </float> </doc> <doc> <str name = "id" > moneymo1 </str> <float name = "x" > 2.0 </float> <float name = "y" > 5.0 </float> <float name = "product(x,y)" > 10.0 </float> </doc> <doc> <str name = "id" > moneymo2 </str> <float name = "x" > 2.0 </float> <float name = "y" > 5.0 </float> <float name = "product(x,y)" > 10.0 </float> </doc> <doc> <str name = "id" > lococom1 </str> <float name = "x" > 3.0 </float> <float name = "y" > 6.0 </float> <float name = "product(x,y)" > 18.0 </float> </doc> </result> id=lococom2 の product(x,y) の値が、0.0 になってしまっています。 <doc> <str name = "id" > lococom2 </str> <float name = "y" > 6.0 </float> <float name = "product(x,y)" > 0.0 </float> </doc> フィールドが空の場合を考慮した処理を行うには、exists関数を 組み合わせてやる必要があります。 例えば、以下の例では、フィールドxの値が空の場合はデフォルト値100を 採用して、ソート順位を最下位にしています。 http://localhost:8983/solr/select/?q=*:*&fl=id,x,y,if(exists(x),product(x,y),100)&sort=if(exists(x),product(x,y),100) asc <result name = "response" numFound = "6" start = "0" > <doc> <str name = "id" > homes1 </str> <float name = "x" > 1.0 </float> <float name = "y" > 4.0 </float> <float name = "if(exists(x),product(x,y),100)" > 4.0 </float> </doc> <doc> <str name = "id" > homes2 </str> <float name = "x" > 1.0 </float> <float name = "y" > 4.0 </float> <float name = "if(exists(x),product(x,y),100)" > 4.0 </float> </doc> <doc> <str name = "id" > moneymo1 </str> <float name = "x" > 2.0 </float> <float name = "y" > 5.0 </float> <float name = "if(exists(x),product(x,y),100)" > 10.0 </float> </doc> <doc> <str name = "id" > moneymo2 </str> <float name = "x" > 2.0 </float> <float name = "y" > 5.0 </float> <float name = "if(exists(x),product(x,y),100)" > 10.0 </float> </doc> <doc> <str name = "id" > lococom1 </str> <float name = "x" > 3.0 </float> <float name = "y" > 6.0 </float> <float name = "if(exists(x),product(x,y),100)" > 18.0 </float> </doc> <doc> <str name = "id" > lococom2 </str> <float name = "y" > 6.0 </float> <long name = "if(exists(x),product(x,y),100)" > 100 </long> </doc> </result> まとめ function query を組み合わせて、新たなfunction query を作成する 方法を紹介しました。結構、柔軟に独自のスコア計算を実現できることが 伝わったのではないかと思います。 この手法では、データアクセスに非効率な部分があり、対象となる ドキュメント数が増加したり、関数が複雑になってくると、速度的な 問題が発生してしまいます。 次回は、それを回避する方法として、「 独自のfunction query を作成して実現 」 を紹介したいと思います。
こんにちは、@szkkentaroです。 最近は、がっつりAWS周りのお仕事をさせてもらっています。 皆さんはネットワークのパフォーマンス測定にどんなツールを使っていますか? 自分の場合は、以前からIperfを利用してきました。 この度、ひさびさに利用する機会がありソースをダウンロードしに行ったら、 なんと、iperf3がstableになっているではありませんか! ということで、 Iperf(ver.2系)の後継バージョンであるiperf3の使い方と追加機能についてまとめてみようと思います。 Iperfとは TCPやUDPでネットワーク帯域のパフォーマンスを測定するC言語で書かれたツールになります。 2台のサーバーにて、それぞれサーバーモード、クライアントモードとして動作させることで、 サーバ間の帯域をテストすることができます。 Iperfとiperf3の違い iperf3 is a new implementation from scratch, with the goal of a smaller, simpler code base, and a library version of the functionality that can be used in other programs. iperf3 also a number of features found in other tools such as nuttcp and netperf, but were missing from iperf2.X. iperf3は、より小さくてよりシンプルなコードをベースにして、他のプログラムから使うことができる機能的なライブラリを目指し、スクラッチから実装され直されたそうです。また、iperf2系にはなくnuttcpやnetperf のような他のツールにあるいくつかの機能が追加されています。 個人的には、ユーティリティ系の機能追加が多いような印象を受けました。新しい機能については後述します。 現時点(2014/02/07)時点での、Stableなバージョンと、ダウンロード先を表にしてみました。 名前 バージョン ホスティング 最終更新日 Iperf 2.0.5 sourceforge.net 2010/07/09 iperf3 3.0.1 code.google.com 2014/01/10 ちなみに、名前の1文字目ですが、ver2系は大文字の『 I 』で、ver3系は小文字の『 i 』を使っています。 これは、以下ソースコードのホスティング(ダウンロード)先で使われている名前と、 wikipedia に習って意図的にそうしました。 留意点 iperf3 is supported on Linux, FreeBSD (FreeBSD Ports Collection), and MacOS X only. (Windows is not currently supported.) iperf3はWindowsにまだ対応していません。 Note that iperf3.X is not backwards compatible with iperf2.X. また後方互換性もないようなので気をつけたいと思います。 インストール iperf3のインストールは、とっても簡単です。 Amazon Linuxで試した感じでは、gccを先にインストールし、 iperf3をソースからさくっとインストールできます。 sudo yum install gcc -y wget http://stats.es.net/software/iperf-3.0.1.tar.gz tar zxvf iperf-3.0.1.tar.gz cd iperf-3.0.1 ./configure make sudo make install type iperf3 # iperf3 is /usr/local/bin/iperf3 ※ Iperf(ver2系)をインストールする場合にはgccと、gcc-c++が必要になります。 基本的な使い方 サーバ側を待ち受け状態にし、クライアント側からパフォーマンステストを行います。 以下の例では、クライアント(172.20.17.181)とサーバ(172.20.17.182)間について、 3秒のパフォーマンステストを行った例になります。 サーバ側 [ec2-user@ip-172-20-17-182 ~]$ iperf3 -s クライアント側 [ec2-user@ip-172-20-17-181 ~]$ iperf3 -c 172.20.17.182 -t 3 Connecting to host 172.20.17.182, port 5201 [ 4] local 172.20.17.181 port 44130 connected to 172.20.17.182 port 5201 [ ID] Interval Transfer Bandwidth Retr [ 4] 0.00-1.02 sec 18.6 MBytes 154 Mbits/sec 18 [ 4] 1.02-2.02 sec 16.6 MBytes 139 Mbits/sec 4 [ 4] 2.02-3.01 sec 18.0 MBytes 153 Mbits/sec 12 - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bandwidth Retr [ 4] 0.00-3.01 sec 53.2 MBytes 148 Mbits/sec 34 sender [ 4] 0.00-3.01 sec 53.0 MBytes 148 Mbits/sec receiver iperf Done. Iperfと基本的な使い方は変わっていなかったので、すぐに使うことができました。 iperf3での新機能 iperf3で追加された機能を見ていきます。 タイトルをつける -T, --title str prefix every output line with this string 大文字Tのオプションで、出力にプレフィックスをつけることができます。 [ec2-user@ip-172-20-17-181 ~]$ iperf3 -c 172.20.17.182 -t 3 -T `date +%F` 2014-02-03: Connecting to host 172.20.17.182, port 5201 2014-02-03: [ 4] local 172.20.17.181 port 44122 connected to 172.20.17.182 port 5201 2014-02-03: [ ID] Interval Transfer Bandwidth Retr 2014-02-03: [ 4] 0.00-1.00 sec 27.5 MBytes 231 Mbits/sec 10 2014-02-03: [ 4] 1.00-2.00 sec 25.8 MBytes 216 Mbits/sec 1 2014-02-03: [ 4] 2.00-3.00 sec 26.0 MBytes 218 Mbits/sec 4 2014-02-03: - - - - - - - - - - - - - - - - - - - - - - - - - 2014-02-03: [ ID] Interval Transfer Bandwidth Retr 2014-02-03: [ 4] 0.00-3.00 sec 79.2 MBytes 222 Mbits/sec 15 sender 2014-02-03: [ 4] 0.00-3.00 sec 78.9 MBytes 221 Mbits/sec receiver 2014-02-03: 2014-02-03: iperf Done. Iperf(ver2系)では、Tオプションは、ttlを設定するオプションなので注意が必要ですね。 -T, --ttl # time-to-live, for multicast (default 1) 詳細情報を表示する -V, --verbose more detailed output 地味に嬉しい機能です。 定期的に帯域テストをしてログ出力するような場合などは、versionやCPU Utilizationの表示が便利そうですね。 [ec2-user@ip-172-20-17-181 ~]$ iperf3 -c 172.20.17.182 -t 3 -V iperf version 3.0.1 (10 January 2014) Linux ip-172-20-17-181 3.4.76-65.111.amzn1.x86_64 #1 SMP Tue Jan 14 21:06:49 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux Time: Mon, 03 Feb 2014 11:36:04 GMT Connecting to host 172.20.17.182, port 5201 Cookie: ip-172-20-17-181.1391427364.967092.2 TCP MSS: 1448 (default) [ 4] local 172.20.17.181 port 44124 connected to 172.20.17.182 port 5201 Starting Test: protocol: TCP, 1 streams, 131072 byte blocks, omitting 0 seconds, 3 second test [ ID] Interval Transfer Bandwidth Retr [ 4] 0.00-1.01 sec 22.0 MBytes 182 Mbits/sec 21 [ 4] 1.01-2.00 sec 22.5 MBytes 191 Mbits/sec 51 [ 4] 2.00-3.00 sec 19.1 MBytes 160 Mbits/sec 5 - - - - - - - - - - - - - - - - - - - - - - - - - Test Complete. Summary Results: [ ID] Interval Transfer Bandwidth Retr [ 4] 0.00-3.00 sec 63.6 MBytes 178 Mbits/sec 77 sender [ 4] 0.00-3.00 sec 63.1 MBytes 176 Mbits/sec receiver CPU Utilization: local/sender 2.3% (0.9%u/1.8%s), remote/receiver 0.5% (0.1%u/0.4%s) iperf Done. はじめのn秒を省く -O, --omit N omit the first n seconds 最初の数秒を統計から省くことができます。 接続直後のネットワークが不安定な場合などに異常値を弾くことができて便利ですね。 [ec2-user@ip-172-20-17-181 ~]$ iperf3 -c 172.20.17.182 -t 3 -O 1 Connecting to host 172.20.17.182, port 5201 [ 4] local 172.20.17.181 port 44132 connected to 172.20.17.182 port 5201 [ ID] Interval Transfer Bandwidth Retr [ 4] 0.00-1.00 sec 18.1 MBytes 152 Mbits/sec 10 (omitted) [ 4] 0.00-1.10 sec 22.9 MBytes 174 Mbits/sec 2 [ 4] 1.10-2.00 sec 13.6 MBytes 127 Mbits/sec 0 [ 4] 2.00-3.00 sec 20.6 MBytes 173 Mbits/sec 2 - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bandwidth Retr [ 4] 0.00-3.00 sec 57.1 MBytes 160 Mbits/sec 4 sender [ 4] 0.00-3.00 sec 58.4 MBytes 163 Mbits/sec receiver iperf Done. リバースモード -R, --reverse run in reverse mode (server sends, client receives) 通常はクライアントから送信するのですが、このオプションを付けるとサーバから送信するモードになります。 サーバーとクライアントを入れ替えて帯域のテストをしたい場合に簡単にできます。 [ec2-user@ip-172-20-17-181 ~]$ iperf3 -c 172.20.17.182 -t 3 -R Connecting to host 172.20.17.182, port 5201 Reverse mode, remote host 172.20.17.182 is sending [ 4] local 172.20.17.181 port 44154 connected to 172.20.17.182 port 5201 [ ID] Interval Transfer Bandwidth [ 4] 0.00-1.01 sec 23.5 MBytes 196 Mbits/sec [ 4] 1.01-2.01 sec 27.6 MBytes 232 Mbits/sec [ 4] 2.01-3.01 sec 24.4 MBytes 204 Mbits/sec - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bandwidth Retr [ 4] 0.00-3.01 sec 76.2 MBytes 213 Mbits/sec 28 sender [ 4] 0.00-3.01 sec 76.0 MBytes 212 Mbits/sec receiver iperf Done. JSONで出力する -J, --json output in JSON format 詳細情報をJSONフォーマットで出力することができるので、mongoDBやElasticSearchに入れたりするのに使えそうです。 [ec2-user@ip-172-20-17-181 ~]$ iperf3 -c 172.20.17.182 -t 3 -J { "start": { "version": "iperf version 3.0.1 (10 January 2014)", "system_info": "Linux ip-172-20-17-181 3.4.76-65.111.amzn1.x86_64 #1 SMP Tue Jan 14 21:06:49 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux\n", "timestamp": { "time": "Mon, 03 Feb 2014 11:37:29 GMT", "timesecs": 1391427449 }, "connecting_to": { "host": "172.20.17.182", "port": 5201 }, "cookie": "ip-172-20-17-181.1391427449.704612.4", "tcp_mss_default": 1448, "connected": { "socket": 4, "local_host": "172.20.17.181", "local_port": 44128, "remote_host": "172.20.17.182", "remote_port": 5201 }, "test_start": { "protocol": "TCP", "num_streams": 1, "blksize": 131072, "omit": 0, "duration": 3, "bytes": 0, "blocks": 0 } }, "intervals": [ { "streams": [ { "socket": 4, "start": 0, "end": 1.00007, "seconds": 1.00007, "bytes": 24641536, "bits_per_second": 1.97119E+8, "retransmits": 50, "snd_cwnd": 127424, "omitted": false } ] }, { "streams": [ { "socket": 4, "start": 1.00007, "end": 2.00013, "seconds": 1.00006, "bytes": 19398656, "bits_per_second": 1.5518E+8, "retransmits": 1, "snd_cwnd": 128872, "omitted": false } ] }, { "streams": [ { "socket": 4, "start": 2.00013, "end": 3.00011, "seconds": 0.999985, "bytes": 15990784, "bits_per_second": 1.27928E+8, "retransmits": 3, "snd_cwnd": 141904, "omitted": false } ] } ], "end": { "streams": [ { "sender": { "socket": 4, "start": 0, "end": 3.00011, "seconds": 3.00011, "bytes": 60030976, "bits_per_second": 1.60077E+8, "retransmits": 54 }, "receiver": { "socket": 4, "start": 0, "end": 3.00011, "seconds": 3.00011, "bytes": 59375616, "bits_per_second": 1.58329E+8 } } ], "cpu_utilization_percent": { "host_total": 2.26325, "host_user": 0.258657, "host_system": 2.32805, "remote_total": 0.921219, "remote_user": 0.117254, "remote_system": 0.787269 } } } 他にも CPU Affinityの設定や、zero copyでの送信など、しっかり計測したい場合のオプションが追加されています。 -A, --affinity n/n,m set CPU affinity -Z, --zerocopy use a 'zero copy' method of sending data また、linux-congestionなどはオプションが "-Z" から "-C" へと、若干変わってたりするので気をつけたいですね。 -Z, --linux-congestion set TCP congestion control algorithm (Linux only) # Iperf -C, --linux-congestion set TCP congestion control algorithm (Linux only) # iperf3 まとめ 主に、iperf3の新機能について見てきました。 基本的な操作は、ほとんどIperfと変わっていないので以前からのユーザは違和感なく使えると思います。 先に書きましたが、出力の形を整える機能や計測に対しての細かい設定ができるようになったのがありがたいですね。 また、測定結果の単位にG(ギガ)が追加されたことも時代の流れを感じてしまいました。 昔から使っている方も、新しく使いはじめる方も癖なく使えると思いますので、 是非、iperf3を触ってみてください! 参考リンク https://code.google.com/p/iperf/ http://sourceforge.net/projects/iperf/ http://en.wikipedia.org/wiki/Iperf
Apple原理主義者の大坪です。 Facebookが米国で2月3日に新しいアプリ、"Paper"を公開するというニュースが流れました。 Introducing Paper from Facebook on Vimeo . ふーん、きっと日本で公開されれば普通Facebookを見るのもこちらに切り替えるんだろうなあ。なんたって今のFacebookアプリは,,ところでこれ写真の上に文字を重ねるのどうやってるんだろう、とかのんびり考えていたわけです。 しかしのんびりしている場合ではなかった。次に出てきたのがこのニュースです。 But what you might not have heard is the story of the tool behind Paper-- Origami . Facebook developed Origami to be a rapid prototyping tool for interfaces, to allow its designers to create and test complex, original animations without the help of the coding team. via: Facebook Develops A Photoshop For Interaction Design, And It's Free For Anyone To Use | Co.Design | business + design いいかげんな訳:しかしPaperの開発に使われたツールについては聞いたことがないかもしれない。Facebookはインタフェースのラピッドプロトタイピングツールとして"Origami"を開発した。それを使えば、デザインチームは、コードを書くエンジニアの助けなしに、複雑なアニメーションを作り、テストすることができる。 引用先の表現を借りれば「インタラクションデザインにとってのPhotoshop」つまりは必携ツール、とのこと。 「iOS7からは動きのデザインが重要になるよねー。動きのデザインってどうやってやればいいのー。紙じゃ無理だよねー。デザイナーとエンジニアもっと協力しないとねー」 とか 他人ごとのように書いていた のがほぼ2ヶ月前。これがチンピラエンジニアとFacebookチームの差異というものなのでしょうか。彼らは単に嘆いているのではなく、「動きのデザイン」を可能にするツールを作成し、かつ(気前のいいことに)無料で公開しているわけです。 Origamiは単独のアプリケーションではなく、Quartz Composerのプラグインとして提供されています。Quartz Composerとはなにか?それはある人がMac OS 10.4 Tigerの「目玉機能」と称したビジュアルなプログラミング環境です。とても興味深いものの、なかなか広く使われていなかった技術でもありました。 さっそく ダウンロードページ から、インストラクションに従ってインストールしてみると、、ををこれは紛う方なき懐かしいQuartz Composerの画面だ。その上で例えばこんな動きを試すことができます。 (これは Facebookが公開している サンプルです) こうした「動き」の検討が紙の上で可能でしょうか?そして今までこうした「動き」のプロトタイプがエンジニアの悲鳴もしくは怨嗟の言葉抜きで可能だったでしょうか? 「ほら、こうやって上にスクロールするとメニューが”にゅい”っと隠れて、でもって指を離すとちょっとバウンドして」 とか言ったところで話はさっぱり進まない。 というわけで 数年ぶりにQuartz Composerの「プログラミング」を思い出したり調べたりしているわけです。下の図がその「プログラミング」の画面。一見とっつきやすそうに見えるが、思ったとおりの動きを実現するのはやはり簡単ではない。 しかし実際にPaperのデザイナー達は、これを使ってPaperのプロトタイプを作ったのです。 And in fact, almost every animation you see in Paper was prototyped first in Origami before it was handed to Facebook's programmers to build into the real app. via: Facebook Develops A Photoshop For Interaction Design, And It's Free For Anyone To Use | Co.Design | business + design Paperで使われているほとんどのアニメーションは、Origami上でプロトタイプされ、そのあと実際のアプリ開発を行うプログラマーに渡された。 つまりOrigamiで作られたプロトタイプは、デザイナーからプログラマーに渡された「仕様書」でもあったわけです。こうした新しいプロセスは正しい方向に向けた第一歩であることは間違いない。 Facebook firmly believes that Origami can strengthen the work of young designers in high school and college today, driving them to think about fluid interaction design rather than static image design. via: Facebook Develops A Photoshop For Interaction Design, And It's Free For Anyone To Use | Co.Design | business + design かなりの意訳:Origamiが高校や大学で若いデザイナー達の役に立つことをFacebookは確信している。Origamiを使うことで、静的な画像のデザインではなく、動的なインタラクションの流れについてより深く考えるようになるだろう。 近い将来、採用面接で「Origami使ったことないの?それでアプリのデザイナー志望?」と言われる日が来るのでしょうか。
Apple原理主義者の大坪です。 iPhoneはこれまで毎年新しいモデル(新しさの度合いは様々ですが)が発表されてきました。というわけで毎年新モデルが発表された直後から「次のモデルは」という噂が出始めるわけです。 Appleが新しい製品に対して非常に厳しい秘密保持を行っていることはよく知られています。しかし末端の部品サプライヤまでその厳しさを徹底させることはどうやら不可能らしい。新しい機種の発表が近づくにつれ様々な部品がリークされ、実際の発表日までにはほとんどその外観が判明します。 さて、例年のサイクルによれば秋に発表があるはずのiPhone6ですが、かなり早い段階から「今度のiPhoneは画面が大きくなる」という噂がでていました。(加えて「2種類の画面サイズがある」という情報もありますが、それはさておき) スマートフォンとか携帯電話の記事、あるいは噂を見ると「最近は大画面化がトレンドだ」というあたかも「 バスに乗り遅れるな 」といった論調を見かけることもあります。例えば横軸に年代、縦軸に画面サイズをプロットして、適当に「トレンドを示す直線」を引き「次のモデルの画面サイズはこのあたりが適当」とかやっている会社も世の中にはあるのかもしれない。しかしApple原理主義者として、Appleはそんなサイズの決め方はしない、と信じたい。 では 仮にAppleが画面サイズを大きくするとして、どのようなアプローチがあるか。9 to 5 Macの こちらの記事及びコメント欄 で活発な議論がなされています。 私なりに論点を整理しますと 仮にiPhone6の画面が物理的に大型になった場合、ソフトウェアの面から見ると以下の2つのアプローチが考えられます。 アプローチ1:画面の画素数は同じままにする。(つまり一画素のサイズが画面サイズに合わせて大きくなる) アプローチ2:画面のサイズ拡大に応じて、画素数も多くする。 記事の著者によれば、アプローチ1を採用し、画素数そのままで画面サイズを5インチに拡大したとしてもRetina(つまり肉眼で個々のピクセルが見えないくらいの解像度ということ)と名乗るにふわさしい画面の細かさが維持できるとのこと。この場合アプリ開発者は「ちょっと画面が大きくなるだけで何もかわんないもんね」と枕を高くして眠れるわけです。 しかし その場合、「なーんとなく引き伸ばした」ということにもなりかねず、そもそも何のための画面大型化か、という問題がでてくる。というわけで例えば一平方インチあたりの画素密度は同じで画面サイズを大きくし、そのぶん画素数も大きくするアプローチ2が説得力を持ってくるわけです。 ただし この場合悲鳴を上げるのは開発者です。320X480という画素数で平和に暮らしていた時代はRetina の登場で揺さぶられ、まだ大丈夫だ、大きいイメージだけ用意すればいいじゃないかと思っていた開発者はiPhone5の登場で「ぎゃっ」となったわけです。320X568ってなんだよ。画素数が変わるだけじゃなく画面の縦横比が変わっちゃうじゃないか。とかなんとか文句を言いながらも結局対応せざるを得ない。 さてAppleはどちらのアプローチを選択するでしょうか? これに対してはいろいろな立場のコメントがあり 【スペック原理主義者】5インチサイズで4Kディスプレイが必要だ! →この意見に対しては ・AppleはSpec競争にこだわったことはない ・価格とかバッテリとかどうすんだ といった反論がでています。 【現実的な開発者】AppleはiPhone5で一度開発者を「ぎゃっ」と言わせたからもう一回それが起こっても驚かないよ。もうAutolayout対応すませたもんね。(Autolayoutという仕組みを使ってアプリを作ると、画面サイズが変わっても「ある程度」自動で対応してくれるのです) 【冷静な開発者】iOS7からは、画面遷移した後戻る操作を行うのに、画面上部の「戻る」ボタンを押さなくても画面を左端からスワイプするだけで可能になっている。これはさらなる大画面化を見越した機能追加だったのではないか。(つまり画面上部に指が届かなくても大丈夫なように) 【冷静で現実的な開発者】画面サイズ変わっても、縦横比が変わらなければそんなにひどいことにならないんじゃないかな… 【小画面原理主義者】iPhoneの良さは画面がコンパクトなところだ。大きくするならAndroidに替えるぞ。 【大画面原理主義者】iPhoneの悪い点は画面が小さすぎるところだ。大きくしないならAndroidに替えるぞ。 【そもそも論】いや、問題は画面サイズではなくバッテリ持続時間じゃね?大きくして例えば12時間バッテリが持つようになればそうする意味があると思うけど。 というわけで プログラムを開発する立場としては、画面拡大に伴って画素数の変更アナウンスがでても心臓発作を起こすこと無く冷静に受け止める必要がある、と悟るわけです。多くの人が忘れているかもしれませんが、ガラケー全盛期には、Docomoのケータイだけとってみても、それはそれはものすごい数の画面サイズが存在しており、縦横比すらとっても様々だったわけです。あれに対応する事を考えれば、iPhoneの画面サイズのバリエーションなんて可愛いもんさ(涙をそっとふく) またこんな意見もあります。 【自称クレージーなアイディア】6月のWWDCで新画面サイズ+新画素数だけアナウンスだ。そうすれば秋に新画面サイズのiPhoneが発売されたときにアプリが対応できる! クレージーと自称してはいるものの、ここらへんが正解かもなあとも思えます。とはいえひょっとすると今年は「新型iPhone」は脇役かもしれない。以前からTim Cookは「2014年には新しい製品を投入する」と宣言しており、その発表のインパクトを高めるため、iPhone6は6月に発表してしまって、、、とか想像だけが膨らみます。 いずれにせよ Appleが画面を大きくするのであれば、それにはちゃんとした理由があるはず。そもそも何故大画面化する必要があるのか?それに対して「トレンドだから」という馬鹿げた理由ではない、ちゃんとした答えを出してくることを期待したいものです。それは新しいデザインかもしれず、あるいは新しい使い方の提案かもしれない。意見にもあったように「大画面化の主目的はバッテリ持続時間の向上」というのも(技術的に本当かどうかは別として)理由としては納得できる。 それはたとえばiPhone5Cが単なる「コストダウンのためにプラスチック筐体を使用した」だけのものではなかったように(Apple原理主義者はそういう捉え方をするのです。異論は受付けません) 今はただおとなしく6月だか9月が訪れるのを待ちましょう。Appleがどんな提案をしてくるのかを楽しみにしながら、そしてどんな画素数が飛んできてもくじけないだけの気力と、わかりづらいAutolayoutにも笑顔で対応できる心のゆとりを養いながら。
大坪と申します。さて、長いサンプルプログラムはよくわからない。ポイントを含んでできる限り短くシンプルに、が信条のSimple Exampleシリーズ第2弾はiOS7から可能になったViewController間のTransitionです。 ここで 「ではいろいろな種類のTransitionについて一つにサンプルで」 とやるとSimple Exampleの主旨から外れるので 「UINavigationControllerに対してpushViewControllerする場合に独自アニメーションをつけて遷移(transition)する」 だけの例を説明します。他の例(というかもっと丁寧な説明)はネット上にいくつか存在していますが、例えばこちらのブログ [iOS 7] &#x7C21;&#x5358;&#x306B;&#x3067;&#x304D;&#x308B;&#x753B;&#x9762;&#x9077;&#x79FB;&#x306E;&#x30AB;&#x30B9;&#x30BF;&#x30DE;&#x30A4;&#x30BA; - iOS &#x958B;&#x767A;&#x30D6;&#x30ED;&#x30B0; Natsu&#39;s note を参照ください。 サンプルプログラムは github に置きました。 起動するとこんな画面がでてきます。 あまりの色彩センスの良さにめまいがするかもしれませんが、気のせいです。これぐらいでひるんでいてはかっこいいTransitionなど実現できません。 まずオレンジの"Simple Transition"というボタンを押します。 するとこんな感じで画面が遷移します。慣れ親しんだnavigationControllerにpushViewControllerすればこうなるわな、という動きです。 次に赤い"Magnify Transition"というボタンを押してみます。 すると、ずりずりと中央から黄色い画面が広がる。それと同時にナビゲーションバーに"< Back"という文字が出てきます。 その文字をを押すと黄色い画面が縮小していき、後ろに青い(最初に見えた)画面が出てくる。iOS7から可能になったView間Transitionのカスタマイズ機能を使うことこういうことができるわけです。 さて、この動きの実現方法ですが... まず"Simple Transition"のほうです。ViewController.mを見ましょう。Simple Transitionボタンを押すと以下のコードが実行されます。 Pushされる(つまり次に表示される)ViewControllerを作ってPushしている。普通のコードですが、その間に謎の「//delegateをクリアする」 という行が入っています。 なんとなく不安を覚えながらも、"Magnify Transition"を押した時に実行される部分を見てみます。 ViewController.mの-(void) magnifyButtonPushed:(id)senderですが。。 なんださっきと変わらないじゃないかと思われるかもしれません。実際違うのは一行だけで self .navigationController.delegate = _transitioningDelegate; ここだけです。(Simple Transitionではnilをセットしていた) この_transitionDelegateは-(void) viewDidLoadの中で[TransitionDelegate new]として作成しています。 ではこのTransitionDelegateは何をしているかと言えばmethodは一つしか無く これだけです。if文の中身は見なかったことにして、やっていることは大枠 「MagAnimTransitionを作って返す」 ということ。であれば、MagAnimTransitionがあれこれややこしいことをやっているに違いない。というわけでMagAnimTransition.mの- (void)animateTransition:(id )transitionContext を見てみましょう。 ..... と書いておいてなんなのですが、このメソッドの中をちゃんと説明しだすととても長くなるので、ソースを見てください。(ちょびっとコメントも書いてあります)先ほど見なかったことにしたif文は、「行きと帰り」でアニメーションがちょっと違うのでそれを区別するためのフラグをたてていたのでした。 「行き」だけコードに沿って説明すると まず「行き先」のViewのサイズを0にして 次にそれをaddSubviewする。 でもってUIView animateKeyframesWithDuration:の中で「行き先」のViewサイズをアニメーション付きで戻して 最後に[transitionContext completeTransition:finished]; を呼ぶ(これ必要) - 最低限これだけやれば、サンプルプログラムにあるような動きが実現できます。そもそもここにでてきたdelegateだのクラスは何をやっているのだ、ということをちゃんと調べたい方は、前述のブログあるいは他の文章を参照ください。 私はとりあえず動くコードがあってそこからあれこれいじっていく人なので、同じような調べ方をする人にとって、このサンプルが何かの参考になればと思っております。
こんにちは、上津原です。 Couchbase [Tokyo] 2014、 Couchbase Liteの日本発公式発表があるので行ってきました。 会場はほぼ満員でした。年に一度しか無いイベントですしね。 Couchbase Server側の話は置いといて、自分の土俵のCouchbase Lite側に絞ってレポートします。 プレゼンテーション Couchbase Liteのプレゼンテーションは、ジェシカさんという女性の方が行いました。 ざっくりとCouchbase Liteとはどういうもので、どんなふうに動くのか、ということを話していて、要約すると以下の様な感じでした。 CouchbaseLiteは世界初のモバイル向けNoSQL ローカルデータをCouchbase Sync Gatewayを経由してSyncする Couchbase Sync Gatewayは、Channelを使って、閲覧可能な情報を制限することができる Channelは、ドキュメントにつけるタグのようなもの FacebookやPersonaを使って認証できるよ Syncもとっても簡単でお手軽に出来るよ パージして、モバイルローカル上のデータだけ消すこともできるよ そしてTodoLite( https://github.com/couchbaselabs/TodoLite-iOS )のデモ TodoのiOSアプリケーションで、CouchbaseLiteでデータ管理をしている。 Facebook認証も入っていて、任意のユーザとTodoを共有できるもの。 あれ、この前まで動いてなかったのに動いてる…。 聞いてみたら、最近動くようになったとのこと。これは僕も動かしてみなければ。 ジェシカさんに僕が個人的に聞いたこと Q: CouchbaseLiteは、モバイルアプリ技術者が単独で使うために作ってる?それともサーバ側の人も一緒に使う用に作ってる? A: CouchbaseLiteのSyncは、モバイル技術者が一人で開発するものではなく、サーバーサイド技術者とコラボレートして利用することを想定してるよ。 Q: CouchbaseServerを理解せずに触って、ViewとかQueryの考え方がよくわからない事が多かったんだけど、モバイル技術者のために、ViewとかQueryのチュートリアルとか無いの? A: ドキュメントがちょっとずつ充実してきてるから見てね。 ViewやQueryの使い方はCouchbaseServerとほぼ同じだからそっちの資料を参考に学ぶといいよ。 動いているものは、さっきのTodoLiteがgithubにあるからコードリーディングして学んでね。 Q: TodoLite以外サンプルアプリ動かそうとしたら動かないんだけど… A: CouchChatとかはもう全然触られてないから、あれは動かないよ。開発者も飽きちゃったみたい。 TodoLiteしか動かないと思う。ごめんね。 最後にジェシカさんから: わからないことはコミュニティかメールでで聞いてくれれば答えるよ。 あなたの問題はみんなの問題だし、わかりにくいのは良くないからね。 バグとかみつけても教えてね 。 一緒に出席してたCouchbaseServer技術者の友達にCouchbaseLiteについてどう思うか聞いた 「ものは面白いけど、SyncGatewayがセキュアなのかどうかが気になる。  SyncGatewayとCouchbaseの間になにかミドルウェアを挟んで対処する必要があるかもなあ。  ゲームだとそこをついてチートするユーザとかがいるからそこの対処ができないとしんどい。」 まとめ Couchbase Liteは使いやすいし、導入も簡単だけど、SyncするためにCouchbaseServerを導入するかどうかがやっぱり気になるところ。(もちろん、実装すればCouchbaseを使わなくてもSyncできるよ!) beta2ということもあって、まだまだ発展途上なCouchbaseLiteだけど、認証なども加わり、ネットワークと連携する強力なモバイルデータベースとなる可能性は十分秘めていると思います。 CouchbaseServerの需要が高まるに連れて、CouchbaseLiteの需要も一緒に上がっていくのかな〜というイメージです。 そしてCouchbase [Tokyo] 2014は、Couchbaseの開発者やセールスの皆さんと話ができるチャンスです。 そして英語ができない僕でも、3次会のカラオケでCHA-LA HEAD-CHA-LAをみんなで歌えちゃうくらいのフレンドリーさがあります。 次のCouchbase [Tokyo]は来年になっちゃうけど、ミートアップもちょいちょいやったりしているので、ちょっと興味があるくらいでもうれしいので参加者が増えていくといいな〜と思います。 あんまりみんな知らない、新しい、面白い技術って、わくわくするでしょ?
今回は、ここまで作ってきたデータをCouchDBと同期してみたいと思います。 この同期機能もCouchbase Liteの大きな特徴です。 Push、Pullどちらも機能を兼ね備えておりかつ、実装が簡単なのが特徴です。 普段にありがちなAPI問い合わせのメソッドなども書く必要は一切ありません。 同期対象は、CouchDB、またはCouchbase(CouchbaseはSyncGatewayが必要)となっています。 今回は、CouchDBとのSyncの方が簡単なのでそちらを見て行きましょう。 まず同期対象となるCouchDBを用意しなくてはいけません。 まずは以下に登録してください。 Cloudantに登録 Cloudant https://cloudant.com/ 値段は、使用料が毎月5ドル以下なら無料となっており、ちょっと触ってみる程度なら課金されません。 登録完了し、サインインすると、ダッシュボードが表示されます。 上部にある、New databaseに作りたいデータベース名を入力し、Createでデータベースを作成します。 これができれば準備完了です。 Replicationしよう! レプリケーション先の準備ができれば、あとはコードを書くだけです。 gist8331915 URL部分は、各自以下のとおり変更を行ってください。 yourId:先ほど登録したID password:先ほど登録したパスワード dbName:先ほど作成したデータベース名 設定できたら、viewDidLoadでこのメソッドを呼出し、Runしてみてください。 これだけで同期はできているはずです。 Cloudantの「Your database」にある、先ほど作成したdatabaseを選択し、ドキュメント情報を見てみましょう。 CouchbaseLiteで作成したドキュメントがそっくりそのまま同期されているはずです。 補足 実は、同期するだけなら、 NSArray *repls = [ap.database replicateWithURL:[NSURL URLWithString:@"https://yourId:password@yourId.cloudant.com/dbName"] exclusively:YES];&lt;br&gt; この項目だけで出来ます。 下に続いているpullとpushは、こちらが任意のタイミングでPull(GET)やPush(Post)を行うために必要になります。 任意のタイミングで同期する これも非常に簡単です。 先ほど作成した、pull、pushに対してメッセージをひとつ送るだけです。 [ pull start] [ push start] これだけで同期を開始します。 進捗を追う 進捗を追うには、最初に設定したobserverを追います。 公式では、completeを追っていましたが、時々completeしないことがあるので、modeを追っています gist8331912 CBLReplicationMode には4つのモードが有り、 kCBLReplicationStopped, / *&lt; The replication is finished or hit a fatal error. / kCBLReplicationOffline, / *&lt; The remote host is currently unreachable. / kCBLReplicationIdle, / *&lt; Continuous replication is caught up and waiting for more changes. / kCBLReplicationActive / *&lt; The replication is actively transferring data. / が存在します。 kCBLReplicationStopped になると、レプリケーション完了、もしくはエラーのモードなので、そこを終了点として持ちます。 このコードでは、同期が完了したらDBを読み直す処理をしています。 ざっくりですが、このような感じでCouchbase LiteとCouchDBを同期することができます。
サムです。 2014/1/15ついに "Google Analytics SDK for iOS" が version 3.0.3 で 64bit 対応版のSDKが公開されました。 変更ログ Google Developers にある 変更ログ には次のように記されていました。 This release contains: ・Added support for 64-bit iOS 7.0 SDK. ・Removed libGoogleAnalytics_debug.a, it’s part of the libGoogleAnalyticsServices.a library. ・Cleaned up CuteAnimals build file for Google Analytics. ・64-bit対応しました。 ・libGoogleAnalytics_debug.a を削除し、libGoogleAnalyticsServices.a に統合した。 ・Google Analytics Services SDK 内にある Exsample Code: CuteAnimals/ をクリーンアップした。 SDKの入手について SDKのダウンロードは Google Developers からできます。 また、Google Analytics は CocoaPods にも対応しており、Podfileに次のライブラリを追加すればOKです pod &#39;GoogleAnalytics-iOS-SDK&#39;, &#39;3.0.3&#39; ビルドしているアーキテクチャの確認 Xcode BuildSettings &gt; Architectures &gt; Architectures を確認する。 Standard architectures (including 64 bit) (armv7, armv7s, arm64) なら64-bitビルド Standard arcitectures (armv7, armv7s) なら32-bitビルドです。 さいごに これまで多くのDeveloperが64-bit対応を見送っておりましたが、やっとこさGoogleも対応してきましたね、これは素直に喜べます。
上津原です。 今回は、Couchbase Liteの機能のひとつである、LiveQuery機能を見ていきたいと思います。 LiveQueryとは、指定したクエリ結果に影響するデータ変更があった場合、自動で通知を出してくれる機能です。 例えば、すべてのドキュメントを呼び出していた時ならば、何かしらデータベースに変更(ドキュメントの追加、削除、更新)が行われた場合に通知が上がってくる、というものです。 これを利用することによって、 変更があった時にすぐに表示に反映する 変更があったらすぐに同期を行う などの対応などが可能になります。 コードはとってもシンプルです。 以下のようになります。 gist8143715 以上です。 KeyPathが「row」じゃなくて「rows」なのでそこに注意が必要です。 通知が取れれば、ここでpushするなり、表示を更新するなりやればリアルタイム反映が可能になります。 これで、データが編集されればそれに対して即時対応や、UIの更新などを行うことが出来、非常に便利です。 欲しい機能が予めこうやって用意されているのはうれしいですね。
Apple原理主義者の大坪です。 PinterestのiOSアプリが3.0にバージョンアップするにあたり、どのような検討がなされたかがPinterest Engineering Blog Making Pinterest &mdash; Behind the Pins: Building Pinterest 3.0 for iOS に公開されています。 iOS7公開後にどのような「バージョンアップ」を行ったかがわかり興味深いので、内容について紹介しつつあれこれ書ければと。 - Ver3.0を作るにあたって大きなポイントは以下の3点だったとのこと。 UICollectionViewの採用とiOS5のサポート停止 iOS7の新機能、View間Transitionの採用 ジェスチャの見直し まず一点目。私もUICollectionViewの概要を知った時には驚きましたが、その機能を使う為にはiOS5以前のサポートを諦めなくてはならない。これをどこで行うかは各社悩ましいところだと思います。 私の理解では2点目と3点目は関連しています。PinterestのiOSアプリには、複数の写真情報を提示している一覧画面と、その中の写真をタップした時に表示される詳細画面が存在している。 アプリの使用状況を調査したところ、多くのユーザが詳細画面を長い時間使っており、かつ別の詳細情報を観る時に、「戻る」ボタンで一覧に戻り、次の詳細情報を観る、という行動をしていることがわかったとのこと。 そのため 詳細画面から左右にスワイプして前後の詳細情報を閲覧できるようにした 一覧画面から詳細画面に遷移する際、そうした「左右へのスワイプ」が可能なことを示すため、一覧中の一つの写真が拡大して、詳細画面になるような遷移(iOS7から可能になった機能です)を採用した とのこと。このように彼らはインタラクションフローの見直しと合わせてiOS7の新機能をとりいれ、結果としてユーザがより便利かつ直感的に使えるようにアプリを改善したのでしょう。 この「一覧画面」「詳細画面」の関連付けについては悩むことが多いです。作る側としては、きっぱり分けてその間を遷移させればよい、ということになるのですが、Pinsterestの解析結果に現れているように必ずしもそれはユーザの直感に合致しているわけではない。二つの異なる情報レベルをもった画面を登ったり降りたりするのではなく、直接同じレベル(例えば詳細画面)にある別の情報に移動できた方がうれしい。その点で、今回Pinterestが用いた方法は参考になる点が多いのではないでしょうか。 - 最後に一点。「iOS7以降では動きのデザインがより重要になる」という主張をしている身としては、彼らが挙げている「改善点」の2、3点目がいずれも「動き」に関するものであることに興味を惹かれます。全体の操作フローを見る立場と、画面遷移の動きをコードで実現する立場双方が協力してできあがった新しいインタラクションではないかな、と想像するわけですが。
今まで、NSDictionaryを使ってドキュメントをCRUDをしてきましたが、実はモデルを使ったほうが俄然使いやすいです! なので、今回はCBLModelというクラスを利用していきます。 これを使うことにより、面倒だったNSDictionaryの作成などがなくなります。 以下のように宣言します。 CBLModel ひとつのドキュメントに格納したいデータを、上記のようにプロパティ宣言をし、.mで@dynamic宣言をすればそれだけでおしまいです。 とっても簡単です。 ドキュメント作成 CBLModelでCreate ドキュメント読み込み CBLModelでRead ドキュメント更新 CBLModelでUpdate ドキュメント削除 CBLModelでDelete いかがでしょう。Modelを使うことで、今まであったDictionaryの操作がなくなり、プロパティへ直接アクセスすることで操作が可能になります。 ドキュメント操作時に出てきていたCBLDocumentも読み込み時にしか出てきません。 作成、更新はputではなく、saveを使うように変わり、意味的にもちょっとわかりやすくなります。 モデル化のメリット プロパティによる値の明確化 作成、更新が容易になる NSDate,NSDataなどの利用が容易になる いちいちこのドキュメントには何が入ってて…とかログで確認したくもないですし、NSDictioary直で触るの嫌だから、このために新たに自作モデルクラスを…なんて事になってしまってはものすごい手間です。 そして、NSDateやNSDataを内部で補完する機能を持っているので、何も考えずにプロパティ宣言をして値を渡してしまえば簡単に保存してくれます。ちなみに、NSDataはbase64に勝手に変換してくれます。 おそらくモデルを利用することがCouchbase Liteの基本となるかな、と。
Apple原理主義者であることを公言している大坪と申します。 少し昔話をしましょう。今から7年前、2007年1月9日の早朝、私は寝ぼけ眼でAppleのサイトを開きました。この日はMacWorldの初日。数時間前にSteve Jobsがキーノートスピーチを行ったはず。そしてそこでは新しい携帯電話が発表されると噂されていたからです。 Appleのサイトをあれこれクリックするうち動画があることに気が付きます。それを見た瞬間眠気はどこかに吹き飛びました。なんだこれは。動揺しつつも自分に言い聞かせます。 「いや、これはデモに違いない。実機でこんなするする動くわけがない」 そう思いながらキーノートスピーチの動画を見続ける。信じ難いことですが、Appleのサイトに載っていた「動き」がそのまま実機で-小さい携帯電話上で-動いている。( &#x52D5;&#x753B; ) 観ているうち、頭の中に一つの考えがぐるぐる回りだしました。 「これは &#x30C9;&#x30EC;&#x30C3;&#x30C9;&#x30CE;&#x30FC;&#x30C8; だ」 そう考えたのは私だけではないようです。Androidの父、Andy Rubinはその時Las Vegasで会議のため移動中でした。しかしJobsが発表したものを知ると、車を止めさせwebcastに見入ったといいます。そして言った言葉が “Holy crap,” he said to one of his colleagues in the car. “I guess we’re not going to ship that phone.” 引用元: The Day Google Had to &#39;Start Over&#39; on Android - Fred Vogelstein - The Atlantic 「なんということだ。」彼は車の中で言った。「(今開発中の)あの携帯電話を出すわけにはいかないな」 Googleは2005年からAndroidを開発していました。その時プロジェクトメンバーは一週間に60-80時間働き(はい、そこで皮肉な笑みを浮かべないように)その年の終わりまでに製品として発表する予定だったのですが。 同じくGoogleのChris DeSalvoはこう言ったそうです。 “As a consumer I was blown away. I wanted one immediately. But as a Google engineer, I thought ‘We’re going to have to start over.’” 一消費者としは、ぶっ飛ばされた思いだ。すぐにでも手に入れたい。しかしGoogleのエンジニアとしては”全部やり直しだ” - さて時は戻って2013年。Steve Jobsの命日の前日に NY&#x30BF;&#x30A4;&#x30E0;&#x30B9;&#x306B;&#x3053;&#x306E;&#x65E5;&#x306E;&#x30D7;&#x30EC;&#x30BC;&#x30F3;&#x30C6;&#x30FC;&#x30B7;&#x30E7;&#x30F3;&#x306E;&#x5185;&#x5E55;&#x3092;&#x8A9E;&#x3063;&#x305F;&#x8A18;&#x4E8B; が公開されました。かなり前に発表されてますし、日本語訳もいくつかあるのですが、あまりに面白いので紹介せずにはいられない。これを読むと、私も含めた多くの人(Andy Rubinを含む)が「ぶっとぶ程の衝撃」を受けたiPhoneのデモが薄氷を踏むようなものだったことがわかります。そしてデモを支えたエンジニア達の気持ちが少し想像できるように思います。 すばらしい日本語訳は &#x30AA;&#x30EA;&#x30B8;&#x30CA;&#x30EB; iPhone &#x306E;&#x30C7;&#x30D3;&#x30E5;&#x30FC;&#x306F;&#x5927;&#x304D;&#x306A;&#x8CED;&#x3051;&#x3060;&#x3063;&#x305F; &mdash; iPhone &#x958B;&#x767A;&#x79D8;&#x8A71; | maclalala2 にあります。ここでは私が特に興味を惹かれた点だけを訳します。 Jobs had been practicing for five days, yet even on the last day of rehearsals the iPhone was still randomly dropping calls, losing its Internet connection, freezing or simply shutting down. Jobsは五日間に渡ってプレゼンのリハーサルをしていた。しかし最終日でさえiPhoneは通話を受け損なったり、ネットへの接続が切れたり、フリーズしたり単に異常終了していた。 まずここで驚くべきは、あのプレゼンのリハーサルは五日間にも渡って行われていた、ということ。良いプレゼンを行う「秘訣」についてはいろいろな議論がありますが、ある方が書いた言葉が一番真実に近いのかもしれません。 「結局練習じゃないか」 It worked fine if you sent an e-mail and then surfed the Web. If you did those things in reverse, however, it might not. Hours of trial and error had helped the iPhone team develop what engineers called “the golden path,” a specific set of tasks, performed in a specific way and order, that made the phone look as if it worked. メールを送ってその後にWebを見る場合はちゃんと動いた、しかし順序を変えると動かない。何時間にもわたる試行錯誤の末、iPhoneチームは「ゴールデンパス」を発見した。指定されたタスクを指定された順番で動かした場合だけ、iPhoneはちゃんと動作した。 ソフトウェアエンジニアであれば、この「ゴールデンパス」がどんなものか想像できると思います。そうなんですよね。なぜか順番を変えると動く。落ちる。何故だ。 Jobs wanted the demo phones he would use onstage to have their screens mirrored on the big screen behind him. (中略) So he had Apple engineers spend weeks fitting extra circuit boards and video cables onto the backs of the iPhones he would have onstage. (中略) But making the setup work flawlessly, given the iPhone’s other major problems, seemed hard to justify at the time. Jobsは使っているiPhoneの画面をそのままスクリーンに映し出したがった。そのため、Appleのエンジニアは数週間に渡って追加の基板とビデオケーブルをiPhoneに追加した。しかし他に問題が多数存在していることを考えると、スクリーンへの投影をうまく行う努力をする価値があるかどうかは疑問だった。 この日のデモを見て、Jobsが実際に使っているiPhoneの画面をスクリーンに投影したときは正直驚きました。仮にiPhoneが現在のように完成度が高いものであっても、そうした「余分な回路+機能」をつけてデモをすることは度胸のいることです。ましてやiPhone自身がバグだらけだった当時において「なんでこんな事をしなくちゃいけないんだ。カメラで操作画面写せばいいじゃないか」とエンジニアは呪ったのではないか、、と想像します。私がその立場なら間違いなくそう言って荒れます。(そしてクビになります) And audience members had to be prevented from getting on the frequency being used. “Even if the base station’s ID was hidden” — that is, not showing up when laptops scanned for Wi-Fi signals — “you had 5,000 nerds in the audience,” Grignon says. “They would have figured out how to hack into the signal.” The solution, he says, was to tweak the AirPort software so that it seemed to be operating in Japan instead of the United States. Japanese Wi-Fi uses some frequencies that are not permitted in the U.S. 観客が無線LANの周波数を使うのを防がなくてはならない。「無線LANのIDは隠されているが-PCからただWifiを検索しただけでは表示されない-観客の中には5000人のコンピュータオタクがいる。無線LANがハックされない、という保証はない」そのため、無線LANの周波数を米国ではなく、日本で使われているものにした。日本向けの周波数は米国では使用が禁じられている。 確かにデモをしているiPhoneが無線LANを使用していると知った瞬間(あるいはそうでなくても)無線LANを利用しようと「何か」を始める人は観客の中にたくさんいたに違いありません。しかしそこまで気を使うか?また米国内で使用が禁止されている周波数を使うとは問題では無いのか? Appleはそこまで配慮しました。彼らにとってはデモの成功が全てであり「いや、観客にそこまでする人がいるとは想定外でした」などという言い訳が通じる状況ではなかったのでしょう。 同じような「細心の注意」は次のパラグラフにも現れます。 Then, with Jobs’s approval, they preprogrammed the phone’s display to always show five bars of signal strength regardless of its true strength. The chances of the radio’s crashing during the few minutes that Jobs would use it to make a call were small, but the chances of its crashing at some point during the 90-minute presentation were high. “If the radio crashed and restarted, as we suspected it might, we didn’t want people in the audience to see that,” Grignon says. “So we just hard-coded it to always show five bars.” ジョブスの許可を得て、実際の電波強度に関係なく電波強度バーが常に5本出るようにプログラムした。ジョブスが電話をする数分の間に無線モジュールがクラッシュする確率は少なかったが、90分のプレゼンの間一度もクラッシュしないとは思えなかった。Grignonはこう語っている「無線モジュールがクラッシュして再起動しても、それを観客に悟られたくはなかった。そのため常に5本バーが表示されるようにプログラムした」 エンジニアであれば一度はこういう「無理やり正常表示」をやった経験があるのではないでしょうか。もちろんクラッシュしないように改善するのがいいのだけど追い詰められるとそうも言っていられなくなる。しかしこうした「捏造」の行き着く先は、「全部嘘の全編ムービー再生」であり、どこかで歯止めをかける必要があります。 さて、Demo専用のビデオ出力、表示プログラムまで装備したiPhoneのデモが始まります。Jobsはメールを送り、メッセージを送り、いくつかのwebサイトをSafariで開いてみせる。Iveにその場で電話をかけたり、スターバックスに「持ち帰りでラテを4000。おっと番号違いだ」と冗談で電話をしたりします。 この日のJobsは自信に満ちており、自分が素晴らしい製品を発表できる喜びに満ちているように見えました。しかし彼が手順を一つ間違えただけで、iphoneはクラッシュし、未完成な製品であることが世界中に「披露」されていたはずです。 そうした「裏の事情」を知っていたエンジニア達は、このプレゼンテーションをどのように見守っていたのか。 By the end, Grignon wasn’t just relieved; he was drunk. He’d brought a flask of Scotch to calm his nerves. “And so there we were in the fifth row or something — engineers, managers, all of us — doing shots of Scotch after every segment of the demo. There were about five or six of us, and after each piece of the demo, the person who was responsible for that portion did a shot. When the finale came — and it worked along with everything before it, we all just drained the flask. It was the best demo any of us had ever seen. And the rest of the day turned out to be just a [expletive] for the entire iPhone team. We just spent the entire rest of the day drinking in the city. It was just a mess, but it was great.” 最後にはGignonはただホッとしただけではく、酔っ払っていた。彼は神経を鎮めるためスコッチを持ち込んでいた「関わったエンジニア、マネージャーはみんな5列目あたりにいた。デモの部分部分が終わる度にスコッチを飲んでいた。5−6人いたかな?自分が担当している部分のデモが終わる度にスコッチを一気飲み。デモが終わる頃ボトルも空になっていた。あれは今まで観た中で最高のデモだった。その日はiPhoneチームにとって最高だった。街にでて飲みまくった。めちゃくちゃだったが、素晴らしい日だった。」 - 最近行われたApple-サムソンの裁判で、フィル・シラーはこう言ったと伝えられています。 "There were huge risks [with the first iPhone]," he said. "We had a saying inside the company that it was a 'bet the company' product.. 引用元: Apple&#39;s Schiller: iPhone was a &#39;bet the company&#39; product | Apple - CNET News 最初のiPhoneは大きな賭けだった。社内では「これは会社自体を賭ける製品だ」と言っていた。 素晴らしくはあるが、まだ未完成の製品を使って、あのプレゼンテーションをしたSteve Jobsはやはり並みの人間ではなかった。私なら処理待ちを示すインジケーター(あのくるくる回る奴です)が一回回るごとに寿命が縮んでいたことでしょう。少し表示が遅ければ、その瞬間動揺を隠せなかったでしょう。 プレゼンの後半、Jobsが「発売は6月」と言ったところで失望の声が上がりました。Jobsはその声に対して「6月に発売する製品をなぜ今日発表するか?これからFCCの認可を得なくてはならないが、FCCから情報が漏れるより自分たちで発表したかったからだ」 それを聞いた私は「6月まで待たなくちゃならないのか。まったく時間のかかる手続きには困ったものだ」と思いました。しかしエンジニア達の本当の戦いはおそらくここから始まったのではないか。この時のiPhoneの完成度がどの程度のものであったのか、当時書かれた別の記事を引用します。 MacworldのAppleブースに展示された2台のiPhoneは、ケースを取り囲む来場者の視線にさらされながら、のんきにぐるぐる回っている。そう、特別な人でなければ触ることはできない。 ちなみに2台しかないにもかかわらず10日昼過ぎ(現地時間)には1台がハングアップして画面が暗くなってしまっている。 引用元: Macworld Conference &#xFF06; Expo2007&#xFF1A;&#x30B7;&#x30E7;&#x30A6;&#x30B1;&#x30FC;&#x30B9;&#x306B;&#x5B88;&#x3089;&#x308C;&#x305F;&#x300C;iPhone&#x300D;&#x3001;&#x5B9F;&#x969B;&#x306E;&#x4F7F;&#x3044;&#x5FC3;&#x5730;&#x306F;&#xFF1F; - ITmedia PC USER 手順を固定したり、いざとなればニセの表示を行うことでデモは(ヒヤヒヤながら)乗り切ることができる。しかし製品はそうした言い訳をまったく考慮しない人たちの手に渡るのです。たった6ヶ月でそこまで製品の完成度を上げ、量産しなくてはならない。 「それからの6ヶ月の物語」が語られることがあるのかないのかわかりません。今はただその苦闘を想像していましょう。それとともに苦闘が素晴らしい製品に結実したエンジニアたちの幸運(苦闘と製品の質の間には不幸なことに常に相関関係があるわけではありません)をうらやましく思いながら。
ネクストでエンジニアをやっています瀧川です。 今回は最近先輩と共同でやった小さめのWEBアプリを作った時に表題の環境を整えるところを担当したのでその紹介をしたいと思います。 同様の記事はネット上にいくつかあったのですが、僕自身これを行うときの段階では node.jsで開発するのも初めてで MongoDBを扱ったこともなく ElasticSearchにいたっては名前を聞いたこともない という無能っぷりだったので色々苦労しました。 そのため、備忘録ということで書かせていただいております。 ただ、本題からずれすぎるのもアレなのでここでは表題のものそれぞれについて細かく説明しません。 あくまでこれらの連携を実現させる方法についてのみ書きます。 一応、それぞれを超カンタンに説明すると node.js … サーバーサイドJavaScript MongoDB … ドキュメント指向データベース Open Source Distributed Real Time Search &amp; Analytics | Elasticsearch … 全文検索エンジン といったところです。 リンク先は各公式HP。 それでは、本題に入りたいと思います。 ElasticSearchとMongoDBを連携させる いまさらですが、ElasticSearchは全文検索エンジンです。 なので何かを検索します。 今回、その「何か」とはMongoDBのもつスキーマなので、MongoDBのもつデータをElasticSearchのクラスタと連携する必要があるわけです。 まずはその設定を行います。 ElasticSearchのプラグインを入れる ElasticSearchのインストールが完了しているなら、plugin というコマンドが使えるようになっているはずです。それを使って $ plugin -install elasticsearch/elasticsearch-mapper-attachments/ 1 . 8 . 0 $ plugin -i com.github.richardwilly98.elasticsearch/elasticsearch-river-mongodb/ 1 . 7 . 0 と実行してください。 もしこのときすでにElasticSearchが起動している場合には、プラグインを認識するために再起動させてください。 補足すると、ElasticSearchにはRiverというクラスタにデータを流し込むサービスがあって、 それのmongoDB版のプラグインが river-mongodb になります。 これを使うことでMongoDBのデータをElasticSearchに流し込むことができます。 mapper-attachmentsのほうは恥ずかしながらなぜ必要なのか正確にはよくわかっていませんが、 きっと型マッピングを扱うために必要なんだと思っております。 なにはともあれ、これでElasticSearch側はmongoDBのデータを受け入れる準備ができました。 簡単! 次はMongoDB側です。 MongoDBのReplicaSetを用意する river-mongodbプラグインはなにやらMongoDBのインスタンスがReplicaSet(レプリケーション)として起動していないとうまく動かないらしいので、 ReplicaSetの設定をし、それを起動してやる必要があります。 まずは ReplicaSet用のディレクトリ(データ保存/ログ保存)を作成します。 もちろん名前などは適宜おこのみでつけてください。 $ mkdir /data/mongo/rs0 $ mkdir /data/mongo/log 作成したディレクトリを使用し、ノードを起動します。 $ mongod --replSet testrep --port 27017 --dbpath /data/mongo/rs0 --logpath /data/mongo/log/rs0.log &amp; ちゃんと起動していることを確認するため、立ち上げたポートにブラウザでアクセスしてみます。 ローカルで作業しているならば http://localhost:27017/ へアクセス。 アクセス先で You are trying to access MongoDB on the native driver port. For http diagnostic access, add 1000 to the port number と表示されていれば成功です。 このメッセージの指示どおり+1000番のポート(今回の例だと http://localhost:28017/ )へアクセスすると管理画面となります。 ここまででレプリケーションの下準備は完了しました。 レプリケーション本来の目的として考えるとノードが1つだけって意味があるのか不明ですが、 今回はElasticSearchとの連携だけが目的なのでこのまま進みます。 これでReplicaSetを動かすための下準備はできました。 あとはコンソールから設定と初期化を行います。 まずはmongoコマンドを実行。ポート番号はもちろん先ほど起動したレプリケーションのポートです。 $ mongo -port = 27017 実行したらrs.initiateというコマンドで初期化します。 &gt; config = { _id: 'testrep' , members: [{ _id: 0, host: 'localhost:27017' }]} ; &gt; rs.initiate( config ); _idキーは先ほど用意したreplSetの名前 members._idはおこのみで members.hostは先ほどブラウザでアクセスしたURL という感じに読み替えてください。 最後に &gt; rs. status (); を実行し、特にエラーが出なければ("ok"の値が1であれば)設定完了です。 これでmongoDB側も準備が完了しました。 最後にElasticSearchのCollection, Indexとの関連付けを行います。 ElasticSearchのCollection, Indexとの関連付け ElasticSearchの設定はcurlコマンドを使います。 僕はここでよくタイポを起こしてウオオオオオー!となることが多かったのでシェルスクリプトで書いて実行していくのがおすすめです。 ということで下記のようなシェルスクリプトを実行します。 (ElasticSearhは9200番ポートで立ち上がっている前提) #! /bin/sh curl -XPUT &quot; localhost:9200/_river/test_es/_meta &quot; -d ' { &quot;type&quot;: &quot;mongodb&quot;, &quot;mongodb&quot;: { &quot;db&quot;: &quot;test_es&quot;, &quot;collection&quot;: &quot;collectionName&quot;, &quot;servers&quot;: [ { &quot;host&quot;: &quot;127.0.0.1&quot;, &quot;port&quot;: 27017 } ], &quot;options&quot;: { &quot;secondary_read_preference&quot;: true } }, &quot;index&quot;: { &quot;name&quot;: &quot;indexName&quot;, &quot;type&quot;: &quot;typeName&quot; } } ' curlコマンドで指定しているURLの"test_es"がIndex名になります。 mongodb.db, mongodb.collection, mongodb.indexの設定は各々実際に使用する際に適したものをつけてください。 実行後、 http://localhost:9200/ river/test_es/ meta にアクセスしたときに { &quot;_index&quot; : &quot;_river&quot; , &quot;_type&quot; : &quot;test_es&quot; , &quot;_id&quot; : &quot;_meta&quot; , &quot;_version&quot; :1, &quot;exists&quot; : true , &quot;_source&quot; : { &quot;type&quot; : &quot;mongodb&quot; , &quot;mongodb&quot; : { &quot;db&quot; : &quot;testEs&quot; , &quot;collection&quot; : &quot;collectionNames&quot; , &quot;servers&quot; : [ { &quot;host&quot; : &quot;127.0.0.1&quot; , &quot;port&quot; : 27017 } ] , &quot;options&quot; : { &quot;secondary_read_preference&quot; : true } } , &quot;index&quot; : { &quot;name&quot; : &quot;indexName&quot; , &quot;type&quot; : &quot;typeName&quot; } }} と表示されていればOKです。 これでようやく連携部分は完了です。 最後にnode.jsから操作してみましょう。 Node.jsからの操作 まずは必要なモジュールをインストールしましょう。 $ npm install mongoose $ nom install es 名称からお察しの通り、mongooseがMongoDB、esがElasticSearch用のモジュールとなります。 まずは適当にデータを登録。(直接DBから入れろという話は置いといて) var mongoose = require( 'mongoose' ) , Schema , Model , db ; // MongoDBへアクセス db = mongoose.connect( 'mongodb://localhost:27017/testEs' ) // Schemaの設定 Schema = new mongoose.Schema( { name: { type: String , trim: true } , type: { type: String , trim: true } } ); // コレクションを生成 Model = db.model( 'collectionName' , Schema); // 適当にデータを入れる var neko = new Model( { name: &quot;mike&quot; , type: &quot;siamese&quot; } ); // 保存 neko.save( function (err) { console.log(err); } ); これでエラーがでなければ、nekoをMongoDBに登録できています。 さて、いよいよこの情報をElasticSearchから取得します。 // 関連付けたindex, typeを設定 var elasticsearch = require( 'es' ) , config = { _index: 'indexName' , _type: 'typeName' } , es = elasticsearch(config) ; // 全文検索開始 es.search( { query: { query_string : { query : &quot;mike&quot; } } } , function (err, data) { console.log(data); console.log(err); } ); これを実行すると、下記のような結果が帰ってくるかと思います。 //出力結果 { took: 2, timed_out: false , _shards: { total: 5, successful: 5, failed: 0 } , hits: { total: 1, max_score: 0.23953635, hits: [ [ Object ] ] } } これが確認できれば完了です。 data.hitsオブジェクトがもつ、hitsという配列(ややこしい)の中にデータが入っています。 お疲れ様でした。 // ちなみに // もちろんtypeでも取得可能 es.search( { query: { query_string : { query : &quot;siamese&quot; } } } , function (err, data) { console.log(data); console.log(err); } ); //出力結果 { took: 2, timed_out: false , _shards: { total: 5, successful: 5, failed: 0 } , hits: { total: 1, max_score: 0.23953635, hits: [ [ Object ] ] } } こんなかんじで、なんとか表題の連携をさせることができました。 探り探りいろんなことを試しながらこの設定をしたのでどこか抜けなどありましたらぜひご一報を。 この環境が整え終わると、すべてがJSで完結してる"風"な気持ちになれて嬉しいですね。
大坪と申します。Simple Exampleシリーズと称して「ググれば見つかるけど、ちょっとわかりづらい」iOS上でのテクニックについて ・実行できるサンプルプロジェクト付(動かないとわかんない) ・肝心な部分を動かすのに必要最低限のコードを書く。余計なものは一切つけない(ごてごて余計なものがあるとどこが肝かわからない) をモットーにサンプルコードを解説&公開していきます。第一弾は「UICollectionViewのframeをアニメーション付きで変更する」方法について。 UICollectionViewというのは私見では実に偉大な画面部品で、あちこちで使いたくなります。その際「何かのアクションでUICollectionViewのframeを変更する」ということがあると思います。 というわけで、 github&#x306B;&#x304A;&#x3044;&#x305F; のサンプルコードを実行してみてください。こんな画面が表示されるはずです。 左端にUICollectionViewが表示されています。画面右側一番上の"Frame Change"を押してみてください。いきなりUICollectionViewの外枠とセルが変化します。 プログラム的には UICollectionView.frame = newFrame; (ViewController.mのsimpleChange:(UIButton*) buttonの処理 としただけです。 これで用が足りる場合もあるでしょうが、iOSでプログラムを書いていると「アニメーションで変化させたい」と思うことでしょう。実際UIView animationWithDuration:は偉大で、ブロックの中に変化させる処理を書けば、たいていのことはアニメーションで変化させてくれます。ではさっそくframeの変更処理をUIView animationWithDuration:の中にいれてやりましょう。 画面のFrame Change + UIView animationを押すとこの処理が実行されます。 確かにアニメーションするのですが、なんとなく動きが期待と違うと思います。とくにUICollectionViewの幅が狭くなる時には、セルが縦一列にいきなり移動し、そのあとアニメーション付きでUICollectionViewのframeが変化する。これはあまり美しくない。 というわけで、一番下のCombo Changeを押してみてください。frameの変形だけではなく、セルの移動にもアニメーションがついたのがわかると思います。 これをどうやって実現しているかというと、ViewController.mのcomboChange:(UIButton*) buttonをみてください。 ほとんどの処理は先ほどのanimChangeと同じですが、その外側に_collectionViewController.collectionView performBatchupdates:がついています。これがつくとセルまでちゃんとアニメーションしてくれる、というわけでした。 みんな大好きStackoverflowにもいくつか回答があるのですが、そこにたどり着くまでかなり苦労したので、ご参考になれば。
はじめまして、長沢です。 弊社では最近一部サービスでAWS および Ruby を利用しているのですが、 今回はAWSの AWS SDK for Ruby を利用して クロスアカウントアクセス をやってみようと思います。 簡単に クロスアカウントアクセスについての説明 クロスアカウントアクセスとは、一言で言うと、異なるAWSアカウントをまたいだリソースの操作を行う事ができる機能です。 クロスアカウントアクセスの方法は IAM Role の AssumeRole API を利用します。 IAM Roleを利用したクロスアカウント API アクセスの方法の詳しい説明は &#x3053;&#x3061;&#x3089; この記事たちを読めば大体は分かります。 個人的に思った注意しなくてはいけないところ (個人的にはまったところ) は AMIユーザーでなければクロスアカウントアクセスは出来ない 認証先のIAM Role の Trust Relationships は適切に設定して権限を管理する と言ったところでしょうか。 の部分に関しては最初は気づかずに Role で AssumeRole をしようとしていて、下記のエラーメッセージが出ていました。 &#34;Roles may not be assumed by federated users&#34; 事前準備(Webコンソール上でクロスアカウントアクセスの設定) 最初にWebコンソールを利用してアカウントの下記のAWSの公式ブログにあるような設定をしましょう。(ここでの説明は割愛致します) Delegating API Access to AWS Services Using IAM Roles | AWS Blog また、こちらの &#x30B5;&#x30FC;&#x30D0;&#x30FC;&#x30EF;&#x30FC;&#x30AF;&#x30B9;&#x69D8;&#x306E;&#x30D6;&#x30ED;&#x30B0; も大変分かりやすいです。 早速やってみる 上記の事前準備の設定が済んだら、 2つのAWSアカウントA、Bがあって それぞれ S3バゲット &ldquo;account-a-bucket&rdquo;(アカウントA) 、 &ldquo;account-b-bucket&rdquo;(アカウントB) が作成されていたときに、 クロスアカウントアクセスの仕組みを利用して、 アカウントAの IAM User &ldquo;homes&rdquo; が、アカウントBのS3に対してRead Only 権限をもつ IAM Role &ldquo;s3-viewer&rdquo; としてふるまい、アカウントBのS3バゲット名を取得する といったようなことを実現するためのコードを書いてみます。 コードの流れを簡単に説明すると、 IAMユーザーのアクセスキー、シークレットキーを利用してSTSにアクセス sts.assume_roleのAPIを利用して Credentialを取得 取得したCredentialを利用してアカウントBにアクセス といった感じです。 ※ 利用したRubyは 1.9.3-p327 です。 ※ キーがシンボルのHashの記法が 1.8.x と少々異なりますのでご注意ください。 cross_account_access.rb require ' aws-sdk ' # 設定 AWS .config({ region : ' ap-northeast-1 ' , access_key_id : ' アカウントAのhomesユーザーのアクセスキー ' , secret_access_key : ' homesユーザーのシークレットキー ' , }) # AssumeRole対象のIAM Role のARN assume_role_arn = ' arn:aws:iam::{アカウントBのID}:role/s3-viewer ' # session名は任意の文字列 assume_role_session_name = ' homess3viewersession ' # S3オブジェクトの取得(アカウントA) s3_a = AWS :: S3 .new puts ' ===== Account A Buckets ===== ' # バゲット名を出力 s3_a.buckets.each do | bucket | puts bucket.name end puts # STSオブジェクトの取得 sts = AWS :: STS .new # AssumeRoleを呼び出し一時的な認証情報を受け取る assume_info = sts.assume_role( role_arn : assume_role_arn, role_session_name : assume_role_session_name, ) # 認証情報の上書き AWS .config({ access_key_id : assume_info[ :credentials ][ :access_key_id ], secret_access_key : assume_info[ :credentials ][ :secret_access_key ], session_token : assume_info[ :credentials ][ :session_token ], }) # S3オブジェクトの取得(アカウントB) s3_b = AWS :: S3 .new puts ' ===== Account B Buckets ===== ' # バゲット名を出力 s3_b.buckets.each do | bucket | puts bucket.name end puts 実行してみます $ ruby cross_account_access.rb ===== Account A Buckets ===== account-a-bucket ===== Account B Buckets ===== account-b-bucket うまくできているようです! 念のため、アカウントA,B それぞれの IAM Policy や Trust Relationship の設定を以下に記載いたします。 アカウント A の設定 IAM User &ldquo;homes&rdquo; の Policy (STSのAssumeRoleとS3の閲覧に権限をつけている) { &#34;Statement&#34;: [ { &#34;Effect&#34;: &#34;Allow&#34;, &#34;Action&#34;: &#34;sts:AssumeRole&#34;, &#34;Resource&#34;: &#34;*&#34; }, { &#34;Effect&#34;: &#34;Allow&#34;, &#34;Action&#34;: [ &#34;s3:Get*&#34;, &#34;s3:List*&#34; ], &#34;Resource&#34;: &#34;*&#34; } ] } ちなみに、STSで AssumeRole しようとして、 もしそのRoleに対して権限がないときは下記のようなエラーが出ます。 A client error (AccessDenied) occurred: User: arn:aws:iam::{AのアカウントID}:user/homes is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::{BのアカウントID}:role/s3-viewer アカウント B の設定 IAM Role &ldquo;s3-viewer&rdquo; の Policy(S3の閲覧に権限をつけている) { &quot; Version &quot;: &quot; 2012-10-17 &quot;, &quot; Statement &quot;: [ { &quot; Effect &quot;: &quot; Allow &quot;, &quot; Action &quot;: [ &quot; s3:Get* &quot;, &quot; s3:List* &quot; ] , &quot; Resource &quot;: &quot; * &quot; } ] } Trust Relationship { &#34;Version&#34;: &#34;2008-10-17&#34;, &#34;Statement&#34;: [ { &#34;Sid&#34;: &#34;&#34;, &#34;Effect&#34;: &#34;Allow&#34;, &#34;Principal&#34;: { &#34;AWS&#34;: [ &#34;arn:aws:iam::{AのアカウントID(12桁)}:user/homes&#34; ] }, &#34;Action&#34;: &#34;sts:AssumeRole&#34; } ] } もし、上記のPrincipalの箇所が &#34;Principal&#34;: { &#34;AWS&#34;: [ &#34;arn:aws:iam::{AのアカウントID(12桁)}:root&#34; ] } としてしまうと、 そのアカウントの全ユーザーがデフォルトでアクセスできることになってしまうので注意が必要です。 株式会社ネクストでは、一緒に世の中をもっと便利に楽しくしたい仲間を募集中です。 主体的に楽しみながら仕事できる方からのエントリーお待ちしています! http://recruit.next-group.jp/
ガジェット大好き、カメラ大好きな非エンジニアです。 360度全天球カメラRICOH THETAを買ってみたので、品川オフィスと合わせてちょっとだけご紹介です。 THETAはスティク型のカメラです。両面に魚眼レンズが付いていて、1回のシャッターで空間をまるっと撮影できます。 手にフィットする形状で、95グラムという軽量さ。荷物が多いガジェット持ちにはうれしい配慮です。 撮影は中央のシャッターボタンを押すだけ。起動が早いので、シャッターチャンスは逃しません。これまたうれしい! iPhone/Androidアプリを使用すればリモート撮影することも可能です。 ということで、本日は品川オフィスのリモート撮影に挑戦したいと思います。 こんな感じTHETAを設置します。 そして、アプリのボタンをぽちっと押すと本体から「キュイン」と可愛い音が鳴ります。 撮影はこれで完了! 撮影した写真はアプリ上で楽しむことも出来ますが、専用のビューワーで共有することもできます。 専用のビューワーは、こんな風に自分のWebサイトに埋め込むことができます。 品川オフィス受付 - Spherical Image - RICOH THETA 専用のビューワーを使用せずに見ると湾曲したJPG画像ですが、ヘッドマウントディスプレイ「Oculus Rift」、3D空間を再現する「Photosynth」などでも楽しむことができます。 撮影画像には位置情報もつけられるので、ライフログなどを付けるのにも良さそうです。 品川フロントビル - Spherical Image - RICOH THETA 一眼の魚眼レンズで撮影して、切って、貼ってみたいな作業不要です。 解像度やISO感度などは一眼レフには遠く及びませんが、 なにせ手軽!楽しい!撮影したときには気づかなかった、自分が見えていない場所も映っていたりといろいろなシーンで活躍しそう。 ただ、残念な点がいくつかあります。 ファームウェアのアップデートがあることを期待しつつも、 セルフタイマーがなーーーいッ! 撮影用の専用リモコンが欲しいッ!!! などなど・・・。 あぁうちのエンジニアなにか開発してくれないかなぁ |д゚)チラッ とつぶやきつつ、本日も「キュイン」と撮影! RICOH THETA: https://theta360.com/ja/
さて、前回にReadとCreateをしたので、次はDelete、Updateをしましょう! ドキュメント更新 前回同様、Updateは、NSDictionaryを操作します。 簡単にいえば、ドキュメント内容をNSDictionaryで受け取って、中身を入れ替えてまたセットし直すという感じです。 まず、この時点で、UITableViewを配置している前提としています。 流れは ドキュメントをMutableDictionaryで受け取る Bool値を入れ替える CBLDocumentに値をputする という感じになります。 Couchbase LiteをCBLDocumentベースで更新 見て分かる通り、ほぼCreateと変わりません。 いうなれば、CBLDocumentを作成する時が、untitledなのか、documentWithIDなのかの差です。 ドキュメント削除 削除は簡単。 流れは 削除する対象のCBLDocumentを作成 CBLDocumentをdeleteする Couchbase LiteをCBLDodumentで削除 こちらもほとんど他のとやり方は変わりません。 まとめ CUDの流れは CBLDocumentを作成 putなりdeleteなりする Rは CBLQuery作成してDocumentを取得 イテレータでArrayに込めなおす って感じですね。 ここまでNSDictionaryで作成したりなんだりしましたが、次回はモデルを使った操作も見てみましょう。 株式会社ネクストでは、一緒に世の中をもっと便利に楽しくしたい仲間を募集中です。 主体的に楽しみながら仕事できる方からのエントリーお待ちしています! http://recruit.next-group.jp/