検索キーワード「���������������」に一致する投稿を関連性の高い順に表示しています。 日付順 すべての投稿を表示
検索キーワード「���������������」に一致する投稿を関連性の高い順に表示しています。 日付順 すべての投稿を表示

2011年2月2日水曜日

"Failed to launch simulated application: Unknown error." が発生したときの対処法



iPhone シミュレーター起動時に表題のエラーが発生したときの対処法です。


表題のエラーは文字通り原因不明の問題が発生したときに表示されるのですが、問題のうちの一つに「バンドルリソース内にMac OSが使用する予約されたリソース名が含まれていると発生する」というものがあります。

たとえば
Contents
Resources
などの名前のディレクトリやファイルをバンドルリソース内に作成してしまうと、ビルドは通るのですがシミュレーター実行時にエラーが発生するようです。これらの予約された名前は使わないようにすると良いです。

2009年12月1日火曜日

Heroes of Newerth の統計情報を集めるサイトをつくってみた



ということで、作ってみました。

■そもそもHeroes of Newerthって何?
http://wikiwiki.jp/hon/
βキーが一つ余ってますので先着一名で差し上げます(コメントなりTwitter Dなりください><)


■何これ?
http://www.heroesofnewerth.com/heroes.phpあたりからデータを抜いてきて蓄積し、現在どのヒーローが一番使われているか、どのヒーローが一番勝率がいいか、等を一覧表示しています。

デフォルトのソート順序はUsage %(使用率)です。テーブルのヘッダをクリックするとソート順序を変更することができます。1回クリックで昇順、2回クリックで降順です(たぶん)。

データは1日1回アメリカ西海岸時間の朝9時半ぐらいに更新するようにしています。日本だと深夜1時ぐらいかな。朝起きたらちょうど良く変わっていると思います。

あ、あとspan機能はいま動きません。あのセレクトボックスを選択しても何も起きません。あしからず><


■で、どうするの?
Opheliaたんが余りにも使われていないのを見て嘆き悲しむといいと思います。


■今後
時系列での変化を見られるようにしたいですね。パッチが当たってScout使用率減って涙目とか、Maliken先生勝率アップとか見てみたいです。

2009年9月25日金曜日

スティーブ・ジョブズが目の前なう






2009/09/25: 帰宅したらきちんとした写真アップします。
2009/09/28: 写真アップしました。

■これまでのあらすじ
アメリカ旅行1日目
アメリカ旅行2日目〜3日目


■まずはGoogleへ
今日はCaltrainに乗ってシリコンバレーへ向かいます。お目当てはGoogleとAppleの本社!

Caltrainのサンフランシスコ駅へ到着。日本の駅と作りが全然違ったり、標準軌道(広軌?)だったり、全2階建ての客車をディーゼルの機関車が引っ張る構成だったり、そんな機関車がずらりと横一列に並んでいる姿が拝めたりと、鉄道マニアにはたまりませんよ。あ、私は鉄道マニアなんかじゃないんだからねっ?><

一路San Antonio駅へ。ここからVTAのバス40番に乗り換えて一路Googleへ移動。
駅で降りてまず感じたのが、空が広い!明るい!緑が鮮やかで綺麗!暖かい!家並みが美しい!まるで絵に描いた楽園のような風景です。なるほど、これはすごい。みんなシリコンバレーで起業したがるわけです。

Google到着。めっちゃめっちゃ広い!!><
雰囲気としては途中Palo Alto駅すぐそばにあるスタンフォード大学のキャンパスとそっくりで、全然「会社」って感じがしません。緑がたくさんあって、へんちくりんな形の建物がどーんと。大学のキャンパスかカフェか何かみたいです。

さんざん周りをうろついて写真を撮りまくったあげく、何かの間違いで中に入れてもらえないかとVisitor用のロビーに向かったら、受付のおねーさんに「ここはClosed Campusです、知り合いが面会者がいない奴は立ち入り禁止です。でてけ」とやんわり怒られ、外に出たらセキュリティのお兄さんにまたやんわり怒られ、あえなく撤退。入れないのはわかっていたもののやはりショック。

"There are no public visitor center or a shop"だそうです。嘘つき!俺は知ってるよ!某氏がGoogle社内見学したとき、お土産にバッグ買って帰ってきたのを!

あ、そうか、だから"public"って断りを入れたのね。というわけで、技術にはオープンですが、本社警備には大変クローズドなGoogleでした><


■続いてAppleへ
ふたたびCaltrainに乗って、こんどはSunnyvale駅へ。

駅からVTAバス55番で南下するとAppleの本社に到着。外見はGoogleと違ってテクノロジー企業らしい企業ビルって感じです。あたりの交差点にたくさんリンゴマークの住所書きがあります。

先ほどのGoogleでの失態を教訓に、まずはすぐに施設に近づかずInfinity Loopの公道からゆっくり観察することに。あ、6 Infinity Loopに山積みのMac Proがある。一つお土産に欲しい。

Infinity Loopを左回りに進むと、6, 5, 4, 3, ....という風に施設が並んでいます。一番最後が1 Infinity Loop, 本社ビルです。

本社ビル前に到着。お土産屋さん発見。観光バスと観光客らしき人も発見。どうやら思ったより観光客に対してオープンになっているみたいです。

しかし当たり前ながら本社ビルの中には入れないようで、入り口の前で立ち往生。


■あんびりーばぶる!
で、中にも入れないし写真も撮ったしそろそろ帰るかと思っていたら。

http://twitter.com/akisutesama/status/4354424273
http://twitter.com/akisutesama/status/4354453168
http://twitter.com/akisutesama/status/4354498099

まさかのジョブズ降臨!!

目の前3メートルの位置にジョブズが!声をかけたけど無視されてすたすた行ってしまわれた。

いや待て、本物がこんなド真ん前を堂々と歩くわけがない、偽物か影武者だと思って売店のおねーさんに聞いてみたら、"then it might have been"(ならたぶん本物だったのよ)とのお返事が。あと"He usually doesn't answer because he's so busy"らしいので、どうやら本物っぽいです。ああ、来て良かった!

最後に記念撮影してたら建物の中が写るからそこで撮るなとセキュリティのイケメン兄ちゃんにやんわり怒られました。またか!><


■じゃあ帰りましょ
Sunnyvaleの駅前で食ったsushiはなかなかおいしかった。カリフォルニアロール、言うほど不味くないですよ。私の舌がゲテモノ食いなのかもしれませんけど。ただし握りはいまいち、しゃりがなんだか変。食えないことはないけど、近所のスーパーのパック寿司のほうがおいしいと思う。これで4ドルは日本だとぼったくり。

帰りのPalo Alto駅で乗ってきたスタンフォードの学生が目の前でMacBook広げてプログラミングを始めたので興味本位でのぞいてみた。ソースはよく見られなかったけど、たぶんAdobe AirかFlash。集中力が凄くって、乗ってからサンフランシスコの駅についてドアが開いてもまだMacとにらめっこしていた。きっと朝から夜遅く(19時)まで授業があっただろうにと思うと、さすがスタンフォードの学生だと感心するとともに、自分も負けていられないと思うのでした。

2012年5月4日金曜日

Unity の GUIStyle でデフォルトで指定できるスタイル一覧

今回は完全に自分用メモになってしまいますがご了承ください><

UnityのGUIStyleはname文字列を指定して生成する事が可能ですが、Unityがデフォルトで持っているGUIStyleの一覧を見つけたので列挙しておきます。
  • box
  • button
  • toggle
  • label
  • textField
  • textArea
  • window
  • horizontalSlider
  • horizontalSliderThumb
  • verticalSlider
  • verticalSliderThumb
  • horizontalScrollbar
  • horizontalScrollbarThumb
  • horizontalScrollbarLeftButton
  • horizontalScrollbarRightButton
  • verticalScrollbar
  • verticalScrollbarThumb
  • verticalScrollbarUpButton
  • verticalScrollbarDownButton
  • scrollView
大文字小文字は関係ないみたいです。たとえばboxのところをBoxと指定しても問題なくそのまま通ります。

参考URL: http://answers.unity3d.com/questions/9844/copypaste-guistyle-in-the-inspector.html
参考URL: http://unity3d.com/support/documentation/Components/gui-Customization

2014年8月25日月曜日

Silent Push / Background Fetch 時の fetchCompletionHandler に渡す引数ごとの挙動の違いを調べてみた

2014/09/10追記: 追加調査によって判明した事項を追記しています。


iOS 7から追加されたPush通知によるバックグラウンド処理機能 (Silent Push) および定期的なバックグラウンドフェッチ機能 (Background Fetch) ですが、これらの機能はバックグラウンド処理が完了したタイミングでいずれもfetchCompletionHandlerにUIBackgroundFetchResult型の値を渡すような設計になっています。

ちょっと調べれば、大体どこの解説サイトにも以下のように説明があります。


  • UIBackgroundFetchResultNewData
    • 新しいコンテンツの取得に成功し、新しいデータが存在した場合
  • UIBackgroundFetchResultNoData
    • 新しいコンテンツの取得には成功したが、新しいデータが用意されていなかった場合
  • UIBackgroundFetchResultFailed
    • 通信エラーなどの理由でコンテンツの取得そのものに失敗した場合


問題は、これらの値をfetchCompletionHandlerに渡したら「何が起きるか」を解説しているサイトや文献がほとんど存在しません。そこでこのたび独自に調査してみました。

UIBackgroundFetchResultの値が影響する項目

いろいろ調べてみた結果、以下の参考文献によりUIBackgroundFetchResultの値は2つの項目に影響することがわかりました。
http://www.objc.io/issue-5/multitasking.html
https://codeiq.jp/magazine/2013/12/3022/

ひとつは、以降のSilent PushおよびBackground Fetchの挙動に対する影響です。OSが返された値を元に、アプリが使用したプロセス時間・電力消費などを判断し、以降のSilent PushやBackground Fetchの頻度に反映させるというものです。ただしどのような判断が行われどのように頻度に対して影響するかは完全にブラックボックスとなっており文献がありません。

もう一つはApp Switcher(ホームボタンをダブルタップした際に表示されるアプリ切り替え画面)のサムネイル画像、スナップショットを更新するために使用しているようです。値が渡されたタイミングで必要に応じてアプリのスナップショットを再描画するために、なんとアプリがバックグラウンドにいるにも関わらずその場でUIの再描画が行われます。この際通常のフォアグラウンドにアプリが存在する場合とまったく同様に、UIViewやUIViewControllerのメソッドが呼ばれます。例えばviewWillAppear, viewDidAppear,  didMoveToSuperViewなどが呼び出されます。

従いましてUIBackgroundFetchResultを渡す際には必ずメインスレッドで行う必要があります。NSURLSessionのdelegateを受け取ったスレッドから直接値を渡すとバックグラウンドでひっそりクラッシュします。ひっそりクラッシュする程度ならまだしもですが、これらのUI処理の中にバックグラウンドで呼び出されることを考慮していない処理が入っていると数々の問題を引き起こします。例えばviewDidAppearのタイミングでユーザさんがその画面を見たものと判断してデータやフラグを更新するですとか、Google Analyticsに統計情報を送っているですとか、そういうことをやっていると一気に大問題になります。

長くなりましたが、以上を踏まえまして、各値ごとにどのような影響があるかを実際に実機でテストして調べました。

UIBackgroundFetchResultNewDataを渡した時

スナップショットの更新が発生します。
Silent Pushの受取可能頻度に対して影響はないようです。一日4回程度、3〜6時間程度の間隔が空いていれば、全く問題なく受取可能です。ただし10分〜15分間隔で受取を行うとレートリミットがかかりはじめ、1〜2日以内に受信不可能になります。一度受信不能になったデバイスが再度受信可能になるかどうかはわかりませんが、数日間程度置いていても効果がなかったので、再度受信ためには最悪デバイスの完全なワイプとリセットが必要になるかもしれません。
Background Fetchの発生頻度に対しても影響はないようです。

UIBackgroundFetchResultNoDataを渡した時


スナップショットの更新が発生します。
三ヶ月程度様子を見ているところ、Silent Pushの受取り頻度に対してNewDataの場合とほぼ同様に影響はなさそうです。したがってNewDataを返す場合と何が違うのか現在のところわかっていません。

以下、旧記述です。
スナップショットの更新は発生しません。スナップショットの更新に伴って大問題が発生する場合には一番簡単な解決策になります。
ただし毎回毎回UIBackgroundFetchResultNoDataばかりを返却していると、Silent Pushの受取可能頻度およびBackground Fetchの発生頻度に対してペナルティがかかる恐れがあります。何度試行してもデータが取れないので、システムが再試行頻度を下げるのではないかという推測ですが、実際に観測できたわけではありませんので、なにか情報が入り次第また詳しくお伝えします。

UIBackgroundFetchResultFailedを渡した時

スナップショットの更新は発生しません。
こちらもUIBackgroundFetchResultNoDataと同様、UIBackgroundFetchResultFailedばかりを返却していると、Silent Pushの受取可能頻度およびBackground Fetchの発生頻度に対してペナルティがかかる恐れがあります。何度試行してもデータが取れないので、システムが再試行頻度を下げるのではないかという推測ですが、実際に観測できたわけではありませんので、なにか情報が入り次第また詳しくお伝えします。


2021年12月1日水曜日

セキュリティを一切考慮しないMMORPGを開発するとどうなるか

どうもご無沙汰しております。本Blogが私の年1回の生存報告、兼、アドベントカレンダー用と相成って久しいですが、今年も一発恒例行事として筆を取らせていただきたいと思います。

今年、私が話題に取り上げますのは、とあるゲームです。Amazon Game Studiosという会社が開発・リリースしました、New WorldというMMORPGについてご紹介させていただきたいのです。ゲームの話題には一切興味がない読者諸君も、どうか少し我慢して、私に騙されたと思って最後まで話を聞いていただけませんでしょうか。そもそも、あのAmazonが開発したMMORPGというのですから、どれほどゲームに興味がなくても、技術に興味のある方でしたら、少しは興味深く感じられるのではないでしょうか?

けして後悔はさせませんよ。悪い方向にね。


さて、ゲームに何ら興味知識のない方にもわかるように少し解説を入れさせていただきますと、MMORPGというのは「数千人単位の人間がネットワーク上に構築された一つの世界を共有するRPG」です。要するにSAOです・・・いや、この説明だと最近の若いのには通じないですかね・・・まぁそういう感じのアレで、要するに滅茶苦茶多数の人間がサーバーに同時接続するゲームってことです。そして説明するまでもございませんが、Amazonという会社は世界で最も巨大なクラウドサービスの一つを展開している会社であります。我々技術畑の人間がシンプルに連想するのは、これほど相性の良い組み合わせはないに違いない、そうですね?

実際、発売前から私もそう思っていましたし、発売された後も、彼らはAmazonの名に相応しい仕事を見せつけてくれました。発売前から話題の合った本作はあれよあれよという間に最大同時接続90万人を記録するモンスターゲームに成り上がったのです。いいですか、90万人同時接続ですよ、90万人。90万DAUとか、90万PV/dayとか、90万Session/dayだとか、そういう雑魚みたいな指標ではありません、同時接続で90万です。貴方は90万の同時接続とリアルタイムレスポンスを保証できるシステムを構築できますか?言うまでも有りませんが私ならその場で全く不可能だと匙を投げますね。

さすがは開発元がAmazonの名を冠するゲーム会社だけあって、サーバーは相当な負荷に耐えきったと言えます。最大同時接続90万人をリリース直後から支えられるMMORPGというのはなかなか世に存在するものではありません。私も思わず「やっぱりAmazonはすごい」と驚嘆したものです。


ですが、我々は後から思い知ることになります。何故、彼らのサーバーが、同時接続90万という途方も無いアクセスに耐えることが出来たのか、その本当の理由を。

・・・サーバーが何もしていないからです。


dupe, exploit, cheatの軌跡 

先に説明したとおり、MMORPGというのはネットワーク上に構築された一つの世界を共有するゲームです。つまり、プレイヤーは全員常時サーバーにリアルタイムで接続された状態になっていますし、必然、全てのプレイヤーのデータはサーバ上で管理されています。

普通はね。

だって、各自のローカルサイドでプレイヤーのデータが管理されていたら、皆が好き勝手にローカルサイドでセーブデータなりメモリなりを書き換えたら、簡単にお金やアイテムが無限に増殖されてしまうでしょう?それでは皆のゲームが成り立ちませんから困りますね?そういう悪いことをゲームで行う・・・チートをする人間は、ここ数年右肩上がりに増えておりますから、ゲームを作る側も畢竟、高レベルのチート対策をゲームに組み入れるのが当然の事項となっているわけです。

そう、だから、普通は、どんなMMORPGを作るときもね、チート対策をするんですよ。

普通はね。


さて、あれはゲームがリリースされて30日もしたころでしょうか。

コミュニティの中で「アイテムが増殖できる」だとか「Goldが無限に増やせる」だとか、そういう噂が流れ始めました。

最初はただの噂だと思われていたのですが、1000万Goldを超える所持金や、ゲーム内で最も貴重なVoidmetalと呼ばれる金属を数千個単位で所持するスクリーンショットが暴露され始めると、「どうやら本当にアイテム増殖バグがあるらしいぞ」ということがわかってきました。私は当初、高度なハッカーの類がパケットを改ざんして攻撃しているのだと推測していたのですが、一週間もしたころでしょうか、突然公式Forumにアイテム増殖の具体的な方法が書き込まれ、一同が唖然となります。

その方法とは・・・


1. 個人間トレードを開始する。

2. 渡す側は、増殖したいアイテムを相手に渡すようにして、Confirmボタンを押す。

3. 受け取る側は、増殖したいアイテムがトレードに乗ったのを確認したら、意図的に通信を遮断する。

4. 受け取る側は、Confirmボタンを押す。

5. しばらく待ってから、渡す側がCancelボタンを押して、取引を中断する。

6. 中断されたのを確認してから、受け取る側は、通信を復旧する。

7. 双方がアイテムを持った状態になる。

 

そう、トレード中に、通信を止めるだけ。

たったのこれだけでアイテムが増殖できてしまうのです。


まさか21世紀も1/5以上が終わった現代において「サーバーを一切介さないでトレードが成立するMMORPG」をあのAmazonの名を冠するゲーム会社がリリースする日が来ようなどとは一体全体誰が予想し得たでしょうか。


結果、サーバー全土に渡る経済封鎖が行われ、個人間トレード、交易所、ギルドバンクの利用などあらゆる富の移動が不可能にされたのですが、現実世界だけではなくゲーム内でも経済活動の制限が行われる日が来るとは実に皮肉なものです。

しかも、問題はこれだけに留まりませんでした。ゲームをWindowedモードで起動し、戦闘中にゲーム画面のWindowを激しく動かしまくって意図的にクライアントサイドでの処理を止めると、なんと完全無敵になるというバグが発見されたのです。全く理解できません。


まさか21世紀も1/5以上が終わった現代において「クライアントサイドの処理を止めると一切ダメージを受けなくなるMMORPG」をあのAmazonの名を冠するゲーム会社がリリースする日が来ようなどとは一体全体誰が予想し得たでしょうか。


あまりの事態に、とうとう開発者たちも重い腰を上げて火消し文章を書きます。

https://forums.newworld.com/t/dev-blog-update-on-current-issues/504297/1

それによると、本作は「To be very clear, New World is not client authoritative — from a simulation standpoint, New World is entirely server based」などと主張されていましたが、誰もこのような世迷い言を信じる者はいませんでした。クライアントサイドでの入力によってサーバーサイドの挙動が不正に変化してしまうのであれば、それは入力に対するValidationが何ら行われていないということであり、Server-Based Authoritativeなアプリケーションとは言えません。この声明発表は単に開発者たちが自らの無能を晒しただけに終わりました。


数日後。

アップデートによりアイテム増殖も無敵化も修正され、経済封鎖も解除され、事態は沈静化するかのように思われました・・・

が、今度は家具を無限に増殖させるバグが発見されました。

その方法とは・・・


1. 家のデコレーションを開始する。

2. 増殖したいアイテムを家に配置する。

3. 増殖したいアイテムが家に配置されたら、意図的に通信を遮断する。

4. しばらく待ってから、Cancelボタンを押して、家具配置をキャンセルする。

5. 中断されたのを確認してから、通信を復旧する。

6. アイテムが家に配置された状態のまま、手元にも残る。


お気づきかと思いますが、前回と全く同じ手順です、彼らは何も学んでいません。

あの日本を代表するみずほ銀行ですらこんなデタラメな修正はしないのでは無いでしょうか。こちらの問題も2回目の経済封鎖の後、現在では修正されているのですが、2回も同じ手口で成功したのですから、今でも似たような手口によるアイテム増殖が可能なのではないかという噂がコミュニティ内で絶えません。もはや完全な無法地帯です。


他にも、チャット欄にHTMLタグが使えてしまうバグだとか、

チャット欄のHTMLタグを利用してリンクをmouseoverした瞬間に攻撃するコードを仕込むバグだとか(onmouseoverが動作してしまうらしい)

全く、話題に事欠かないゲームです。


さて、ここまでお読みいただいたエンジニア諸兄の中にはお気づきの方もいらっしゃることかと思いますが、このNew Worldというゲーム、根幹がそもそもMMORPGとして動作するように設計されていない可能性が極めて高いです。実際、企画の初期段階では、MMOではなく、MOのPvPゲーム(ゲームに詳しい方ならRustといえばご存知でしょうか)として作られていたという噂があります。そのため中央のサーバーが全ての処理を解する設計になっていないのでしょう。現在のNew Worldは単に我々が動作させているクライアントと全く同じものをHeadlessモードでサーバー側で動作させ、その処理を真とするような実装になっているだけなのではないか、と私は勝手に推測していますが、あくまで推測の域を出ません。

ここでの学びは、根幹がそもそも間違っているシステムは、後から何をやっても救えないということです。

くれぐれも皆様もご用心を。


bot、その進化の軌跡

もう一つ、本作New Worldの世界で技術的に興味深い話題があります。それはBotです。MMORPG界隈においてBotという単語を使った場合、それは「プログラムで自動的にキャラクターを操作し、何らかの行動を行わせ続ける」行為を指し、全世界に存在するほぼすべてのMMORPGのエンドユーザーライセンス契約条項において禁止されています。禁止されている理由は多々ありますが、基本的にMMORPGというのは単一の行為をひたすら尋常ではない回数と時間に渡って繰り返しまくることで資源やアイテムやお金や経験値といったリソースを収集して強くなるのが目的ですから、当然自動化と相性がいい・・・失礼、自動化すると真面目に刺し身たんぽぽをポチポチやってる人間が不利益を被るわけです。そういったわけで禁止されているんですね。

まぁ、禁止されてるってことは、当然やる人間が居るわけです。本作New Worldにおいても全く例外ではないのですが、ただでさえバグだらけでマトモな入力検証すら行っていないゲームであることがすでに露呈しているわけですから、今や世界中のBot職人の格好の的となり、過去のゲームで見られた単純なBotとは比較にならないほど洗練されたBotの数々が世界の至るところで観察されるようになったのです。

そのいくつかをご紹介しましょう。


バージョン1.0: Fishing Bot


最初期からいるBotです。多分ゲーム開始2週間後にはもう大量発生していたと思います。名前の通り、自動的に釣りを行うBotとなっています。釣りは一定の位置から動く必要もなく、ただひたすらマウスをタイミングよくクリックするだけで無限に実行できますから、大変自動化しやすいんですね。おまけに本作の釣りは針を垂らした瞬間に針にかかった魚の種類がサーバからクライアントに送信されるというキ○○○・・・失礼、少々脳細胞が足りていない実装になっておりまして、そのためパケットをキャプチャすることで目的の魚が針にかかるまで即座にリセットを繰り返すという大変効率の良い機能まで兼ね備えております。

ちなみにこのBot、2021/12/01現在でも対策されておらず、世界中どこでも見ることができます。


バージョン1.1: Mining/Logging Bot

次に現れたのがこちら。鉱石を自動で掘ったり、リソースを自動で採集したりしてくれるBotです。本作の鉱石やリソースは毎回全く同じ座標に再生するため、同じ座標まで歩いていってボタンを押すだけで自動化できちゃうんですね。とはいえ、先に他の人が採掘していたり、リソースが存在しなかった場合にどうするか、とか、移動が邪魔された場合に対応する必要があるとかで、単純な釣りBotよりは遥かに実装難易度が高いようで、私がプレイしてたサーバーではなかなか見かけませんでした。


バージョン2.0: Questing Bot

https://www.reddit.com/r/newworldgame/comments/qhck8d/complex_scripted_questing_bots_already_available/

YouTubeに動画が見つからなかったのでRedditから引用してきたのですが、必見です。とうとう彼らBotは釣りや採集といった単純動作だけではなく、戦闘とクエストを自動でこなせるように進化してしまったのです。何十体ものBotが蟻の行列のように規則正しく動いて規則正しくクエスト目標の敵をしばき倒す姿は戦慄さえ覚えますが、AIだとかRPAだとかの波はこんなゲーム内にも訪れてきているわけですね。


バージョン3.0: PvP Bot

2週間ほど前ぐらいに発見された最新のBotです。これは噂レベルでしか聞いたことがないので真偽不明ですが、とうとうこの最新のBotはPvP (Player vs Player:対人戦) クエストを自動でこなせるようになったとのこと。といってもBotが我々人間相手に斬りかかってくるわけではなく、対人戦モードを有効にした状態で敵の基地に忍び込んでアイテムを手に入れて帰ってくる、みたいなお使いクエストでしかありません。ただのゾンビですから発見さえすれば簡単に始末できるのですが、問題はこれらが自動化されていて24時間365日常に動き続けているという点にあります。我々人間には休息が必要ですが、彼らは一瞬一秒たりとも休むこと無く、何度殺されても諦めること無く、ひたすら目標めがけて突進してくるのです。これにはどれほど優れた人間も太刀打ちすることが出来ません。これはゲームの中のお遊びですから笑い話ですが、現実世界の戦争がこのように無人の自動兵器がひたすら突撃を繰り返してくる世界になってしまったらと思うと、薄ら寒い思いがしますね。


ちなみにですが、今しかたgithubをちょっと調べてみたところ、golangで書かれたNew World向けのBotのソースコードが転がっていたりしましたので、ちょっと探せば皆様も簡単にBotを運用できるかと思いますが、決して真似しないでくださいね。


まとめ

なかなか得られない機会かもしれませんが、もし読者の皆様におかれまして、MMORPGを開発するという案件に参画するご縁がお在りになりましたら、その際は是非本記事の内容をご参考にしていただければ幸いです。


#pyspa Advent Calendar 2021 - 1日目


2011年8月12日金曜日

[NSObject load] と [NSObject initialize] の違い

クラスがObjective-Cのランタイムにロードされ利用可能になったタイミングで、そのクラス全体の初期化を行いたいということはよくあると思います。Objective-CではNSObjectクラスの以下のメソッドを用いてクラス全体の初期化を行うことができます。
  • + load
  • + initialize
この2つですが、結構挙動が異なります。詳細については以下のとおり。
http://cocoawithlove.com/2008/03/cocoa-application-startup.html
  • loadメソッドはクラスがロードされて利用可能になったら即座に呼び出される。
    • このとき、自分以外の他のクラスはまだロードされていない可能性があるので、自分以外のクラスを利用するような初期化はできない。
    • main関数の内部のNSAutoReleasePoolが用意されるよりも先に呼び出されるので、autoreleaseを使うような初期化を行う場合には自分でNSAutoReleasePoolを生成して管理する必要がある
  • initializeメソッドはそのクラスに実際のアクセスが最初に発生したタイミングで呼び出される。
    • 要するに一度も使われないクラスでは呼び出されない。
    • 自分以外のクラスもロードが完了しているので、自由に他のクラスを利用できる。
    • autoreleaseについても特に気にしなくて良い。
基本はinitializeメソッドを使うほうがより安全で確実なうえに、使われないなら初期化されないので経済的でいい感じです。こちらを使うことをお勧めします。

またloadメソッドについては、iOS実機で自家製frameworkを使っているを使っているとき、framework内部にビルドされているクラスのloadメソッドが呼び出されないという問題があります(静的ライブラリ.aについては未検証)iOSシミュレータおよびMacではきちんとframeworkに含まれているクラスについてもloadメソッドが呼び出されるのですが・・・ともかく地雷が大きいので避けたほうが懸命です。

2009年10月4日日曜日

各種WebサービスのAPI認証方法を調べてみた

自分でWeb サービスを作る際に、APIの認証ってどうやって作ればよいのだろうと思い立ち、各種Web サービスのAPIの認証方法を調べてみました。


■Google
参考にしたページはこちら。
http://code.google.com/p/pyrfeed/wiki/GoogleReaderAPI
認証方法
ユーザーIDおよびパスワードを元に、Cookieを生成
認証時のAPI通信
HTTPS POST
認証URL
https://www.google.com/accounts/ClientLogin
認証後のAPI通信
HTTP GET/POST, HTTPS GET/POST
トークン送出方法
HTTPリクエストヘッダのCookie属性に「SID」を含める
Cookieを使う方法ですので、Webブラウザを用いるWebアプリケーションに対する認証の場合は非常に簡単ですが、クライアントアプリケーションの場合は、認証URLのレスポンスを元にSIDを保存し、HTTPリクエストヘッダのCookie属性にSIDを追加する処理を自分で行う必要があるため、ちょっとだけ面倒です。それでもシンプルで分かりやすい方法だと思います。


■Remember the Milk(RTM)
参考にしたページはこちら。
http://www.rememberthemilk.com/services/api/authentication.rtm
http://www.rememberthemilk.com/services/api/methods/rtm.auth.getFrob.rtm
認証方法
APIキー、開発者とサーバー間の共通鍵、使い捨てセッションの3つを元に、セッションIDを生成
認証時のAPI通信
HTTP GET
認証URL1(使い捨てセッションの取得)
http://api.rememberthemilk.com/services/rest/?method=rtm.auth.getFrob&api_key=abc123
認証URL2(本認証)
http://www.rememberthemilk.com/services/auth/?api_key=abc123&perms=delete&frob=123456&api_sig=zxy987
認証URL2(認証済みセッションIDの取得)
http://api.rememberthemilk.com/services/rest/?method=rtm.auth.getToken&api_key=abc123&frob=123456

認証後のAPI通信
HTTP GET
トークン送出方法
HTTPパラメータに「auth_token」を含める
Webアプリケーションだけではなくクライアントアプリケーションへの対応を行っているためか、先ほどのGoogleのAPIと比べて格段に難しくなります。その代わり、HTTPS POSTを用いなくても、HTTP GETのみで認証を行うことが出来ます。こういうのをRESTって言うんでしょうか?正直良く分からず。
以下、クライアントアプリケーションでの認証手順。

1.APIキー申請をすると、以下の2つの値がもらえる
api_key・・・開発者がサーバーからもらえる公開鍵。公開鍵なので外に漏れても良い。
shared secret・・・開発者とサーバーが持つ共通鍵。絶対に外に漏れてはならない。

2.api_keyを元に、使い捨てセッションを生成する
frob・・・使い捨てセッション。以降、認証手続きの間のみ利用する。
frobの意味についてはhttp://en.wikipedia.org/wiki/Frobを参照。

3.shared secretと認証URLのリクエストパラメータを元に、api_sigを生成する。
認証URLには以下の3つのリクエストパラメータがつきます。
api_key=abc123&perms=delete&frob=123456
これを記号を抜いて結合して、
api_keyabc123permsdeletefrob123456

先頭にshared secretをつけて、
BANANASapi_keyabc123permsdeletefrob123456

md5 hash値を計算します。計算結果がapi_sigになります。
md5('BANANASapi_keyabc123permsdeletefrob123456')

4.api_key, frob, api_sigの値を元に、ユーザー認証画面を開いてIDとパスワードを入力してもらう
ここで認証URL2をユーザーにブラウザで開いてもらって、IDとパスワードを入力してもらえば認証が完了します。

5.認証が完了したセッションIDを取得する
auth_token・・・セッションID。次以降のリクエストには、全てこのauth_tokenをHTTPリクエストパラメータとして含める。


■Evernote
参考にしたページはこちら。
http://www.evernote.com/about/developer/api/evernote-api.htm#_Toc200272584
認証方法
メールアドレス、パスワード、consumerKey, consumerSecretを元に、セッションIDを生成
認証時のAPI通信
Thrift TBinaryProtocol wrapping a THttpClient transport
認証URL
https://www.evernote.com/edam/user
認証後のAPI通信
Thrift
トークン送出方法
UserStore Authentication Token?を付与する
Thriftというフレームワークが使われているようです。Thriftの実装がC, Java, PHP, Python, Perl, Rubyなどで用意されていて、クライアントはこれらの実装を用いて認証すればよいらしいです。詳細は良く分かりませんが、こちらもAPI Keyと共通鍵を利用しているため、比較的RTMの認証に近いことをしているように見えます。
ちなみに上記はクライアントアプリケーション用の認証で、Webアプリケーション用の認証にはOAuthを使用しています。OAuthについてはまた別の機会に調べます。


■Twitter
参考にしたページはこちら。
http://usy.jp/twitter/index.php?Twitter%20API
http://twitter.pbworks.com/API%20Docs#Authorization
http://watcher.moe-nifty.com/memo/2007/04/twitter_api.html
認証方法1
ユーザーID、パスワードを利用したBasic認証
認証時のAPI通信
なし
認証URL
なし
認証後のAPI通信
HTTP GET
トークン送出方法
http://username:password@twitter.com/のようにしてユーザーIDとパスワードを送出する
認証方法2
ユーザーIDおよびパスワードを元に、Cookieを生成
認証時のAPI通信
HTTP POST(HTTPSは無い?)
認証URL
http://twitter.com/login
認証後のAPI通信
HTTP GET
トークン送出方法
HTTPリクエストヘッダのCookie属性に「_twitter_session」を含める
認証方法3
OAuth
詳細については不明
以下、http://watcher.moe-nifty.com/memo/2007/04/twitter_api.htmlから転載。

public_timeline の取得等一部の API を除くほとんどの API で、認証を使用する。応答に protected なユーザに関する情報が含まれる可能性のある API は認証が必須となっている。
現在、OAuth認証とBASIC認証が使用可能。

トークンベースの認証 OAuth は、今のところベータ版であるが、将来、OAuth 認証を標準的な認証方法にする予定。
BASIC認証で使用するユーザ名はメールアドレスまたはスクリーン名のどちらでも構わない。

Webブラウザ等を経由して Twitter にログインしたときに発行される cookie を使うことで BASIC 認証の代わりにすることもできるが、公式には cookie を使っての API 実行はサポートしない。
(訳者による注記: API によっては cookie 使用時も BASIC 認証が要求されるものもある。また、BASIC認証での API 実行と cookie を利用しての API 実行では、異なる結果が返る API もある。特に、protected なユーザに関する挙動に違いが見られる。
OAuth 認証時も API の応答に cookie が含まれるが、次回 API 実行時にこの cookie をサーバーへ送り返す必要はない)。

もうこれを見るだけでばっちりでした。

2014年2月24日月曜日

Google Analytics for iOS SDK バージョン3で自動セッションマネージメントをするライブラリを書きました

Google Analytics for iOS SDK バージョン3にちょっとした機能を追加するライブラリを書いてみましたので公開いたします。

https://github.com/akisute/GAI-AutomaticSessionManagement

iOS 5.0以上で動作します。MITライセンスです。

■これは何?

Google Analytics for iOSは皆さんご存知の通りiOSアプリのセッション解析を行ってくれるSDKです。WebのGoogle Analyticsと同様に、ユーザーさんがどれぐらい、どのように自分のアプリを使ってくれているのかを調査することができます。

そんな便利なGoogle Analyticsなのですが、iOS向けのSDKバージョン3から何故か自動セッションマネージメント機能がなくなってしまいました。すなわちアプリがHomeボタンを押されてバックグラウンドに入ったとしてもセッションが自動的に切れないためセッション数およびセッション時間の正しい計測ができません。iOS SDKバージョン2以前、およびAndroid SDKバージョン3では相変わらず自動セッションマネージメント機能が有効になったままのため、何も考えずにSDKを導入するとAndroidとiOSで大幅に数値が異なるという悲しい事態に陥ります。多分大人の事情なんだろうと思います

ということでこのライブラリを作りました。このライブラリを突っ込めばこれまでどおりアプリがバックグラウンドに入ったら自動的にセッションが切れます。他にも同じように困っている人が居るはずだと思って似たようなことをしている人を探してみたのですが、まったく見つからず困ったので自分で作ることにしました。

インストール方法や使い方などはgithubのリポジトリを見ていただければと思います。

2012年11月11日日曜日

iOS 6.0の advertisingIdentifier と identifierForVendor にはバグがあるので注意

いささかタイミングを逃した感が強いのですが、厄介なバグにぶち当たってしまったので共有いたします。

iOS 6からUDIDに変わる識別子としてUIDeviceのidentifierForVendorとASIdentifierManagerのadvertisingIdentifierが使えるようになったのはすでにみなさんご存知かと思います。ですがどうもこやつらiOS 6.0だと正しく機能しない場合があるようなのです。

詳細は以下のとおり。
http://stackoverflow.com/questions/12605257/the-advertisingidentifier-and-identifierforvendor-return-00000000-0000-0000-000

こちらの情報元によると、iOS 6.0に Over-The-Air アップデート (iTunesを使わないで端末からアップデートする方法) するとこれらの識別子が常に00000000-0000-0000-0000-000000000000を返してしまうらしいのです!iOS 6.0.1では修正されているらしいです。

見事に私の UIApplication-UIID ライブラリもこのバグを踏んづけて大爆発してしまいました。

対処法としては生成されたIDが00000000-0000-0000-0000-000000000000でないか文字列比較する方法がよさそうです。

2008年12月21日日曜日

Cocoa(iPhone)で、日本語を含むURLを開く方法

  • 基本的にはCore FoundationのC関数を利用する
    CFURLCreateStringByAddingPercentEscapes()
  • ただし、一部問題のあるケースがある
    URL中に&を含む場合などは正しく作れないので別の方法が必要

プログラマをやっていると、だんだんと日本語が嫌いになってきます。
いや、嫌いというのはおかしいのですが、とにかく英語以外の言語はトラブルが多いです。
コンピューターというのはつくづく英語を処理するためだけに作られているのだと思います。
(だからこそ、プログラムの「国際化」で飯を食える人がいる訳ですけど!)

すみません、前置きが愚痴っぽくなってしまいました。
iPhoneプログラミングにおいてももちろん、日本語を使う際に罠があります。
たとえばNSURLRequestなどがそうです。
日本語の含まれるURLを開こうとすると、うまく解釈してくれないんです。
(といいますか日本語が含まれるURLはURLエンコードしなくちゃいけないんです。・・・当たり前ですね)

例えばこんな感じです:
http://s3.amazonaws.com/twitter_production/profile_images/65140989/るーみゃ_normal.jpeg


自分の画像だけ出ないぞ!

そこでURLエンコーディングを行う関数を探してみたところ、ありました。
Cocoaで日本語のGETを飛ばそう!

早速真似してやってみました。
CFURLCreateStringByAddingPercentEscapes()関数を使うのがポイントらしいです。
            NSString *originalUrl = [userDict objectForKey:@"profile_image_url"];
           NSString *encodedUrl = (NSString *) CFURLCreateStringByAddingPercentEscapes
           (NULL, (CFStringRef) originalUrl, NULL, NULL, kCFStringEncodingUTF8);
           person.profile_image_url = encodedUrl;

たったのこれだけでうまくいきました!



ところが、有頂天になってTwitter上で騒いでたら、
@psychsさんから突っ込みが。
JavaScriptでいうencodeURI相当なんで、
日本を適当にエスケープしたいときにはいいんだけど、
たとえばパラメタに「&」を入れたい場合とかは、
ちゃんとencodeURIComponent 相当のやつを使って組み立てなきゃだめだよ

なんと具体的なアドバイス!(ありがとうございます!)ちょっと調べてみました。
javascript: escape(), encodeURI(), encodeURIComponent() 比較

細かいところはわかりませんが、とにかく一部の文字がエンコーディングされないみたいなのです。
深刻なのは?とか=とかでしょうか。気をつけないとトラブルに巻き込まれそうですね。

ちなみにiPhoneでencodeURIComponent相当のエンコーディングを行うためにはどうすればよいのかですが、
リファレンスを引いてみても特にそれらしきものは見当たりませんでした。
ひょっとしたら自分でやるしかないかもしれません。

■2008/12/29 22:00追記
Twitter上で、@norio_nomuraさんにencodeURIComponent相当のエンコードを行う方法を教えていただきました!ありがとうございました!
http://twitter.com/norio_nomura/status/1083641557
CFURLCreateStringByAddingPercentEscapes(NULL, string, NULL, CFSTR (";,/?:@&=+$#"), kCFStringEncodingUTF8);

最大のポイントは、第4引数のCFSTRです。ここで指定した文字列はエンコードされずにそのまま残るようです。
第4引数をNULLのまま使うと標準のencodeURL相当になり、
この例のように指定すると、encodeURLComponent相当のエンコードが可能になります!

2011年2月28日月曜日

UIWebView のスクロールを制御するためのプロパティを書いてみた

UIWebView にどうして scrollEnabled プロパティがついてねえんだ Apple のチンパンジー野郎!とお嘆きの全国1000万の iOS 開発者の皆様、こんばんわ。もちろん私もその一人であります。

嘆いていてもしょうがないので何とかスクロールを制御する方法を・・・と思って探していたら、すでに2009年の地点で @nakamura001 さんがこんなブログを書いてらっしゃいました。

http://d.hatena.ne.jp/nakamura001/20090520/1242837408

が、遷移先で詳解されている

http://praveenmatanam.wordpress.com/2009/04/03/how-to-disable-uiwebview-from-user-scrolling/

のコードが正直いまいちなのです。何がいまいちって、せっかくのCocoa環境であるにも関わらず、わざわざobjc/runtime.hなんていう低レベルなC言語の関数を使っています。別にパフォーマンスがタイトな場所でもないですし、かっこよくCocoaっぽく書き直してみました。ということで書き直したコードがこちら↓

https://gist.github.com/846258

で、このAdditionを導入するとですね、
UIWebView *webView = [[[UIWebView alloc] initWithFrame:frame] autorelease];
webView.webViewScrollEnabled = NO;
みたいな書き方ができてハッピーになれます。

内部的には NSInvocation を使っています。 Mac OS X 10.5 (iOS 2.0) から存在するこのクラス、本当に便利で、ぶっちゃけ呼び出し対象のシグネチャさえわかれば何でも呼び出せるスグレモノです。フレームワークを作るときなど、呼び出し先のシグネチャしかわからない状況下で対象のメソッドを呼び出す時などに便利な感じかもです。その上 NSInvocationOperation を使ってそのまま並列化もできたりして。
NSMethodSignature *sig = [subview methodSignatureForSelector:selector];
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:sig];
[invocation setTarget:subview];
[invocation setSelector:selector];
[invocation invoke];
BOOL result = YES;
[invocation getReturnValue:&result];
return result;

2016年6月30日木曜日

Auto Layout と Manual Layout を混載させるときに役立つ UIView.translatesAutoresizingMaskIntoConstraints プロパティの話

Auto LayoutがiOS 6で導入されてはや4年、未だによく理解していなかった挙動に UIView.translatesAutoresizingMaskIntoConstraints があります。このプロパティは自分がプログラムコード上で生成したviewをAuto Layoutするときにfalseにする必要があるものということで皆様記憶されているかと思うのですが、具体的にこのプロパティは何をやっているのかが個人的に全く謎でした。それが今日一つ謎が解けましたのでここに共有させていただきたいと思います。

UIView.translatesAutoresizingMaskIntoConstraintsの値がtrueのときとfalseのときの違いについて以下に記載します(iOS 8以上で確認しています)。

  • trueのとき
    • 対象のviewのframe、すなわちx, y, width, heightの4つの要素をview.frame, view.bounds および view.center プロパティから直接操作することが可能になります。これはAuto Layoutが導入される以前のiOSの世界と同じ状態です。この挙動をAuto Layoutとマッチさせるため、対象のviewのx, y, width, heightの4要素を指定された値に固定するようなAuto Layout Constraintsが自動的にシステムによってviewに挿入されます。この自動的に挿入されるAuto Layout Constraintsのpriorityは常に1000 (Required)になります。
  • falseのとき
    • 対象のviewのframe、すなわちx, y, width, heightの4つの要素はすべてAuto Layoutエンジンが管理するようになり、view.frame, view.bounds および view.centerの値を直接書き換えても一切無視されるようになります。Auto Layout Constraintsが設定されていない場合、viewのframeはCGRect.zeroになります。

プロパティの名前にAutoresizing Maskとか入っているのでてっきりAutoresizingの仕組みに影響している用に見えますが、実際には全く関係ありません。その証拠にAutoresizingMaskの値をどのように変化させても勝手にAuto Layout Constraintsが挿入されてしまいます。このプロパティはあくまで当該viewのframeを自動的に操作するようなAuto Layout Constraintsを挿入するか否かを決めるフラグとして覚えると良いでしょう。

さてこの挙動を覚えると何が嬉しいかと申しますと、Auto Layoutと非Auto Layoutを混載させるときに非常に役立ちます。こうすることで、特定のviewだけをframe手動操作で設定し、他のviewはAuto Layoutに任せるというような荒業が自由自在に可能になります。

具体例を見てみましょう。例えば以下の様なニュースを表示する画面を作ってみようと思います。



ここでこのnewsを表示するviewのframeは複雑なアニメーションをさせたいなどの理由で外部からマニュアルで設定したいが、viewの中身はauto layoutに任せたいというようなケースがあるかと思います。

というわけで普通にAuto Layoutで作ってみましょう。
private func commonInitialize() {
        self.translatesAutoresizingMaskIntoConstraints = true
        self.backgroundColor = UIColor.white()
        
        self.imageView = UIView()
        self.imageView.translatesAutoresizingMaskIntoConstraints = false
        self.imageView.backgroundColor = UIColor.green()
        self.addSubview(self.imageView)
        
        self.titleLabel = UILabel()
        self.titleLabel.translatesAutoresizingMaskIntoConstraints = false
        self.titleLabel.text = "factorio alpha 0.13 has been released!"
        self.titleLabel.font = UIFont.preferredFont(forTextStyle: UIFontTextStyleTitle1)
        self.titleLabel.numberOfLines = 2
        self.addSubview(self.titleLabel)
        
        self.articleLabel = UILabel()
        self.articleLabel.translatesAutoresizingMaskIntoConstraints = false
        self.articleLabel.text = "In 0.13 we have the new multiplayer matching server and server browser. This will let you find games of people online join your friends and other stuff. Server games are published to the server and clients can browse existing games. The first thing you will notice is the new multiplayer menu. When you click on 'Browse Public games' you will be asked to log in to your factorio account."
        self.articleLabel.font = UIFont.preferredFont(forTextStyle: UIFontTextStyleBody)
        self.articleLabel.numberOfLines = 0
        self.addSubview(self.articleLabel)
        
        let views: [String: AnyObject] = ["imageView": self.imageView,
                                          "titleLabel": self.titleLabel,
                                          "articleLabel": self.articleLabel]
        
        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[imageView]|", options: [], metrics: nil, views: views))
        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-10-[titleLabel]-10-|", options: [], metrics: nil, views: views))
        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-10-[articleLabel]-10-|", options: [], metrics: nil, views: views))
        self.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[imageView]-10-[titleLabel]-10-[articleLabel]|", options: [], metrics: nil, views: views))
        self.imageView.addConstraint(NSLayoutConstraint.init(item: self.imageView, attribute: .height, relatedBy: .equal, toItem: self.imageView, attribute: .width, multiplier: 0.66, constant: 0))
    }


しかしながらこのコードはAuto Layout Warningが発生してしまいます。

2016-06-30 23:24:12.050906 AutoLayout[1725:80607] [LayoutConstraints] Unable to simultaneously satisfy constraints.
 Probably at least one of the constraints in the following list is one you don't want. 
 Try this: 
  (1) look at each constraint and try to figure out which you don't expect; 
  (2) find the code that added the unwanted constraint or constraints and fix it. 
 (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "NSAutoresizingMaskLayoutConstraint:0x7fd60ae1db90 h=--& v=--& AutoLayout.Example2View:0x7fd60ad35b70.width ==   (active)",
    "NSLayoutConstraint:0x7fd60ac360a0 H:|-(10)-[UILabel:0x7fd60ad3c6b0'factorio alpha 0.13 has b...']   (active, names: '|':AutoLayout.Example2View:0x7fd60ad35b70 )",
    "NSLayoutConstraint:0x7fd60ac36380 H:[UILabel:0x7fd60ad3c6b0'factorio alpha 0.13 has b...']-(10)-|   (active, names: '|':AutoLayout.Example2View:0x7fd60ad35b70 )"
)

これは先程のUIView.translatesAutoresizingMaskIntoConstraintsについての説明を元にすると以下のように解釈できます。

  1. UIView.translatesAutoresizingMaskIntoConstraintsがtrueに設定されていることにより、このviewにはwidth=frame.size.widthになるようなAuto Layout Constraintsが自動的に設定されている。
  2. このviewにはさらに "H:|[imageView]|"となるようなAuto Layout Constraintsが設定されている。これはimageViewを横幅いっぱいに表示するため。
  3. しかしながらこのような設定を行うと、imageViewが親となるviewの横幅を自分の横幅に合わせて引っ張ろうとするConstraintsが定義されてしまうので、1. で自動的に設定されたConstraintsと衝突してしまう。
  4. 結果としてwarningが発生する。
これを回避してやるにはいくつか方法があります。

  1. "H:|[imageView]-(0@999)-|"のように設定することで、右側ないし下側のpriorityを999に下げる。こうすることによってUIView.translatesAutoresizingMaskIntoConstraintsによって設定されるConstraintsのpriorityが勝つためワーニングは発生しなくなる。
  2. 両側を引っ張るようにvisual formatを使って設定するのをやめて、view.x=imageView.x, view.width=imageView.widthとなるようにConstraintsを付与する。
例えば2. のケースはiOS 9以降であればNSLayoutAnchorを使って簡単に設定ができます。

self.imageView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
self.imageView.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 1.0).isActive = true

これでAuto LayoutとManual Layoutをより自由自在に混載させることが可能になると思います。

2012年5月5日土曜日

C# で map とか reduce みたいな楽しいリスト操作をしたい

プログラミング言語がロジックを組むのに向いているかいないかを判断するときの基準に、私はよく「文字列の操作が優れているか否か」「配列・辞書・集合の操作が優れているか否か」を評価点に挙げます。文字列や配列、辞書、集合の操作はほとんどすべてのアプリケーションで必要になり、それらの生産性が高く高速に処理してくれる言語ほど簡単で高速なロジックが組めると思うからです。

そういう意味でObjective-Cを考えると、文字列の操作はまぁまぁ良い(特にUnicode周りがなかなか優れている、正規化もできるし)のですが、配列・辞書・集合の操作がイマイチで、作るの面倒なら操作するのも面倒。さらには良く欲しくなる以下の操作が欠けています。
  • map - 条件式を渡して、もとの集合の各要素に条件式を通した結果を新たな集合として返す。
  • reduce - 条件式を渡して、要素を前から順番に計算して畳み込み、集合から一つの要素にする。
  • any - 一つでも要素が条件式を満たすならtrue, すべての要素が満たさないならfalse
  • all - すべての要素が条件式を満たすならtrue, 一つでも満たさないならfalse
ではMono上のC#ではどうなんだろうということで調べてみたところ、LINQを提供するSystem.Linq名前空間に高度な配列・辞書・集合操作を行うための拡張メソッドが用意されているということがわかりました。

http://docs.go-mono.com/?link=T%3aSystem.Linq.Queryable
http://msdn.microsoft.com/ja-jp/library/system.linq.queryable(v=vs.90).aspx

ということで早速使ってみます。



実行速度が高速なのかどうかはわからないのですが、なかなか面白いです。ラムダ式が使えるのもスマートで素敵ですね。うーん、C#好きになってきたかも。

2019年12月7日土曜日

最近のクルマの話

こんにちわ、毎年恒例 pyspa Advent Calendar も今年は2019年となりました。12月7日担当のakisuteです。明日の担当は @rokujyouhitoma です。

さて皆さん突然ですが、クルマは持ってますでしょうか?

持ってない?当たり前ですね。馬鹿みたいに高い税金と車両代金と高速道路通行料を搾取されるだけで何一つメリットありませんから。

それでは皆さん、スマホはお持ちでしょうか?

持ってる?馬鹿にすんな?当たり前ですね。今どきスマホ無いと生活が困難ですね。10年ほど前はどうやって電車の車内で時間を潰していたのかすら、もはや思い出せそうもありません。

そういうわけで、私のブログをわざわざ見に来てくださっている方はIT業界、それもスマホアプリ関連の業界の方が大多数でしょうから、スマホについてはバリバリ詳しいけど、クルマなんて持ってないから何一つ知らないし興味もないよ、という方が多数派を占めてらっしゃるのではないでしょうか。本日はそういう多数派の方向けのお話をさせていただきたいと思います。

さて、皆さんがクルマに何の興味も知識もない前提でお話を進めさせていただこうと思うのですが、それでも平成以降に製造されたクルマにはカーナビとかいう地図とか表示するディスプレイが付いていたり、スマホをBluetoothで接続したりしてカーオーディオを聞いたりできるということぐらいはご存じの方が多い、と私は勝手に信じております。ところが今や元号も令和となりまして、どこもかしこもインフォメーションテクニック的なヤツが幅を利かせる世の中になっておりますゆえ、当然クルマに搭載されているカーナビですとかカーオーディオ的なやつもご多分に漏れず高機能化しています。おまけに最近は自動運転 (=カーナビの現在位置情報や道路情報を車両側が使用したい) ですとか、クルマの走行特性モードの設定 (=車両側の走行特性をUIから操作する必要がある) ですとか、クルマのハンドルについているスイッチからカーオーディオを直接操作したい (=車両側の入力をカーオーディオと連動させなければならない) ですとか、そういう昭和の時代では考えられないような高度な統合処理が必要な機能の搭載がクルマにとってもはや必須となっておりまして、昔のように後付で買ってきたカーナビやカーオーディオに配線すればOK、というわけにはいかなくなってきました。

そこで、ここ2年ほど以内に発売された新車の真ん中に鎮座するディスプレイはカーナビではなくマルチファンクションディスプレイとかディスプレイオーディオとか呼ばれるようになってきておりまして、タッチパネルを装備し、カーナビ、オーディオ、エアコン、車両の設定などを直接タッチ操作で行う高度なシステムに変貌しています。

それだけには留まらず、今年発売された新車に至っては4G接続用のSIMとアンテナが装備されており自立通信が可能で、電話は当然として緊急時の連絡サービスや、煽り運転されたときにボタンひとつで自動的に通報してくれるサービスもあり、カーナビの地図情報の自動更新やシステムアップデートもこなすことができます。なぜクルマにこのようなものが必要かといいますとこれまた将来的に重要になる自動運転が絡んできまして、最新の地図が常に使用されていないと自動運転は危険だからだとか、万一なにか問題があったときに自動運転中の車両を遠隔監視できていないと駄目だとかそういう理由があります。そこで先んじてこのような通信能力をクルマに持たせているというわけです (※おそらくは裏で匿名運転データの収集も行っているのではないかと推測していますが、そのへんは不明です) 。

さらには昨今のスマホ社会を反映し、各社自動車メーカーが用意したスマホアプリと車両が連動して、スマホアプリから現在の自分のクルマの位置を調べるだの、走行距離とガソリンの残量を調べるだの、カーナビに目的地を送信するだの、果ては走行前に社外からエアコンをONにしたりクルマのドアロックをスマホアプリから開けてしまうだのといったことまで可能になっているのです。

どうでしょう、まるでスマホみたいですね。
そのとおり、最近のクルマにはちゃんとしたOSがいます。

このクルマのOSは現在のところ基本的には各社自動車メーカーが内製で作っている (かまたは基本システムだけ買ってきて各社勝手にカスタマイズして使っている) ものが多いようで、iOSとAndroid以外のOSが事実上絶滅したスマホの世界とは異なり、どの会社からクルマを買うかによって大いに出来栄えと機能に差がある状態です。

中にはスマホ世代の我々にはひと目見ただけで開発者をコンクリ詰めにして東京湾に投げ捨ててやりたくなるようなひどい代物も多数存在します。それどころかタッチしてから0.5秒遅れで反応するUIなどといった、初代iPhoneどころかAndroid 1.5 Cupcake世代の産業廃棄物スマホにすら劣るレベルの実装すら、世の中のクルマに存在します。

ヤバいですね。何処のドイツでしょうか、そんなひどいブツを作るのは。実際に見てみたいと思いませんか?

・・・ここで、ちょっと話は変わるのですが。

メルセデスというクルマのブランドがあります。

あまりクルマに詳しくない人でも、ベンツといえば分かるでしょうか。そう、ドイツの高級車メーカーです。最近はメルセデスという名前で名乗っていますので、メルセデスと呼ぶことにします。

でそのメルセデスといえば、皆さん誰でもすぐに高級な外車で、金持ちが乗ってそうなイメージを浮かべると思います。当然、見た目も高級だし、内装も高級だし・・・

・・・車載OSの動作だって高級で高品質を期待します。当たり前ですよね。下手すると1000万円とか払うクルマなわけですから、iPhone 11 Proみたいにきれいな見た目で高速にちゃんと動作することを期待したいじゃないですか。iPhoneなんて10万円かそこらしかしないんですから。

じゃあ見てみましょうか。メルセデス。でもなぜだか知らないんですが日本仕様・右ハンドル・日本語表示のMBUX (Mercedes Benz User Experience) のUI、ネット上にほとんど落ちてないんですよね。しょうがないから実車の写真を撮ってきました。

ネットから拾ってこれた左ハンドルでの画像ですが、横一列につながった超ワイドなディスプレイ、一見なかなかカッコいいですね。高級感もあるし、これは期待できるのでは・・・

って思った?残念!半角カナちゃんです!

見ての通り、細部に至るまで半角カナです!

 こっちは半角カナじゃありませんがガソリンスタン...になっちゃってます。

幸いにしてタッチパネルの操作感度はまぁまぁ良好だし、タッチしてから1秒以上固まるみたいな絶望的な遅さは無いのですが、どう見ても初代iPhone 3Gとドッコイドッコイ程度の動作速度しかありません。そもそも走行中にカーナビを操作したくなったら、身を乗り出してこのタッチパネルを手で触れというのでしょうか?まことに素晴らしいUXだと思います。

引用元: https://www.elasticfeed.com/44db3e868b3b3bfe82835c65441ea3ca/

もちろんそのような危険なことをしないで済むように、こうして中央手元にMagic Trackpadもどきが付いているのですが、手触りはともかくこいつの反応が10年前のWindowsノートパソコンのほうがマシなのではという次元でして、自分のmacに触れた後にこいつに触ると引きちぎって車外に投げ飛ばしてやりたい気分に駆られること間違いありません。設定で感度を調整したりもできますがどう調整してもイライラするので、結局中央のタッチパネルを身を乗り出して触るほうがマシだったりします。

その他、ハンズフリー操作のために音声アシスタント、いわゆるSiriもどきがついてるのですが、社内での会話中にうっかり「メルセデス」という単語を口にした瞬間「なにか御用でしょうか」と起き上がってきて大変邪魔です。名前を呼んではいけないあの人扱いみたいになっています。しかも初代のSiriより頭が悪く、基本的に何お願いしてもマトモに対応してくれないので使えません。お願いだからOK Googleと席を変わってほしくなります。



極めつけはカーナビです。なんとこちら1年近く道路情報が更新されていないようで、設定画面から最新状態に更新しているにもかかわらず半年前に開通した道路が地図上に存在しません。当然カーナビはそこを迂回しようとします。全く使えません。メルセデスの人に聞いてみたところ、現在地図データを頑張って作ってるらしいです。頑張ってくださいね。

ちなみにこちら、お見せしましたのはAクラスといいます一番安物のメルセデスですが、実は2000万円以上するメルセデス・マイバッハと言われる最上位車種でも全く同様のMBUXが使用されています。それどころかAクラスは今年モデルチェンジされたため、下手するとそれらよりも最新のOSが搭載されています。要するに2000万円以上する超高級車でも1年前の道路しか存在しないカーナビを使わされるというわけです。まぁ、そんなクルマを買うような人は自分で運転なんかしないでしょうから、何の問題もないのかもしれませんね。

じゃあ、次はメルセデスのスマホアプリを見てみましょうか。

まぁ悪くはないですね。

なんかWebViewっぽい匂いがしますが・・・

一応ネイティブアプリのようです。FOSS (Free/OpenSource Softwares)をちゃんと列挙しているのは好感が持てますね。

一応機能は充実してますね。

見ての通りクルマのドアロックをこのアプリから開けることもできます。

カーナビ連動もありまして、この画面から目的地を検索してクルマのカーナビに飛ばすことも可能なのですが、

見ての通りこの程度の検索すらロクにできませんので使い物になりません。まぁよしんば目的地がクルマに送信されたところで1年前の日本地図に基づいてナビゲーションされてしまうので、どっちにしろ使い物にならないと思いますが。

いかがでしたでしょうか。まぁ、車載MBUXに比べると幾ばくかマシかもしれませんが、2000万円以上払った人専用アプリなんてものはございませんので、最廉価のAクラスユーザーでも最上位のVIPユーザーでもこのアプリを使わされることになります。もうちょっと頑張れよと思わないでも無い気がします。

一応、弁護させていただきますと英語版やドイツ語版は半角カナではないのでフォントはマシですし、多分ドイツにサーバがあるのでドイツで使ったほうがアプリの動きも多少はサクサクだと思いますし、ドイツ国内のカーナビは多分1年遅れで更新されるとか言うことも無いはずです。要するに日本版が飛び抜けてショボいだけかもしれません。あとクルマ自体は普通にとんでもなく良かったので、アプリとか気にしない人にとっては最高のクルマだと思います。

で。
また閑話休題で申し訳ないのですが。

テスラというクルマのブランドがあります。

最近話題になることが多い電気自動車のメーカーです。あのイーロン・マスクの会社というと我々IT業界人には通じやすいのではないでしょうか。何かやってくれそうな気がしませんか?

そのテスラなんですが、こんな感じなんです。

iPad Proより巨大なタブレットのようなサムシングが鎮座しています。

引用元: https://response.jp/article/2019/08/19/325526.html

見てください、これ。まるでiPadのアプリにしか見えません。このテスラ モデル3の社内にはハンドル周り以外に一切の物理ボタンが存在せず、ディスプレイもダッシュボードのメーターもありません。速度計の確認も、エアコンの操作も、ハンドルとアクセルとブレーキとギアの選択とウィンカー操作以外、何もかもすべてこのiPadで行うことになります。

普通にメチャクチャ綺麗です。カーナビに至ってはおそらくこれGoogle Mapsそのものです。当然、Google Mapsですから、1年前の日本地図が表示されることもなければ、パチンコガンダム駅が存在することもありません。完璧に最新最先端の地図が無料でいつでも使えます。素晴らしいですね。

では、テスラのアプリはいかがでしょうか?

当然のようにネイティブです。まぁReact NativeかもしれませんしFlutterかもしれませんが。




どうみても出来が良いです。半角カナなんて汚物は当然存在しません。

これで、テスラ モデル3の車両価格は最高でも717万円、最安値ですと511万円で収まります。

以上で大体私の申し上げたいことはなんとなく伝わってきたのではないかと思います。つまり、我々スマホ開発者界隈が買うべきなのはテスラってことです!さぁ、このテスラの新車を今すぐ予約してあなたも未来を体験しましょう!



・・・やっぱりメルセデスのほうがいいですね!メルセデス最高!



最後に余談になりますが、Apple CarPlayとAndroid Autoについてちょっと話します。この2つはスマホ開発者界隈の皆様も少しくらいは聞いたことがある名前ではないでしょうか。これまで見てきましたとおり、最近のクルマには車載OSが存在するのですが、Apple CarPlayやAndroid Autoは車載OSのアプリの一つとして動作します。要するに車載OSのナビアプリを開いたりオーディオアプリを開く感覚で、CarPlayアプリを開いて、その中でCarPlayのUIの中で更にiOSのアプリを操作する、みたいな作りになっています。

引用元: https://www.elasticfeed.com/44db3e868b3b3bfe82835c65441ea3ca/

そのせいで、見てのとおりに画面が狭いです。どうやらApple Carplayは7インチの画面を前提に作ってあるようで、10インチのワイド画面であるメルセデスのディスプレイオーディオにはフィットしません。しかしながらこの中は完全にiOSであり、マトモなGoogle Mapsが機能します。おまけにスマホと車体が連携しており、車体側から車載GPSやジャイロコンパスなどのより正確な情報を提供してもらえるため、スマホ上で使うGoogle Mapsより段違いに自車の現在位置精度が高くなります。

参考: https://developer.apple.com/documentation/carplay
参考: https://developer.apple.com/design/human-interface-guidelines/carplay/interaction/car-data/

なんかクルマ向けにアプリ作ってみてえなと言う人がいらっしゃいましたら今がチャンスかも知れませんのでぜひぜひやってみていただければと思います。まぁ、デバッグに実車が必要・・・かもしれませんけど・・・

最近は国産車でもトヨタ・カローラという昭和のジジイ以外だれも知らないようなオワコンカーがびっっっっっっっっっっくりするぐらいモダンで素晴らしいクルマに生まれ変わりまして、そちらの新型トヨタ・カローラには標準でカーナビがついておらず、Apple CarPlay / Andriod Autoやトヨタ独自のSDLという規格を利用してスマホをナビにすることを前提の作りになっています。まさに今のスマホ時代にふさわしい素晴らしい方針だと思います。

2008年11月9日日曜日

CS193P Cocoa Programming - 1日目の宿題Aをやってみた

  • http://www.stanford.edu/class/cs193p/cgi-bin/index.php
  • これはわかりやすい
  • 宿題があるのが実にうれしい
  • Interface Builderで配置したクラスの属性(位置とか表示するイメージとか)を操作するときは、Command + Shift + I またはCommand + 1から4
  • Labelの文字の大きさとフォントは変えられたが色とか太字はなぜかうまくいかず
  • vertical centerな配置やholizonal centerな配置を行うには、メニューのLayout > Align
今日からスタンフォード大学の学生になった気分でさくさくっと勉強。
手始めに1日目の宿題から。・・・こんなに宿題を嬉々としながらこなす私の姿を学生時代の自分に見せてやりたいものだ。
一つ目の宿題は、コーディングはいっさいなく、interface builderからCocoa Touchが用意しているviewやclassを配置して画面をデザインしてビルドして走らせるだけというもの。


こんな感じでinterface builderにパーツを配置して・・・


できた!初日の宿題だけあって実に簡単。

こういったグラフィカルなGUI作成機能は別に目新しいものではない(Microsoft Visual Studio 2005などでもできる)が、これまで使ったことがあるツールよりは使いやすかった気がする。まぁVS2005はマイクロソフト語で書かれているので読めない=論外だし、Eclipse GEFはそもそもSwing自体があまりよいフレームワークでなかったため使いづらかった。
なによりiPhoneの場合はデバイスの画面サイズが決まっているから絶対座標指定がしやすい。

2009年3月4日水曜日

MacとiPhone向けの高機能GTDツール、OmniFocusを使って、ThinkingRockと比べてみました


  • 非常にMac的なショートカットと、GTDツールというよりアウトラインプロセッサーのような操作感覚に慣れるまでが大変だが、慣れてしまえば大変高速にタスクを収集・処理できる優良ソフト=OmniFocus
  • 名前の通り、フォーカス能力が超強力。自分が今何をしているか、何をするべきかに一瞬でフォーカスできる
  • 「次に今すぐ行動可能なアクション」だけが目の前に表示される快感
  • Macのどこからでもショートカットキー一発でタスクを収集できる機能や、現在の検索条件および画面設定をパースペクティブとして保存できる機能など、地味に便利な機能がそろっている
  • 入力補完がかなり高レベルのできばえで気持ちよく使える。Remember the Milkと同等以上
  • 日本語版のOmniFocusは翻訳が狂っていたり日付指定フォーマットに一部問題があるので、英語版を推奨
  • 欠点は高すぎることと、Mac+iPhoneの環境が前提になっていること(それ以外の環境で利用できない)
  • 安くてどこでも使えるシステムを構築したい方にはThinkingRock + Remember the Milkをお勧め

つい先日にThinkingRockを使いはじめた旨の記事を書いたのですが
アレからしばらくして、やっぱり我慢ならなくなり、ついにMac&iPhone向けの高機能GTDツール、OmniFocusに手を出してしまいました。
ということでThinkingRockと比較してみたレポをアップしたいと思います。


■そもそもGTDとは何ぞや

グーグル先生にお尋ねになるか、または以下の本を読んでみていただけるとよいかと思います。
はじめてのGTD ストレスフリーの整理術


■これまで試したGTDツール変遷

フランクリンプランナー

Remember the Milk + フランクリンプランナー

Remember the Milk Pro + Appigo Todo + フランクリンプランナー

Remember the Milk Pro + Remember the Milk for iPhone + フランクリンプランナー

ThinkingRock + Remember the Milk for iPhone + フランクリンプランナー

OmniFocus + OmniFocus for iPhone + フランクリンプランナー


新しい物好きが災いして、いろいろと試してます。
一時期フランクリンプランナーを辞めようかと思った時期もあったのですが、紙の手帳はやはり絶対必須みたいです。
私はアナログな人間だからか知りませんが、ペンで書かないと頭に入ってこないですし、モニタを見るのも嫌だというぐらい疲れているときでも紙でしたら平気です。あと、会議中にiPhoneを取り出してメモを取るのがはばかられるという問題もあったりします。

その後長いことRemember the Milkを使っていたのですが、使い込んでいるうちにRemember the Milkは備忘録としては超一級でもGTDツールとしては3流という結論に至ってしまったため、別のツールを探す必要性が出てきました。
(コンテキストの概念がないのはまだ運用で何とかなったのですが、プロジェクトの階層化ができないのと、プロジェクトとアクションをシームレスに追加変更できないのが致命的でした)


えーマジ?階層化できないの?キモーイ!orz

そこで最初に目を付けたのが前回ご紹介したThinkingRockです。


■ThinkingRock + Remember the Milk
この組み合わせの長所と短所を簡単にご紹介すると以下のようになります。

○長所
  • ThinkingRockは厳密にGTDのプロセスを体現しているので、GTD初心者には最適。使っているだけでGTDの考え方を学べる
  • プラットフォームを全く選ばない。ThinkingRockはJavaさえあればどこでも動きますし、Remember the MilkはWebに接続できればどこからでも利用できます
  • プラットフォーム間の連携が容易。DropBoxを利用してThinkingRockのデータベースを共有することで、手持ちのマシン全てで簡単に同期を行うことが出来ます。WinとMacの間の連携もスムーズ
  • ほとんど無料。必要な出費は、せいぜいRemember the MilkをProにして(年間たったの25ドル)iPhoneからでも利用できるようにする程度
×短所
  • ThinkingRockとRemember the Milkの間の連携が全くない。Remember the Milkで収集したタスクをThinkingRockに移動したり、逆にThinkingRockから明日やりたいタスクを持ち出すためにRemember the Milkに書き出すなどといった無駄な作業を行う必要があります
  • ThinkingRockは厳密にGTDのプロセスを体現しているがゆえに厳密すぎ、逆にRemember the Milkは備忘録程度の機能しかなくプロジェクトを扱うには不向き
  • どちらのツールもフォーカス力不足。プロジェクトが30程度、アクションが100を超えてくるともはや制御不能


ThinkingRockとRemember the Milkで、無料&どこでも使えるGTDを!

お金がかからずに、どこでも使える組み合わせと言う意味で、これからGTDをやってみようとお考えの方には最適ではないかと思われる組み合わせです。ThinkingRockは本当にお勧め。
問題はプロジェクトとアクションの数が増えてきたときです。どちらのツールもフォーカスをするための機能が貧弱です。
ThinkingRockには検索とソート程度しかフォーカスをするための選択肢がなく、
RtMには強力な検索機能がありますが、ThinkingRockがメインのシステムになっているため生かしきれませんでした。あと、検索結果を残すためのSmart Listが10とか20とか大変な数になってしまい、今度はどのSmart Listを利用すればよいのかわけがわからなくなるという別の問題が生じる恐れが・・・


■それで結局、OmniFocusってどうなの?
対するOmniFocus。こちらも長所と短所をまとめてみました。

○長所
  • 「今すぐ出来る行動」と「今すぐは出来ない行動」を自動的に判断してくれるのが素晴らしい
  • Mac版、iPhone版ともに、タスクに対するフォーカス能力が超強力
  • MacとiPhoneが完全にシームレスに同期可能。iPhoneからいつでもメインのシステムに入っているアクション一覧を参照可能だし、iPhoneからいつでもInboxの整理をしたり出来る
  • ThinkingRockに比べ入力補完やショートカットキーが大変充実しておりサクサク使える
×短所
  • 高い。高すぎる。Mac版とiPhone版あわせて1万円、さらにMobile Me代が年間1万円
  • Mac+iPhone以外では利用できない。うかつに携帯を買い換えたりPCを買い換えたりしようものならタスクが参照できなくなる危険がある
  • 同期が重い。Mobile Meを利用しているのですが、同期ボタンを押したらたまに2分とか待たされるときがあります。RtMなら20秒程度で済むのに


OmniFocus for iPhone

OmniFocusの機能で特筆に価するのはなんといってもフォーカス力です。
Thingsのようなほかの有名GTDツールを使ったことがないので比較は出来ないのですが・・・

「期日が明日までで、かつ仕事先のオフィスの近くで出来るタスクだけを表示してほしい」
「今週どうしてもやっておきたかったとマークしたタスクだけを、今すぐ行動できるものだけ表示してほしい」
「そういえば最近Blog書いていなかったので、Blogネタの中で一番時間がかからないものを書きたいからアクションを表示してほしい」
「時間が空いたので開発をしたい。開発タスクの中で次に行動できるアクションを表示してほしい」

このようなニーズを(設定さえしておけば)ショートカットキー一発で満たしてくれるというのが神業的に素晴らしいです。
しかもMacの中だけではなく、iPhoneでもほぼ同じことが出来ます。
帰りの電車の中で、「家に帰ったら寝るまで1時間しかないけれども、何が出来るか」などと考えたりできるわけです。もう最高。


今日、帰ってから実行するアクションは・・・

「今すぐここで出来る行動」を管理できると言うのも素晴らしい点です。
たとえば3月3日現在の地点で、「今すぐやる」リストに3月8日開催の「例大祭の列に並ぶ」なんてアクションを出されてもイラッとするだけです。
このアクションは3月8日の朝に「今すぐやる」リストに表示されてほしいのです。そしてこれが実現できているGTDツールが意外なほど少ない。
RtMには「開始日時」の概念がないですし、ThinkingRockには「開始日時」の設定項目がありますが、それを使って今すぐ実行可能なタスクだけにフォーカスをすることが出来ないに等しかったので役に立ちませんでした。


これで安心!あとは例大祭のことは忘れてソースでも書きましょうか!


あとは完全に主観になってしまいますが、「使っていて楽しい」というのも非常に重要なポイントです。
はじめてのGTD ストレスフリーの整理術にも、「使っていて楽しいツールを利用するとタスクを管理するのが楽しくなる」といった旨の記述がありますし、使っていて楽しくて仕方がないと思うのであれば少々高くてもお金を出す価値はあると思います。


ただしOmniFocusは欠点も無茶苦茶多いです。まず第一にMac + iPhoneの組み合わせ以外では利用不可能。これだけで使う人を選んでしまいます。
残念ながら今後Windowsなど他のプラットフォームで利用可能になることも永遠にないと思います・・・(開発元のOmni社がMac製品しか作らないので)。
そしてべらぼうに高い値段。「無料でいくらでもネット上に落ちているタスク管理ツールに1万円+αも出せるかどうか」というところがポイントになりそうです。
この「Mac信者のMac信者によるMac信者のためのGTDツール」と言わんばかりのハードルの高さを乗り越えられるかどうかが肝だと思います。実際にはMac信者とか全く関係なしに非常に良いツールなのですが・・・


■まとめ
初めての人、複数台マシンを持っている方、Linuxユーザーの方、無料だけれどもしっかり使える優良ツールをお探しの方にはThinkingRock+αを、 手持ちのコンピュータが全てMacで携帯はもちろんiPhoneという方、人とは違うものを使ってみたい方、「次にいますぐ行動可能なアクション」だけが目の前に出てくる快感を味わってみたい方にはOmniFocusをお勧めします。


■おまけ:日本語版のOmniFocusはイマイチ
何がイマイチって、まず翻訳が何かおかしい。所々見ていてへんな単語が・・・フォマーットとか・・・
そして何より、日付の指定が日本語じゃないとできないのです。


英語版だとこう指定できます。「next sun@8」で来週の日曜日の8時にセットできます。
「tod」だけで今日の0時、「tom」で明日0時、「now」で今この瞬間にセット。
便利!


ところが日本語版だとこうなります。ちょっと!なんだよ「翌週日曜日」って!
「明日」とか「今日」とか「今」って入力面倒じゃないの!
こんなところまで余計なローカライズしなくていいよ!!

と言うことで私は英語版を強くお勧めいたします。
英語版のダウンロードは、http://www.omnigroup.com/applications/omnifocus/ の画面右端、



こちらをクリックすると英語版がダウンロードできますよ。

2011年12月7日水曜日

CocoaPods に対応していないライブラリを集めた自分用リポジトリを作る方法

この記事はiOS Advent Calendar 2011の7日目の記事になります。ということでもうすぐクリスマスですね。クリスマスプレゼントの準備はお済みですか?まだの方はちょっとオシャレに、今年のプレゼントをCocoaPodsでご用意してみてはいかがでしょうか?


■ご存じ、ないのですか!?

さて念のためCocoaPodsについておさらい。要するにiOS/OS X用のmavenです。以上。細かい点については以下の記事が詳しいのでそちらをご参照ください。っていうかMac Dev JP Advent CalendarとネタがもろかぶりこのCocoaPodsを使うと今まで大変面倒くさかったライブラリの管理が嘘のように簡単になります。たとえば、新しいプロジェクトを始めるときに、
  • 通信したいからASIHTTPRequestを使おう
  • APIのレスポンスがJSONだからJSONKitも必要だな
  • DBにはCore Dataを採用したいから、MagicalRecordも欲しいな
  • Blocksバリバリ使うからBlocksKitは常識だよね
と思ったら、さくっと以下のような設定ファイル(Podfile, mavenで言うところのpom.xml)を書いてやれば、あとはCocoaPodsが指定されたライブラリを取ってきてビルド設定までやってくれるわけです。
platform :ios

dependency 'ASIHTTPRequest' ,'~> 1.8'
dependency 'JSONKit'        ,'~> 1.4'
dependency 'BlocksKit'
dependency 'MagicalRecord'
ARCあり・なしのライブラリを混ぜても全く問題ありません。素晴らしい!!


■公開なんて、あるわけない

とまぁ実に素晴らしいツールなのですが、問題もあります。
  • つい最近できたばかりのツールなので、対応しているライブラリが少ない
  • 対応しているライブラリでも、もともと依存関係処理をするという文化があまりなかったせいか、一部のライブラリ(Reachabilityとか)が内包された状態で出回っていたり、バージョンタグが一つや二つしか付いていないので上手くバージョン管理が出来ない(しかも極端に古かったりバグがあったり、さんざん)
  • CocoaPods自体が開発途中ということもあり、機能がどんどん追加されているようなのだがドキュメントが追いついていない
例を挙げると以下の画像のような感じで、バージョンが一つしかなかったり、あるんだけれど飛んでいたりなどなど。要するに自分の使いたいコードがCocoaPodsの中央リポジトリで管理されていないということがままあります。



■俺達は、自分たちでpodspecを用意することを......強いられているんだ!

ありがたいことに、CocoaPodsには中央リポジトリ以外の任意のリポジトリをライブラリ管理用のリポジトリとして追加する機能があります。この機能を使って、自分で対応していないライブラリのpodspecファイルを書いて、CocoaPodsで使えるようにすることができます。またCocoaPods 0.3.0以降であれば、設定ファイルに直接自分の好きなライブラリのpodspecを書くこともできるみたいです。

まず最初のステップはpodspecを書くことです。今回は例としてMKNetworkKitというライブラリのバージョンv0.8a用のpodspecを書いてみることにします。

# まずは対象のリポジトリをcloneしてくる
# ここでは相手のリポジトリを直接使ってますが、github上でforkして、そっちを使うようにしてもいいです。forkしたほうが自分で自由にコードに改変を加えたりtagを打ったりできますのでよいかも。
git clone https://github.com/MugunthKumar/MKNetworkKit.git
# 移動
cd MKNetworkKit
# 対象のバージョンにHEADを移動します
git reset --hard v0.8a
# podspecファイルのひな形を出力します
pod spec create MKNetworkKit
これでMKNetworkKit.podspecファイルが出力されますので、今度はこのファイルを書き換えます。先ほどcloneしてきたライブラリのソースコードとプロジェクト設定を見ながら、必要なソースコード、必要なリソース、不要なファイル、ライブラリやフレームワークなどのビルド設定を考えて、適切な設定を用意しなければなりません。
今回はこんな感じで書きました:
Pod::Spec.new do |s|
  s.name     = 'MKNetworkKit'
  s.version  = '0.8a'
  s.license  = 'MIT'
  s.summary  = 'Full ARC based Networking Kit for iOS 4+ devices'
  s.homepage = 'https://github.com/MugunthKumar/MKNetworkKit'
  s.author   = { 'MugunthKumar' => 'mknetworkkit@mk.sg' }
  s.source   = { :git => 'https://github.com/MugunthKumar/MKNetworkKit.git', :tag => 'v0.8a' }

  s.source_files = 'MKNetworkKit/*.{h,m}', 'MKNetworkKit/Categories/*.{h,m}'
  s.clean_paths  = 'MKNetworkKitDemo', '*.xcodeproj', 'sample.JPG'
  s.frameworks   = 'CFNetwork'
  s.requires_arc = true

  s.dependency 'Reachability', '~> 2.0'
end
大事なのはsource, source_files, frameworks, requires_arc, dependencyぐらいです。あとは自分しか使わないならでたらめでかまいません。
sourceは:tagの指定の代わりに:commitでコミットのハッシュ値を指定することもできるみたいです。
このpodspecファイルの記法、やたらとたくさんある上にドキュメントがあまりないので、私は結局公式リポジトリのpodspecを探して見よう見まねで書きました。以下、参考にした物を列挙します。
https://github.com/CocoaPods/Specs
https://github.com/CocoaPods/Specs/blob/master/ASIHTTPRequest/1.8.1/ASIHTTPRequest.podspec
https://github.com/CocoaPods/Specs/blob/master/ASIWebPageRequest/1.8.1/ASIWebPageRequest.podspec
https://github.com/CocoaPods/Specs/blob/master/BlocksKit/0.5.0/BlocksKit.podspec
https://github.com/CocoaPods/Specs/blob/master/BlocksKit/0.9.0/BlocksKit.podspec
https://github.com/CocoaPods/Specs/blob/master/SSToolkit/0.1.2/SSToolkit.podspec
https://github.com/CocoaPods/Specs/blob/master/Kiwi/1.0.0/Kiwi.podspec
https://github.com/CocoaPods/Specs/blob/master/MGSplitViewController/1.0.0/MGSplitViewController.podspec
使用するリソースファイルも指定出来るみたいです。
https://github.com/CocoaPods/Specs/blob/master/SVProgressHUD/0.5/SVProgressHUD.podspec
巨大なのになるとこんなのも書けるみたいです。
https://github.com/CocoaPods/Specs/blob/master/RestKit/0.9.3/RestKit.podspec
https://github.com/CocoaPods/Specs/blob/master/Nimbus/0.9.0/Nimbus.podspec

書き終わったら、書いたpodspecファイルに問題がないかをチェックします。
pod spec lint MKNetowrkKit.podspec
何か問題があれば何かエラーが出ます。修正しましょう。何も無ければ何も出ません。
問題が無くなったらひとまずpodspecファイルについては完成です。次はこのpodspecファイルを置くリポジトリをgithubを使って用意します。github上に適当な名前でリポジトリを作りましょう。私は今回 https://github.com/akisute/Specs というリポジトリを作りました。
リポジトリを作ったら、先ほど作ったpodspecファイルを、以下の命名規則に従ってリポジトリの中に配置します:
/podspecのs.name/podspecのs.version/先ほど作ったpodspecファイル
たとえば今回の例では:
/MKNetworkKit/0.8a/MKNetworkKit.podspec
という名前で配置する必要があります。私が試した際は、間違ってると正しくpodspecファイルを認識してくれませんでした。ファイルを配置したらこのリポジトリをgithubにpushします。

さてこれでpodspec用のリポジトリが出来ましたので、今度はCocoaPods側の設定を行います。以下のコマンドを実行します:

pod repo add myrepo リポジトリのURL
これでmyrepoという名前でリポジトリが登録されます。 ~/.cocoapods/ 以下を覗いてみると、確かに myrepo という名前のリポジトリが追加されているはずです。

あとは普通にCocoaPodsを使うのと同じ要領で、Podfileを書いて、pod installすればうまくいくはずです。・・・といいたいところなのですが、一発でうまくいくことはまれで、たいていpodspecファイルの書き方に問題があったりとか、pod化したい対象のライブラリのコードに問題があってビルドが通らないのが普通です。そこで以下のようなワークフローになります。
  1. コードに問題があるなら、コードをforkして自分の思うように書き換えてpush
  2. podspecファイルを修正して自分のpodspec用リポジトリにpush
  3. 組み込みたいプロジェクトのPodsディレクトリ、Podfile.lockファイル、生成されたxcworkspaceを削除。
  4. 再度 pod install MyProject.xcodeproj を実行。
  5. ビルド。
  6. 問題があれば1. に戻る。
うん、これは素人にはお勧めできない。

しかしながらこのCocoaPodに対応するライブラリが増えていけば、iOSの開発はずいぶんと楽になるはずです。ということで積極的に使っていきたいと思います!

2011年8月5日金曜日

自分流 View Controllerの作り方 その2



その1はこちら

ぼくのかんがえたさいきょうのせっけいです
主に以下の書籍に影響受けまくりであります
0321127420Patterns of Enterprise Application Architecture (Addison-Wesley Signature Series (Fowler))
Martin Fowler
Addison-Wesley Professional 2002-11-05

by G-Tools
4798116831レガシーコード改善ガイド (Object Oriented SELECTION)
マイケル・C・フェザーズ ウルシステムズ株式会社
翔泳社 2009-07-14

by G-Tools


図を適当に補足
ViewWrapperは既存のすでにあるどうしようもない設計のViewを何とか救いたいときに非常に便利、Wraper / Decoratorパターンを適用してボタンのタップを奪い取ってViewHelperに流すみたいな役目をする
ViewHelperは簡単に言うならUITableViewControllerのdelegate, datasourceだけを担うオブジェクトみたいな感じ。要するにView専用のドメインロジックを書くオブジェクト
Viewの表示を制御するドメインロジックが途方もなく大きくてViewControllerに納めるのが不可能になってしまったときに超便利

Serviceは準ドメインロジックだと思っている、たいていの場合セミシングルトンみたいにする(通信が絡むので複数画面をまたいで使うことがほとんど)
Androidの場合はここ、普通にServiceクラスでいいんじゃないでしょうかね

Managerはドメインロジックというよりもプロセス外へのリソースアクセスを行うためのクラスというイメージ、個人的にはゲートウェイみたいな感じ
  • NSFileManager
  • NSUserDefaults
  • KeychainAccessManager
  • InAppPurchaseManager
  • APIConnectionManager
Modelはドメインモデルです、ほとんどの場合はCoreDataのNSManagedObject。用がないときでもCoreData使っておけ(超便利)
と思ってましたがCoreDataのモデルは特にN:N関係を正しく扱わないと簡単に問題が発生してしまいますので、安易に採用すると危険かも。

Task, Operationってのは非同期で実行されていく特別なドメインロジックのイメージ。要するにAPI通信みたいなもんです
API通信を複数束ねて使ったりとか並列実行したりとかの制御が絶対必要になるのでそういうときに使う via @monjudoh
// MyTaskが終わったらMySuperTaskを実行して、それが終わったらさらにMySuperDuperTaskを連続して実行したい
// 終わったらselfに通知させたい
Task *root = [[[MyTask alloc] init] autorelease];
root.nextTask = [[[MySuperTask alloc] init] autorelease];
root.nextTask.nextTask = [[[MySuperDuperTask alloc] initWithId:100] autorelease];
root.delegate = self;
[root start];

2015年12月17日木曜日

レガシーな Objective-C プロジェクトを Swift なプロジェクトに変換する

ここで言うレガシーなObjective-Cプロジェクトの定義とは
  • iOS 7時代 (Xcode 5) より前に作成されたプロジェクトである
  • Swiftのコードを一行も含んでいない
  • IOS_DEPLOYMENT_TARGETが8.0よりも小さい (7.xをサポートしている)
とします。

こんな由緒正しいiOSのプロジェクトを未だにメンテしている人もなかなかいないのかと思いますが、もしいらっしゃいましたらそんな方のためにSwiftなプロジェクトに変換していく方法をメモしておきます。

■前提条件

まずIOS_DEPLOYMENT_TARGETを8.0以上にしましょう。IOS_DEPLOYMENT_TARGETを8.0以上にすることでdynamic frameworkおよびclang moduleが使えるようになるため、Objective-CとSwiftの間の垣根が非常に低くなります。

■ケース1: 根っこはObjective-Cのまま、Swiftのファイルを追加

普通にSwiftのファイルを追加したらXcodeが上手いことしてくれます。具体的には

clang moduleを有効にしてdyldを使えるようにする設定
CLANG_ENABLE_MODULES = YES;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";

Swift化されるターゲットごとにブリッジングヘッダを作成
SWIFT_OBJC_BRIDGING_HEADER = "myapp/myapp-Bridging-Header.h";

Releaseビルド以外に対して最適化レベル設定
SWIFT_OPTIMIZATION_LEVEL = "-Onone";

が自動的に行われるので後はせいぜいブリッジングヘッダに使ってるObjective-Cのヘッダを書き込む程度ですみます。

■ケース2: 根っこからSwiftにする

「根っこからSwift」とは要するにmain.m(C言語のmain関数)を持たないアプリにしたいということですが、これも実は思ったより簡単にできます。
  • AppDelegate.swiftファイルを作成する
  • 既存のObjective-Cで書かれたAppDelegateを継承て新しくswiftなAppDelegateを作成して、@UIApplicationMainアノテーションを付ける
  • main.mを消す
参考: http://stackoverflow.com/questions/31309249/how-to-convert-objective-c-appdelegate-to-swift

たったのこれだけでプロジェクトの根っこがSwiftな状態になります。簡単ですね。

■さらにSwift化をすすめる

とりあえずAppDelegate.swiftを作ることで根っこはSwiftになるのですが、せっかくだからAppDelegateをまるごとSwiftにしてしまいたいものです。ここでもし、
  • UIWindowをAppDelegate内部でカスタマイズしている
  • UIApplicationをmain.mでカスタマイズしている
などの場合はそのままだとSwiftで対応するのがちょっと面倒です。

まずUIApplicationについてはInfo.plistのNSPrincipalClassを変更することで任意のカスタムクラスに差し替える事が可能です。参考はこちら: http://stackoverflow.com/questions/31642956/how-to-detect-all-touches-in-swift-2

UIWindowについてはMain.storyboardを使わなければ勝手にUIWindowを差し込まれることはなくなるので問題ないのですが、それでは困ると言う場合はawakeFromNibとかを実装してその中で
@UIApplicationMain
class MyAppDelegate: NSObject, UIResponder, UIApplicationDelegate {
  var window: UIWindow?
  override func awakeFromNib() {
    super.awakeFromNib()
    guard let defaultWindow = self.window else {
      fatalError("Something is wrong")
    }
    let window = MySuperDuperWindow()
    window.rootViewController = defaultWindow.rootViewController
    self.window = window
  }
}
とかすれば動くと思います。多分。