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

2014年6月9日月曜日

既存の Objective-C のメソッド引数の Swift 上での扱われ方を調べてみた

前置き

こちらの記事には2014/06/09現在、公式にはリリースされていないiOS8プレリリースドキュメントへのリンクが含まれます。iOS8にて新しく追加された内容には一切触れておらずAppleとのNDA規約にも違反するものではないという認識ですが、場合により予告なく削除する可能性があります。予めご了承ください。

本題

iOS8プレリリースドキュメントを眺めていて気になったのですが、ほとんどのCocoaのメソッドの引数に!がついています。例えばNSKeyValueObservingプロトコルのaddObserver:forKeyPath:options:context:メソッドのシグネチャは以下のようになっています。

func addObserver(_ anObserverNSObject!,
      forKeyPath keyPathString!,
         options optionsNSKeyValueObservingOptions,
         context contextCMutableVoidPointer)

第1引数と第2引数に!がついているのがわかると思います。
更にもうひとつ気になったのが、このメソッドの第3・第4引数です。これらは元々0やNULLポインタを渡すことができる引数だったのですが、見ての通り引数が?で宣言されておらず、そのままnilを渡してしまうとエラーになってしまうように見えます。

直感的に考えると、

  • !がついている引数は呼び出し時に強制的にunwrapされるので、nilを渡してはいけない、必須引数なのではないか?
  • !がついていない第3第4引数はnilが渡せないのではないか?

という風に思うのですが、実際には

  • !がついている引数にnilを渡すことができる。さらにnilを渡してもランタイムでクラッシュしない。
    • 今回の例のaddObserver:forKeyPath:の場合はクラッシュしますが、これは元々のAPIがそうだったからで、例えば他のCocoaのAPIで!がついているものでnilを渡しても問題ないものが多数存在します。
  • !がついていないNSKeyValueObservingOptions, CMutableVoidPointerにnilを渡してもコンパイルエラーにすらならず、何の問題もなくそのまま動作する。

このような私の理解とは正反対の挙動をします。大変気になったので調べてみることにしました。

!がついている引数の謎

これについてはそもそも私のSwiftに対する理解が完全に間違っていました。正しい理解は以下のとおりです。

  • 無印型 - nilを渡すことができない。nilになる瞬間が一瞬もない。これこそがnilを渡せない必須引数である。
  • ?型 - Optional型であり、nilを扱うことができる。これについては問題ない。
  • !型 - ImplicitlyUnwrappedOptionalという特殊なOptional型であり、Optional型なのでnilを扱うことができる。すなわち!が付いている引数については任意引数でありnilを渡すことができる。
    • !も?も演算子でも命令でもなく型であり、!や?をつけることで対象を型に包んでいる、と考えれば良いと思います。
    • !と?の違いはメンバにアクセスした際に暗黙的かつ強制的に元の型にアンラップされるか、そのままOptionalとして扱われるかの違いだけです。

ではなぜCocoaのAPIは引数で?ではなく!を使ってnilを受け取れるようにしているのでしょうか?これは引数を受け取った後に、その引数にアクセスした場合の挙動がおそらく影響しているのではないかと思われます。以下の例を見てください。

こちらのコードですが、safeSwim()メソッドが旧Objective-Cと完全に同じ挙動を示します。これは以下の様な理由によるものです。

  • self.name(!型)が?型によってラップされる。
  • self.name?の.descriptionにアクセスする。このとき?型によってラップされているので、self.nameがnilであれば何も起こらず、self.nameがnilでなければまず?型からアンラップされ、次に強制的に!型からアンラップされ、その結果通常通りdescriptionが呼び出される。
  • 通常だと?型の返り値は?型にラップされてしまうが、self.nameが!型なので強制的にアンラップされ?型ではなく素のString型を返すことができる
    • ここだけよく理解できてないです・・・

間違っていたらすみません(´・_・`)

とにかくこれこそが旧Objective-CのAPIについて!で型が表現されている理由ではないかと考えています。

!がついていないのにnilが渡せる引数の謎

こちらの謎はカラクリがわかってしまえば簡単です。前回の記事をご覧になった方はお気づきになったかもしれませんが、これらの型はAppleが__convertion()メソッドをNilTypeに対して追加しているため、nilを問題なく扱うことができます。

まとめ


  • !が付いている引数は?と同様にnilを渡しても良い引数である。nilを渡せない必須型は何もついていない引数のみである。
  • !はImplicitlyUnwrappedOptionalという特殊なOptional型であり、アクセス時にOptionalではなく強制的に元の型を返す点がOptionalと異なる、と考えれば理解しやすい。
  • 変数をすべて!で定義し、メンバアクセス時に常に?をつけるようにすると、どの変数にもnilを渡すことができ、実行時にnilがあればそこで実行がスキップされ、さらに返り値もOptionalではない通常の型にできるため、旧Objective-Cとほぼ同じ挙動になる。
    • ただしSwiftで新しく用意されているAPIを見る限りnilを扱う必要がある箇所については可能な限り!ではなく?を引数や返り値に使っているように見えるため、新しくSwiftで書く箇所については!を乱用するべきではないと思われる。
  • Cocoaの用意している型のうち、元々nilやNULLや0を渡すことができた型については、NilTypeに__conversion()メソッドが追加されているので、そのままnilを渡すことができる。

2011年11月19日土曜日

iOS 5でのSSL/TLS通信時にエラーが発生した場合のエラーコードの調べ方

iOS 5より新たにSecurity.frameworkというフレームワークが追加されました。このフレームワークはAppleが実装したSSL/TLS用のライブラリで、iOS 5よりCFNetwork系のクラス(NSURLConnectionなどの内部実装にも使われています)のSSL/TLS通信時に使われるようになったみたいです。

http://developer.apple.com/library/ios/#releasenotes/General/WhatsNewIniPhoneOS/Articles/iOS5.html の一番下にちょこっとだけ説明があるので、見てみると、
Security
The Security framework (Security.framework) now includes the Secure Transport interfaces, which are Apple’s implementation of the SSL/TLS protocols. You can use these interfaces to configure and manage SSL sessions, manage ciphers, and manage certificates.

For information about the Secure Transport interfaces, see the SecureTransport.h header file of the Security framework.
うむ、ドキュメント無いからヘッダ読めとはなかなか適当ですね。

普通にアプリを作っている最中にこのSecurity.frameworkを直接触る必要は全くないのですが、問題はこのSecurity.frameworkを使っているAppleのframeworkが通信時にSSL/TLS絡みのエラーを吐いたときに発生します。このとき、それらのフレームワークは以下のようなNSErrorを返してきます。
Error Domain=NSOSStatusErrorDomain Code=-9830 "The operation couldn’t be completed. (OSStatus error -9830.)
NSErrorの読み方はErrorDomainに指定されている文字列に応じてエラー番号が定義されているドキュメント/ヘッダファイルを見るというものなのですが、このNSErrorはNSOSStatusErrorDomainのErrorDomainを指定しているにも関わらず<CarbonCore/MacErrors.h>にエラー番号の定義が全く書いていないのです!

色々原因を調べた結果、以下のQ&Aが見当たりました。
http://developer.apple.com/library/mac/#qa/qa1499/_index.html
どうやらSecurity.framework絡みのエラーはNSOSStatusErrorDomainを使っているがMacErrors.hではなく<Security/SecureTransport.h>などに定義が書いてあるみたいなのです。見てみると・・・
/*************************************************
 *** OSStatus values unique to SecureTransport ***
 *************************************************/

/*
    Note: the comments that appear after these errors are used to create SecErrorMessages.strings.
    The comments must not be multi-line, and should be in a form meaningful to an end user. If
    a different or additional comment is needed, it can be put in the header doc format, or on a
    line that does not start with errZZZ.
*/

enum {
 errSSLProtocol    = -9800, /* SSL protocol error */
 errSSLNegotiation   = -9801, /* Cipher Suite negotiation failure */
 errSSLFatalAlert   = -9802, /* Fatal alert */
 errSSLWouldBlock   = -9803, /* I/O would block (not fatal) */
    errSSLSessionNotFound   = -9804, /* attempt to restore an unknown session */
    errSSLClosedGraceful   = -9805, /* connection closed gracefully */
    errSSLClosedAbort    = -9806, /* connection closed via error */
    errSSLXCertChainInvalid  = -9807, /* invalid certificate chain */
    errSSLBadCert    = -9808, /* bad certificate format */
 errSSLCrypto    = -9809, /* underlying cryptographic error */
 errSSLInternal    = -9810, /* Internal error */
 errSSLModuleAttach   = -9811, /* module attach failure */
    errSSLUnknownRootCert  = -9812, /* valid cert chain, untrusted root */
    errSSLNoRootCert   = -9813, /* cert chain not verified by root */
 errSSLCertExpired   = -9814, /* chain had an expired cert */
 errSSLCertNotYetValid  = -9815, /* chain had a cert not yet valid */
 errSSLClosedNoNotify  = -9816, /* server closed session with no notification */
 errSSLBufferOverflow  = -9817, /* insufficient buffer provided */
 errSSLBadCipherSuite  = -9818, /* bad SSLCipherSuite */
 
 /* fatal errors detected by peer */
 errSSLPeerUnexpectedMsg  = -9819, /* unexpected message received */
 errSSLPeerBadRecordMac  = -9820, /* bad MAC */
 errSSLPeerDecryptionFail = -9821, /* decryption failed */
 errSSLPeerRecordOverflow = -9822, /* record overflow */
 errSSLPeerDecompressFail = -9823, /* decompression failure */
 errSSLPeerHandshakeFail  = -9824, /* handshake failure */
 errSSLPeerBadCert   = -9825, /* misc. bad certificate */
 errSSLPeerUnsupportedCert = -9826, /* bad unsupported cert format */
 errSSLPeerCertRevoked  = -9827, /* certificate revoked */
 errSSLPeerCertExpired  = -9828, /* certificate expired */
 errSSLPeerCertUnknown  = -9829, /* unknown certificate */
 errSSLIllegalParam   = -9830, /* illegal parameter */
 errSSLPeerUnknownCA   = -9831, /* unknown Cert Authority */
 errSSLPeerAccessDenied  = -9832, /* access denied */
 errSSLPeerDecodeError  = -9833, /* decoding error */
 errSSLPeerDecryptError  = -9834, /* decryption error */
 errSSLPeerExportRestriction = -9835, /* export restriction */
 errSSLPeerProtocolVersion = -9836, /* bad protocol version */
 errSSLPeerInsufficientSecurity = -9837, /* insufficient security */
 errSSLPeerInternalError  = -9838, /* internal error */
 errSSLPeerUserCancelled  = -9839, /* user canceled */
 errSSLPeerNoRenegotiation = -9840, /* no renegotiation allowed */

 /* non-fatal result codes */
 errSSLPeerAuthCompleted     = -9841,    /* peer cert is valid, or was ignored if verification disabled*/
 errSSLClientCertRequested = -9842, /* server has requested a client cert */

 /* more errors detected by us */
 errSSLHostNameMismatch  = -9843, /* peer host name mismatch */
 errSSLConnectionRefused  = -9844, /* peer dropped connection before responding */
 errSSLDecryptionFail  = -9845, /* decryption failure */
 errSSLBadRecordMac   = -9846, /* bad MAC */
 errSSLRecordOverflow  = -9847, /* record overflow */
 errSSLBadConfiguration  = -9848, /* configuration error */
 errSSLLast     = -9849, /* end of range, to be deleted */

    /* DEPRECATED aliases for errSSLPeerAuthCompleted */
    errSSLServerAuthCompleted   = -9841, /* server cert is valid, or was ignored if verification disabled DEPRECATED */
 errSSLClientAuthCompleted   = -9841,    /* client cert is valid, or was ignored if verification disabled; reusing error as you can only be client or server - DEPRECATED */

};
ばっちり書いてますね。これでエラーの原因も特定できます。今回のケースはerrSSLIllegalParamだったみたいですね。

2011年7月18日月曜日

BPStudy #46 での発表資料 ViewController の作り方を公開してます

かなり旧聞になってしまいますが、 BPStudy #46 で話したときの資料をpreziにアップしてますので、ご覧いただけます。
http://prezi.com/7b_joy2lcfil/bpstudy-46/

発表時にお見せしたソースコードはちょっと公開出来なかったのでこの資料だけだとイマイチよくわからないのですが、要するに
  • View Controllerにサービスを書かないようにしてサービスレイヤを分離しろ
  • View Controllerの描画コードは、アウトレットの生成、アウトレットのデータ更新、アウトレットのレイアウトの3つに分離しろ
って言う感じのお話をしました。

すげー当たり前のことしか書いてなくて恐縮なのですが><
実際、人様のコードをメンテしたりバージョンアップしたりするお仕事をいただいていると、ほとんど全てのコードがこの問題に引っかかっていて非同期通信化すると動かないとかちょっとView Controllerの呼び出され方を変えると動かないとかあって、iOS開発者の皆さんがこういうところに気を配ってコードを書いていただければなぁそして私が楽できればなぁという思いで書いてみた次第であります。

2009年11月8日日曜日

表示専用のカレンダーJSが欲しい

日付を選択するためのカレンダーJSはよく見るのですが、表示に特化したものは余り見ないので。たとえばこんな感じでjQueryのプラグインみたいに使えるカレンダーが欲しいのです。
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Calendar object test</title>
<script type="text/javascript" src="js/jquery-1.2.6.min.js" type="text/javascript"></script>
<script type="text/javascript" >
$(function(){
// add a new text and set its style
$("calendar")
.from(2009, 10, 30)
.to(2009, 10, 31)
.text("Halloween")
.color("#ffcccc")
.style("bold")
.backgroundcolor("#ffddcc");
});

// calendar event handling
// use customized Event object which has "day" and "from, to" attr
$("calendar").click(function(e){
day = e.day
this.day(day).text("clicked")
});
$("calendar").select(function(e){
from = e.from
to = e.to
this.from(from).to(to).text("selected")
});
</script>
</head>
<body>
<div id="calendar">Calendar will be shown here</div>
</body>
</html>
どうでしょ?探せばあると思うのですが・・・

2009年2月15日日曜日

jweekyforecast近況報告(2009/02/15)


ようやく予報区のマスターメンテができるようになりました。
天気予報区のデータをアプリケーションに登録して、それをAPIを通じてiPhoneに引き渡すというような処理を行おうと思っています。


天気予報区には大きく分けて一次細分区域と二次細分区域がありますが、今のところ一次細分区域しか対応していません。二次細分区域を対応させようと思ったら親子関係を持たせないとなぁ。

何より問題はこのマスターメンテが死ぬほど面倒くさいことかなぁ・・・と思います ;(

さてさて、次はAPIを作らないとシステムとして成り立たないですね。

2009年9月24日木曜日

アルカトラズなう

1日目に引き続き、2〜3日目。


■2日目
朝起きてまずは近所の散策。Whole Food MarketとWalgreensを発見。

ジャパンタウンへ行ってみるがあまりにも閑散としていておもしろくないので即撤退。伊藤園の充実野菜が売ってあった。

おもむろに目の前を走っていたMUNIバスに興味本位で乗ってみたら降り方がわからず大変苦労した。運ちゃんに降り方を聞いてようやく降りたらなぜかシビックセンターについた。

シビックセンター見学

ダウンタウンへ移動

ダウンタウン見学、Apple StoreでWifi電波を借りたりFallout 3の実物大Power Suitにつられて地元のゲーム店によってみたり。DSとWiiのソフトが日本と全然違う。ほかはにたりよったり

あ、Walgreensにおーいお茶(濃い味)売ってる、$1.69

ケーブルカーでフィッシャーマンズワーフへ

Pier39で飯、ようやくこっちのレストランの作法とか文化的背景がわかった

Pier33で明日のアルカトラズ行きのチケットを入手

適当にバスに乗ったらゴールデンゲートブリッジについた

ゴールデンゲートブリッジを見学。風が糞強い!めちゃ揺れる!落ちる!!ロードバイクにひき殺される!!死ぬ!!><

ゴールデンゲートウェイ公園に移動。広すぎる上に車道があって公園というイメージがしない。地元の田舎道みたいな雰囲気。

なぜかツタンカーメン展をやっていたので入ってみた

やばい、ツタンカーメン展ヤバい、超おもしろい!
日本で一般的に開催されている展覧会と違って見せ方が上手い!展示に移る前に、決められた人数ずつ展示室前の墳墓を模した暗室に誘導され、そこでディスカバリーチャンネルみたいなかっこいいムービーを見る。凄い盛り上がる!ムービーが終わったらいよいよ中へ、入ると同時にツタンカーメンの像がお出迎え!とこんな具合にストーリー性があっておもしろい。美術品を順番に置いて説明テキストおいて終わりじゃなくて、映像とか部屋の構造まで使ってストーリー性を持たせているのが、まるでディズニーのアトラクションみたいだと思った。

2時間もたってしまって19時になったのだが、外がまだ明るい。よく考えたらサマータイムのおかげでまだ日本時間では18時相当なのだった。サマータイムに生まれて初めて感謝した。

近所のWhole Food Marketで晩ご飯を買って食う。普通に食える。おいしい。誰だアメリカの飯が不味いとか言ったやつは。自分にはこれでちょうどいいぐらいだ。


■3日目
朝9時半からアルカトラズへ移動

昼14時半までアルカトラズ観光。とにかくすばらしいの一言につきる。島から見えるサンフランシスコの町並みも、刑務所跡も、とても綺麗で歴史が感じられる。ここの歴史なんてたかだか100年とちょっとしかないはずなのだが、数百年歴史があるはずの地元山口・錦帯橋よりも歴史が感じられるのは、やはり見せ方が上手いのと、音声ツアーガイドがあまりにも秀逸であるおかげだろうなぁと思った。アルカトラズ刑務所の音声ツアーガイドは本当にすばらしい!ディスカバリーチャンネルの世界を自分の足で歩いているような雰囲気がたまらない!
※帰ったら写真と動画うpします。

フィッシャーマンズワーフで遅い昼飯。ただのハンバーガー(10ドル)、これが凄くおいしい。ポテトもマックやバーキンの安物とは全然違って、30分たってもおいしい!このぐらいなら、なるほど料理と読んで差し支えないなぁ。

近所のWhole Food Marketで晩ご飯と朝ご飯を買いだめ

終了


作法や町歩きの仕方が身についてきて、英語も耳に慣れ聞き取れるようになり、おもしろくなってきました。明日はいよいよGoogleとAppleの見学に行ってきます。余裕があればStanfordも!

2014年8月12日火曜日

UINavigationController.interactivePopGestureRecognizer の挙動を魔改造して変えてみる

注意: 今回の記事は久しぶりの誰得記事です。こちらで紹介されている手法を用いるにはプライベートAPIを操作する必要があるため、おそらく審査に通りません。じこせきにんでお願いします。

iOS 7からUINavigationControllerにinteractivePopGestureRecognizerというプロパティが新たに追加されました。これは「ナビゲーションコントローラの左端から右にスワイプすると前の画面に戻る」機能を司るgesture recognizerで、同じくiOS 7から追加されたUIScreenEdgePanGestureRecognizer(画面端からのみ反応するPan Gesture Recognizer)とUIViewControllerInteractiveTransitioning(ViewController間の画面遷移時に、ユーザーのインタラクションを追加する。例えば画面半分までスワイプしたら50%画面遷移するとか)の仕組みで実装されています。

ここで、例えばこの「ナビゲーションコントローラの左端から右にスワイプすると前の画面に戻る」機能について、見た目はそのままがいいんだけど左端からしか反応しないのが気に入らないから画面の中央でも反応するようにしたいという要望があったとします。自前で実装してもよいのですが、やはりApple純正の動きが良いですよね。

そこで以下のページで紹介されている黒魔術を使ってみたいと思います。
http://blommegard.se/blog/2014/01/31/a-take-on-custom-transitions-with-uinavigationcontroller/
こちらのページにUINavigationController内部でどのようにinteractivePopGestureRecognizerが実装されているかという解説があります。

  • 先述の通り、UIScreenEdgePanGestureRecognizerのインスタンスが使用されている
  • targetは_UINavigationInteractiveTransition型のオブジェクト、調査の結果_cachedInteractionControllerという隠しプロパティがそれに相当することがわかった
  • actionは@selector(handleNavigationTransition:)

ここまでわかったので、あとはtarget/actionを自分で生成したUIGestureRecognizerにセットしてやれば、標準の動きをジャックすることができるというわけです。今回はUIPanGestureRecognizerを使用しました。



このコードを実行してみると確かに画面中央からでもいつものスワイプジェスチャで戻る挙動が可能になっています。やりましたね\(^o^)/

2015年9月7日月曜日

Apple Watch アプリの実機インストールが失敗する時のメモ

Apple Watchアプリの実機インストールが失敗する際にチェックする事項をまとめてみました。Apple Watchアプリが実機インストールに失敗する原因は多岐にわたり、ここにまとめてある内容だけではとても網羅しきれませんが、何かの参考になれば幸いです。

以下、すべてwatchOS 1での結果です。watchOS 2ではまた何かが変わっているかもしれませんが、基本的なところは同じかと思います。

最初にやること

いずれの場合でもまず最初に必ずやることとして、XcodeのDeviceウィンドウを開いて実機のログを確認するようにしましょう。このログにApple Watchアプリをインストールするときのログも全て残るようになっていますので、一番の手がかりになります。

症状: 「インストールに失敗しました」ダイアログ

このダイアログが表示されるときは、殆どの場合iPhone本体にインストールされるApple Watch App ExtensionとApple Watch上にインストールされるApple Watch Appの両方がインストールに失敗していることが多いです。

失敗の理由ですが私が遭遇したものの中だとダントツ多かったのがコードサインエラーです。どうしてもインストールが成功しない場合は、Xcodeのビルド設定などから使用するProvisioning Profileを明示的に指定してやるとうまくいくことが多かったです。

このとき指定するProvisioning Profileは、Debug向けビルドであれば標準のデバッグ用のiOSTeam Provisioning Profile: *を使うか、または専用に用意したcom.example.myapp.watchkitappないしcom.example.myapp.watchkitextensionのようなApp IDを使うプロビジョニングを選びましょう。Release向けビルドの場合は必ず専用に用意したApp IDを使うプロビジョニングを選択する必要があるので、面倒でも必ずApple Watchアプリを作る際には新しくApple Watch App Extension用のApp IDとApple Watch App用のApp IDを作るようにしましょう。

症状:「インストール中です」が終わらない

このようなケースの場合は、どうやらiPhone本体にインストールされるApple Watch App Extensionのインストールには成功しているのだけれども、Apple Watch上にインストールされるApple Watch Appのインストールが何らかの理由で失敗している事が多いようです。

私が遭遇した失敗の理由ですと、例えばApple Watch AppのInfo.plistに不要な値を記載してしまったためにインストールにコケたということがありました。具体的にはLSApplicationQueriesSchemesとNSAppTransportSecurity(いずれもiOS 9向けのキー)をApple Watch AppのInfo.plistに記載していたのですが、インストール時にこれらが
(Error) WatchKit: validateWatchKitApplicationInfoDictionary, invalid Info.plist key 'LSApplicationQueriesSchemes'
のように言われて失敗してしまっていました。

watchOS 1の地点ではインストール時にかなり厳密に値をチェックしているようなので、Apple Watch App向けのInfo.plistには余計なものを記載しないほうがよさそうです。watchOS 2ではまた変わるかもしれませんが・・・

症状: インストールは正常に完了するのにアプリが全く立ち上がらない

大変稀なケースですが、インストールは完了するのにアプリが全く立ち上がらず、最初のアプリ名が表示されてクルクルが回ったまま永遠に先に進まなくなってしまう事があります。私が遭遇した場合の原因ですが、Entitlementsの指定が間違っていたときにこのような問題が発生したことがありました。通常アプリのEntitlementsは自動的にXcodeが生成して付与するため問題にはならないのですが、何らかの理由で自分でApple Watchアプリ向けにEntitlementsを自作して設定した場合は大変問題になります。

具体的には、Apple Watchアプリ向けにEntitlementsを自作する場合には必ず以下のようにしてください。

  • Apple Watch App Extension向けのEntitlementsには、必ずapplication-identifierとkeychain-access-groupsを含める。
  • Apple Watch App向けのEntitlementsには、application-identifierとkeychain-access-groupsのみを含め、その他のキーは絶対に指定しない。

application-identifierは指定がないとそもそもApple Watchアプリのインストールに失敗します。

keychain-access-groupsはアプリ内でKeychainを一切使っていなくても必ず含めてください。どうやらiPhone本体とApple Watchアプリ間の通信の際にこのEntitlementsをこっそりシステムが使用しているようで、keychain-access-groupsが指定されていないと冒頭で紹介したような事態が発生するようです。エラーメッセージも何も一切表示されなかったため調査に大変苦労しました。

2009年4月12日日曜日

Mac OS XのBlogライター、ectoを試してみる

前回に引き続き、今度はMac OS Xで最も優れたBlogエディタとの呼び声も高い、ectoを試してみました。

■タイトルのテスト



ちょっと崩れたHTMLを書いた結果がこれだよ! abesihidebuたわばおおお!?なんだこれ!?うまくいった!!!

でも改行を入れないとダメみたいね>< なんだそれ・・・


どういう風にアップロードされるのか 見てみようかな 列挙使いたいな
あべし
ひでぶ
たわば



■以下、余りにも使いづらいので結局普通に編集
詳しいことはよくわかりませんが、この子もWYSIWYGエディタが思いっきり悪さをするのでHTMLエディタのみを使うのが正解のようです。
WYSIWYGエディタからHTMLエディタに戻ってくると勝手にPタグやBRタグを大量に付与されてしまうため完全に文章が壊れます。


あと、タグを適切に閉じないとものすごく怒られます。ひどい。


最大の問題は、このような投稿済みの記事を取得してきたら・・・


こんな風に猛烈に崩れます。これはちょっとなぁ。まぁ、Bloggerの改行をBRタグに置換するなんていう男気のないオプションを使って、純粋なHTMLで記述していない私が一方的に悪いのですが、しかしその辺もうまく対応して欲しいです。

うーん。結論から言うといまいちです。画像アップロードもFlickerにしか対応していないように見えますし。Picasaに投稿したいんですけれど。
MovableTypeやWordpressを個人で利用されている方にはおそらく福音のようなソフトなのでしょうが、しかしBloggerを利用している私にはいまいち使い出がよくなかったです。
しばらくはPetrus Bloggerを使ってみることにします。

2009年5月4日月曜日

Objective-CのnilとNULLの違いって何?

自分用メモ。
nilは「Objective-Cの空のオブジェクト」、NULLは「C言語の空ポインタ」と解釈する。もっとも良い例がNSFileManagerのcreateDirectoryAtPath:withIntermediateDirectories:attributes:error:です。
http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSFileManager_Class/Reference/Reference.html#//apple_ref/occ/instm/NSFileManager/createDirectoryAtPath:withIntermediateDirectories:attributes:error:

リファレンスを読んでみると、attributes:の指定が不要なときはnilを、error:の指定が不要なときはNULLを引数として与えろと明示的に書かれています。これは、attributes:が(NSDictionary *)型=NSDictionaryのオブジェクトを引数として受け取るのに対して、error:は(NSError **)型=NSErrorのオブジェクトのポインタを引数として受け取るためだと考えられます。といっても私はObjective-CもC言語もド素人ですので、ひょっとしたら大嘘かもしれません。違ってたらごめんなさい><


※追記:やっぱり定義そのものが違った><
すたっくおばふろ曰く(http://stackoverflow.com/questions/557582/null-vs-nil-in-objective-c
えらいひと曰く(http://www.libjingu.jp/trans/clocFAQ-j.html#objects-nil


知らないとついつい全部nilもNULLも同じだろと思いnilって指定してしまいそうですね。おそらくnilを指定しても動くとは思いますが・・・Objective-CとC言語の混ざり合いというか関わり合いがこんなところで垣間見れて面白いです。

2010年1月9日土曜日

パスワードが無くなってログインできない Mac に無理矢理ログインする方法

会社で前任者が使用していたMacを使用する際など、管理者ユーザーのパスワードが紛失してしまってログインできなくなってしまう場合があります。っていうか、実際になって大変困りました><
このような場合、Mac OS Xでは「シングルユーザーモード」でOSを起動して、ユーザーのパスワードを自由に書き換えてログインできるようにすることができます。万が一の場合に便利なのでご紹介いたします。

参考にしたページはこちら。
http://d.hatena.ne.jp/viz3/20091029


■対象環境
  • Mac OS X 10.4以上であること (launchctlを使用するため)
  • UNIX / Linuxの対話環境についてある程度以上の知識があること
  • 失敗しても泣かない心があること
大変申し訳ありませんが、こちらに記載されている内容を実行して失敗してシステムが起動しなくなっても当方は責任を負いかねます><


1.シングルユーザーモードでMacを起動する
Macの電源を押して起動する際に、キーボードのCommand + Sキーを同時に押しっぱなしにしておくと、シングルユーザーモードで起動することができます。真っ黒な画面に文字がずらずら出てきたら成功です。


2./を読み書き可能にする(再マウントする)
画面上に
If you want to make modifications to files:
/sbin/fsck -fy
/sbin/mount -uw /
と表示されているので、ここに表示されているコマンドをそのまま入力します。コマンドの内容は以下の通り:
  • /sbin/fsck -fyは強制的にディスクチェックとディスクの問題修正を行う。-yがついているので、全ての問い合わせに対してYESを返したものと見なす。
  • /sbin/mount -uw /はすでに読み取り専用モードでマウントされている/を再度書き込み可能なモードでマウントし直す。

3.dsclをlocalonlyモードで起動する
お次はdsclというツールを使用します。ですがこのツールそのままでは起動できないようなので(dscl .とタイプするとなにやら警告が出ると思います)、以下のようにしてlaunchctlを使ってdsclツールの設定ファイルを読み込んでから、dsclをlocalonlyモードで起動します。
launchctl load /System/Library/LaunchDaemons/com.apple.DirectoryServicesLocal.plist
dscl localonly


4.対象のアカウントのパスワードを書き換える
dsclがlocalonly対話モードで起動したら、cdコマンドとlsコマンドを利用して対象のアカウントのある/Local/Target/Usersまで移動します。
 > ls
Local

Contact
Search
> cd Local
/Local > ls
Default
Target
/Local > cd Target
/Local/Target > cd Users
/Local/Target/Users > ls
ずらずらとここにアカウント名が羅列されるはず
ここまで移動できたら、以下のコマンドを入力すればパスワードが書き換わります。
passwd 対象のユーザー 新しいパスワード
たとえばさっきの状態からユーザーakisuteのパスワードをabesiに書き換えたいなら、
passwd akisute abesi
です。


5.再起動してログインする
あとは再起動して普通にMacを起動します。
reboot
先ほど入力したパスワードでログインできるはずです。


■注意
この方法を用いれば、物理的にアクセス可能な任意のMacの操作権限を奪うことが可能になってしまいます。くれぐれも悪用しないように。

逆に、この方法を用いてアクセスされたくないMacについては、以下のページで紹介されている方法を用いてシングルユーザーモードによる起動を禁止することができます。
Mac OS X 10.1 以降で、ファームウェアのパスワードプロテクションを設定する


■UNIX / Linuxユーザー向け
Mac OS Xのユーザーって、/etc/passwdじゃなくてDirectory Serviceで管理されてるんですねー。最初/etc/passwd以下を調べに行ってハマりました・・・

2012年1月23日月曜日

Jenkins を iOS アプリ開発に導入してみた (GHUnit編)


前回 はSenTestKitを用いてJenkins上で単体テストの自動実行を行いました。今回はGHUnitを使った単体テストの自動実行にチャレンジしてみたいと思います。またついでといっては何ですが、単体テスト時に必要になってくるモックを作成するためのライブラリOCMockも同時に導入してみようと思います。


■なぜGHUnitを使うのか

GHUnitを使うことで、SenTestingKitと比べて以下のようなメリットが得られます。
  • 非同期処理のテストを行うための仕組みが用意されている(GHAsyncTestCase)これをSenTestingKitないし他のテスティングフレームワークでやろうとすると大変骨が折れます。
  • .app形式(要するに実際のiOSアプリケーション)でテストを実行するため、UIApplicationやUIWindowといったUIコンポーネントを使うクラスのテストが可能になる。UIテストの支援をするための仕組みも最近のGHUnitには追加されている。
    • SenTestingKitの"Logic Test"では想定通りに動作しないUIKitやFoundationのクラスが幾つかあり、中には純粋なロジックで使いそうなクラスも含まれている。そのためSenTestingKitではテストできないロジッククラスが発生する場合があるので、そういう時はGHUnitを使うと良い。
    • 参考: https://gist.github.com/1662887
  • 実機上でテストを実行できる。そのため実機でのみ発生するバグをつかまえることが可能。
  • SenTestingKitのテストケースをそのまま実行できるため上位互換として使用できる。
逆にGHUnitにはSenTestingKitと比べて以下のようなデメリットがあります。
  • Xcode 4で統合されたUnitTestingの恩恵に預かれない。すなわちCmd+U一発でテストケースを実行することができない。Xcode経由だとCmd+Rでアプリケーションとして起動して、その後起動したアプリのテスト実行ボタンを押す手間が必要になるため、開発のテンポが乱されてしまう。特にTDDを採用している場合には深刻。
  • 上記問題を回避するためCUI経由でテストを実行することもできるのだが、Xcodeのエディタと連携しないため、どこがエラーになっているのかが一目でわかりづらい。
  • カバレッジの取得が楽。SenTestingKitをシミュレータ上で実行している時が一番やりやすい。これについては別の記事で解説します。
これを踏まえると、以下のように使い分けができるようになります。
  • SenTestingKitは純粋なロジックの単体テストを実行したいときに良い。Xcodeからすぐに実行できて一瞬で結果がコード上に出るので、開発のテンポを乱さない。TDDに向いている。
  • GHUnitは非同期なAPIを持ったクラスの単体テストを実行したいときに良い。またUIApplication, UIWindowといったUIコンポーネントと結合したり、通信やファイルアクセスなどの外部リソースとの結合をおこなった状態でのテストを作る際にも向いている(純粋な単体テストともユーザー受け入れテストとも異なるため、仮に結合テストと呼ぶことにする)ので、そういったものが必要な場合には最高の選択肢となる。
プロジェクトの開発方針や開発対象によってどのようなテストが必要になるのかが異なってくると思いますが、どれか1つだけ必要であればGHUnitを選択し、きちんとしたXPでやりたいプロジェクトはSenTestingKitに単体テストをやらせ、ユーザー受け入れテストに KIF などを採用し、その中間を埋める必要があればGHUnitも導入する、というのが良いのではないかと考えています。ちょっと面倒ですけど。


■GHUnitを導入する

以下のリポジトリからコードを取得して、XcodeからビルドすればOKです。
https://github.com/gabriel/gh-unit
注意点として、GHUnitは.framework版がそのままgithubからダウンロードできるのですが、
https://github.com/gabriel/gh-unit/issues/69
このような問題が報告されており使えないので、githubからソースコードをクローンしてきて、
cd Project-iOS
make
でビルドしてできたGHUnitIOS.frameworkを使うようにしてください。

frameworkをビルドしたら自分のプロジェクトに追加して、GHUnit用のビルドターゲットを作ります。
http://gabriel.github.com/gh-unit/docs/appledoc_include/guide_install_ios_4.html

ビルドターゲットができたら、次は試しにテストケースを追加してみて、問題なくテストが走るか確認。
http://gabriel.github.com/gh-unit/docs/appledoc_include/guide_testing.html

最後にCUI(要するにxcodebuildコマンド経由)でビルドができるようにします。これをやらないとJenkinsと連携できません。
http://gabriel.github.com/gh-unit/docs/appledoc_include/guide_command_line.html


■OCMockを導入する

以下のリポジトリからコードを取得して、XcodeからビルドすればOKです。
https://github.com/erikdoe/ocmock
iOS向けビルドはスタティックライブラリ用のビルドしか用意されていませんので、どうしてもフレームワークが良いとこだわる人は頑張ってください>< そうでなければ非常に簡単です。ビルドしたら成果物をそのままプロジェクトに突っ込めばすぐ使えるようになります。
OCMock自体の使い方にはここでは触れませんが、例えば以下のようなテストケースが作れたりします。

- (void)testOCMock
{
    // Creating a new stub object from class
    {
        id mockedObject = [OCMockObject mockForClass:[NSString class]];
        STAssertThrows([mockedObject length], @"Mocked object raises exception because the fake method is not ready yet.");
        STAssertThrows([mockedObject isKindOfClass:[NSString class]] , @"Mocked object can't even call isKindOfClass: because it's not ready.");
        STAssertFalse([mockedObject class] == [NSString class], @"Mocked object is not a kind of the target class.");

        [[[mockedObject stub] andCall:@selector(mockedLength) onObject:self] length];
        STAssertEquals([mockedObject length], (NSUInteger)100, @"Mocked object returns the fake value.");

        [[[mockedObject stub] andReturn:@"AllYourBaseAreBelongToUs"] lowercaseString];
        STAssertEqualObjects([mockedObject lowercaseString], @"AllYourBaseAreBelongToUs", @"Returns mocked value.");
        STAssertEquals([mockedObject length], (NSUInteger)100, @"Previously mocked methods are still valid.");
    }

    // Method Stubbing for existing object
    {
        NSString *stubTargetObject = @"I am a stub target.";
        STAssertEquals([stubTargetObject length], (NSUInteger)19, @"The original implementation of the length method.");

        id mockedObject = [OCMockObject partialMockForObject:stubTargetObject];
        STAssertTrue([mockedObject isKindOfClass:[NSString class]] , @"Mocked object is a kind of the target class.");
        STAssertEquals([mockedObject length], (NSUInteger)19, @"Mocked object returns the original value.");

        [[[mockedObject stub] andCall:@selector(mockedLength) onObject:self] length];
        STAssertEquals([mockedObject length], (NSUInteger)100, @"Mocked object returns the fake value.");
        STAssertEquals([stubTargetObject length], (NSUInteger)100, @"Stubbed target object also returns the fake value.");
    }
}
- (NSUInteger)mockedLength
{
    return 100;
}


■Jenkinsと連携させる

最後にJenkins側でビルドターゲットを作りましょう。
http://gabriel.github.com/gh-unit/docs/appledoc_include/guide_ci.html

こんな感じの設定になると思います。

あとはいつもどおりジョブを回してみて問題がないか確認すれば完了です。前回ご紹介したようなSenTestKitの単体テストジョブがうまく回っていれば、それをコピーしてきてちょっと設定を変えれば簡単にできるとおもいます。

2008年11月30日日曜日

DEMOsa Vol.5に参加してきました

DEMOsaについてはこちら
http://www.mosa.gr.jp/?p=2168

デザイナー系の人が自分のやった新しくて面白いことをライトニングトークみたいに発表する会でした。
スケジュールを見てもわかる通り、iPhoneネタが非常に多かったです。

最初の半分を遅刻して見逃してしまいましたが、以下感想を箇条書き。

■ポメラ
  • ノートPCが持ち込めない場所(会社とか会社とか会社)では大活躍できそうだ。言われてやっとその価値に気づいた。
  • でもポメラからPCにデータが移せない(USBメモリは全面禁止が当たり前なので)ので、結局意味がない気がしてきた。
  • 実際に触らせていただいたが、小さい。初代DSにそっくり。キーボードはいまいち。EeePCのどうしようもないキーボードに比べれば遥かによいが、MacBook Airのキーボードに慣れてしまうとやはりMacBook Airに軍配が上がる。

■Cookie
  • 絵文字だけでコミュニケーションする子供向けSNSのようなもの。
  • 実装はともかくとして、絵文字だけでコミュニケーションという発想は最高。これでイスラエル人ともヘブライ語を覚えずしてコミュニケーションできる訳だ。絶対にこれはiPhoneで実現するべき。

■iKotoの中の人
  • 日本文化に関するアプリばっかり作る理由は、文化を創るか。かっこいい。
  • 漫画とアニメ以外の文化は創らないと伝わらない。

■Afrous
  • ブラウザだけでマッシュアップが作れるというアプリ。らしい。
  • RSSもAPIもないただのWebページのDOMを解析してデータを抜き出して、youtubeやamazonと連携させていた。凄い!
  • たとえば日経新聞のトップページを切り出して強制的にRSS化することもできそうだ。

■RainbowNoteの中の人
  • 1万アプリものアプリケーションがあふれ変えるiTunes Storeの壁は高い、キーになるのはプロモーション
  • Moopf's App Store Review Scrapper itunesレビュー評価を見る
  • Mybefia 競合アプリとの人気の差を見る
  • Mobclix 国別の評価を見る

■BiND
  • 10分でWebページを作成していた。Webページをブロック単位に分割して、ブロックを追加したり編集したり。
  • 本当なら6時間かかる作業を10分でやったんだって!本当かどうかは信じがたいが、確かにページの編集速度は速かったしCSSをいっさい触ってなかった。
  • ロゴ画像の編集は本当に便利そうだった。

■MemoryTreeの中の人(宮田さん)
  • 空間にPostitみたいに画像を貼付ける!セカイカメラみたいだなぁ。もっともセカイカメラと違ってこちらは本当に実現されている(できることが限られているけど)。
  • iPhoneを振ってメモリーを投げ、メモリーを受け取るというアイディアがすばらしい!後から懇親会でお聞きしたところ一人の天才プログラマーとセットで1週間不眠不休でやって作ったそうな。
  • Motion Comics Playerなんて動画を電子書籍としてリリースしているけどほとんど売れてないらしい。それでもそこそこのApp Storeで上位だから電子書籍はよほど売れていないんだろうとのこと。
  • 以下、後から懇親会でお話ししたときのこと・・・
  • 貫禄がある人だった。ちょっと怖い。でもいい人。
  • 1万アプリのうちほとんどは有象無象、いいものを作ってプロモーションをきちんとやればいくらでも上位に入れるよ、とのこと。
  • ケータイなんかに比べてiPhoneを購入する層の嗜好は偏っているとのこと。いわゆる新しいものとかっこいいものが好きな人たち。
  • 実はこの宮田さん、世界で一番最初に着メロを作って大もうけした人なんだとか。なんてこった・・・
  • で、あまりにも着メロが大成功だったのでモバイルからは遠ざかっていたが、iPhoneでモバイルに復帰したそうな。それほどの可能性か!iPhone
■Niconの新製品は化け物か
  • http://www.nikon.co.jp/main/jpn/whatsnew/2008/1007_up_01.htm
  • 第一印象:どう見てもスカウターです
  • 第二印象:やりやがったNicon、やりやがった
  • 実際に体験しましたが、ヘッドフォン部分のフィット具合は凄くいいです。画質も凄い、目の前に画像イメージがわいてくる感じです。サイズは640*480らしいですが実際はもっと小さく見えます。
  • ディスプレイ位置の調整がかなり大変です。あと振動にも弱いです(大きくぶれて見えなくなるので、歩きながらはつらいと思います)。
  • しかしこれにGPSとアプリケーション作成プラットフォームをつけてiPhoneみたいにしたら面白そう
  • そうしたら私は絶対にライトセイバーアプリならぬスカウターアプリを作る。「戦闘力、たったの5か・・・ゴミめ・・・」

■ノルウェーの留学生の方とお話
  • 外人さんは未だにちょっと苦手かも
  • 彼曰く、留学している日本人とそうでない日本人は話す内容が全然違うらしい
  • 留学していない日本人は、日本の否定的なところを言われたときに鵜呑みにしてしまうが、留学している日本人は嫌な顔をする(そうではないという)らしい。う・・・

2017年12月4日月曜日

2017年のしめくくり

pyspa Advent Calendar 2017 12/4です。

どうもお久しぶりです。オンライン上では一切活動しておりませんが、おかげさまで大変元気にやってます。というわけで2017年のまとめを書いておこうと思います。

そもそもなんで最近ブログ書いてないの

書くことがないからです。というのも私は最近iOS/Androidの開発ではなくジャバでサーバサイドアプリケーションを書くみたいな仕事をしていて、別にSpring Bootの話なんかここに書いてもあまり面白くもなければ、すでにありとあらゆる先人が地雷を踏み尽くしていて調べればだいたいなんでもわからないことは分かる状態になっているので、わざわざ私が書く必要もないと思われるためです。

なんでiOS/Androidやってないの

端的に言えば愛想が尽きました。iPhone Xは買っていません。SwiftとKotlinは大好きで今でも仕事で使ったり他人のコードをレビューしたりする毎日ですが、もはやiOSもAndroidもどちらも執着するには値しません。

なんか技術的に新しいネタないの

正直今ネイティブアプリに大きな流れはないので(ARとクライアントサイド機械学習程度しか新規性のあるネタがなく、どちらも適用分野が限られ、ほとんどのアプリケーションでは縁がない)、後数年の間はモバイルネイティブアプリ一択だった流れが半分ぐらいウェブに返ってくると思っていて、ゲームなどではすでにその兆候もありますし、React NativeとかモバイルフロントエンドJavaScriptとかを見ておけばいいんじゃないのかなぁと思っています。個人的にはあんまりどちらも好きじゃないんですが、悪いものではないと思います。
また、Rxはもはや使えて当然と思っていますが、しかしながらRxを限界まで酷使するようなアプリはごく少数の人間しかメンテできなくなり、そのうちメンテがおぼつかなくなり、Rxのシグナルやオブザーバを使うのではなく昔ながらのdelegateやcallbackで手っ取り早くコードを修正するような事態が訪れ、ゆっくりと破綻していくような事態が周囲で散見されています。正直人間のほうが技術側に追いついていないというか。使えるやつは使えるんだけどそれじゃメンテ回らないよというか。

なんか注目のビジネスネタないの

フィンテックはガチだと思います。儲かるので。ただしビットコイン、というかブロックチェーン技術を神聖視するのは正直イケてないと思います。
動画配信系もガチだと思います。これもカネの匂いがします。ただしこっちはそもそもプレイヤーになれる存在が非常に限られており、ドワンゴすらそろそろもうついていけないかなぁという状態ですので、皆様がんばってくださいという感じです。
AIが〜とか機械学習が〜とかはやらなくてはならないのは間違いありませんが、ぶっちゃけ現状ではまだそんな言うほど大したことないです。どっかのタイミングで私みたいな何もわからないド素人でも簡単に放置してデータ食わせるだけで勝手に最良の状態に学習する事ができるような仕組みが誕生すると思っていて、そうなったら本当に爆発的に世界が変わる気がしています。それまでは自動運転みたいなカネが突っ込まれまくる分野でのみ世界が変わるほどの進展が見られるんじゃないかと勝手に思っています。
スマートスピーカーは正直使ってみて要らないと思います。不便とは思いませんが、現状無くてもほとんど全く困らないので要らないです。無いと困るようになったら爆発的に売れるのではないでしょうか。

今年のゲームはどうだったの

ゼルダがGotY 2017なのは間違いありませんが、その他も非常に豊作な一年だったと思います。個人的な推しはtheHunter: Call of the Wildです。

家どうなったの

友人が家を探しているというので色々物件を紹介していたら、まさかの我がマンションに引っ越してきました。おかげさまで大変楽しい毎日です。近所付き合いってのも悪いもんじゃないと思います。
あとはベッドを買い替えました。こちらもiPhone Xを買うより100倍ぐらい幸福になりました。良いお金の使い方をしたと思います。

婚活どうなったの

退会しました。1年間、のべ数百人単位の女性を紹介してもらって、数十人単位と付き合って、数人とじっくり時間を過ごしてみた得た結論は、正直私は女性と付き合っても煩わしいばかりで楽しくないし、相手も私といても満足できないし、結婚してもいいことはないというものでしたので、そのようにしようと思います。1年前はちょっと焦りとかもありましたが、今では大変気分も落ち着いて平穏が得られました。自らの人生について考える良い機会だったと思います。

今どうなの

おかげさまで非常に幸福な人生を送っていると思います \(^o^)/
来年もいい年になりますように!

2009年3月17日火曜日

GTDで己の無知の知を知る

OmniFocusを導入していちばん大事だと思ったことなのですが、
GTDを実践していると、「実はびっくりするぐらい自分は何も分かっていない」ということが明らかになります。

どういうことか?
と申しますと、

たとえば今開発しているアプリに新機能を追加したので、テストしたいと考えています。
ですからまずはOmniFocusに「〜をテストする」という考えを追加します。

これだけでは行動に落とし込めないので
「テストクラスを書く」
「テストクラスをTextMateから実行する」
といった行動レベルに分解する必要があるのですが、

ここでふと「テストクラスを書くって、どうやるんだっけ」とか
「TextMateからテストを実行するショートカットってどうだったっけ」とか
そういう疑問が次々にわいてくるわけです。

だからそれらの本筋アクションを実行する前に、
「Pythonのunittestの使い方をネットで調べる」
「サンプルコードを動かして学ぶ」
「TextMateのショートカットを調べてEvernoteにメモする」
というような雑用作業が次々に出てきてしまいます。

今までの自分は、こういう「調べる」とか「学ぶ」というアクションを
全く思考に入れずにプログラムを作ったり仕事をしたりしていました。
これが今までずっと「なぜか知らないが、たったこれだけしか仕事の量がないのにどうしてこんなに時間がかかるのか」と思っていた原因なんだなぁと独り合点しています。

おかげさまで、今では「自分は何も知らないのだから時間がかかる」という風に考えられるようになり、仕事が進まないストレスも減ったかなーと。

2011年4月17日日曜日

UINavigationController に管理されている UIViewController の view のサイズを変更したい

たとえばアプリ内の固定の位置に広告を突っ込みたいときなど、 UINavigationController に管理されている UIViewController の view 構造を操作したい場合があると思いますので、調べてみました。


■UINavigationController.viewの中身

iOS 4 ですと以下のようになっています。



最上位の UINavigationController.view に相当するのが UILayoutContainerView で、その下に UINavigationTransitionView があり、そのまた下に UIViewControllerWrapperView というのがあって、こいつが表示される UIViewController.view をラップして表示しているという仕組みになっているようです。

おそらく iOS 3.2 でも同じだと思います。 iOS 3.1.3 以前についてはわかりません。


■実際にやってみる

いろいろ試してみた結果、もっともうまくいきそうなのは以下のように、 viewDidAppear のタイミングで UIViewController.view.superview に対して操作を行う方法であることがわかりました。
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.headerView.frame = CGRectMake(0, 0, 320, 100);
self.view.frame = CGRectMake(0, 100, 320, self.view.superview.frame.size.height - 100);
[self.view.superview addSubview:self.headerView];
}

■問題発生

ところがこの方法では画面遷移時のアニメーションが発生すると上記の例で追加した headerView の表示が無茶苦茶に壊れてしまいます。またタイミングを誤ると headerView のサイズが self.view.superview.bounds の大きさに補正されてしまいます。恐らくは UINavigationTransitionView とかいう private な view がなにやらやっているのでしょう。アニメーションが発生しない範囲であれば問題なさそうですが、いまいちです。


■結論

横着しないで表示される UIViewController の view で何とかするしかないみたいですね><

2010年7月7日水曜日

Core Graphics (Quartz) のみで日本語文字列を描画するライブラリのヘッダファイルを書いてみた

とある理由で UIKit の描画機能が使えず、 Core Graphics のみで文字列の描画処理を行わなければならないことになってしまったので、適当に調べてみました。


■ことのはじめ

Core Graphics の機能だけで日本語の文字列を描画する方法については、既に先人の方々が調べて記事にまとめてくださっていたので、そちらを見ていただければ大丈夫です。
http://iphone-dev.g.hatena.ne.jp/ktakayama/20100129
http://d.hatena.ne.jp/r_kurain/20100316
基本的にはこちらで紹介されている方法に従って進めていけば困ることはありません。ただし、描画した文字が上下反対になることがありますので、 CGContextSetTextMatrix を使う箇所を調整したりする必要があるかも。

で、最大の問題になるのがこれらの記事で紹介されている CGFontGetGlyphsForUnichars と呼ばれる関数です。この関数を使えば楽に日本語文字列を文字化けすることなく描画することができるのですが、あろうことかこの関数はプライベートAPIであり、使うとリジェクトされてしまうらしいです。ということで別の作戦をとらなければなりません。

上記の記事にいろいろな対処方法が載っているのですが、私は一番単純に
http://www.mexircus.com/codes/GlyphDrawing.mm
というライブラリを自分のアプリに組み込む方法を試してみることにしました。この方法を使えば、 CGFontGetGlyphsForUnichars でまず作って動くようにしてから、 CMFontGetGlyphsForUnichars と書き換えるだけでそのまま動作するので楽です。

問題はこのライブラリ、mmファイルしか用意されていないのでそのままではうまく使えません。適当にヘッダファイルを用意してやる必要があります。


■と言うわけで書いたもの

と言うわけで GlyphDrawing.h を書いてみました。
http://gist.github.com/466297

ライセンスは元の GlyphDrawing.mm に準じますが、 GlyphDrawing.mm のライセンスが不明なので、不安ならばまず作者さん (http://mexircus.com/blog/) に一言聞いてみるのがいいと思います。


■使い方
  1. http://gist.github.com/466297http://www.mexircus.com/codes/GlyphDrawing.mm をダウンロードします。
  2. GlyphDrawing.mm のインポートを以下のように書き換えます:
    //#import <Foundation/Foundation.h>
    #import "GlyphDrawing.h"
  3. GlyphDrawing.hGlyphDrawing.mm をプロジェクトに追加してビルドします。
  4. CGFontGetGlyphsForUnichars の代わりに CMFontGetGlyphsForUnichars を呼びだせばOKです。


■実際に使ったコードの例
NSString *message = @"本日は快晴なり";

// フォントを設定
// ついでにmessageのサイズも取得
// ただしsizeWithFontは UIGraphics の機能なので、本当に Core Graphics だけで描画したいならここで使ってはいけません
UIFont *font = [UIFont fontWithName:@"HiraKakuProN-W6" size:32.0];
CGRect messageRect = [message sizeWithFont:font];
CGFontRef fontRef = CGFontCreateWithFontName((CFStringRef)font.fontName);
CGContextSetFont(c, fontRef);
CGContextSetFontSize(c, font.pointSize);

// Glyphを作成
size_t length = [message length];
CGGlyph glyphs[length];
UniChar chars[length];
[message getCharacters:chars range:NSMakeRange(0, length)];
CMFontGetGlyphsForUnichars(fontRef, chars, glyphs, length);

// 文字列が上下反対になるのを防止する
CGAffineTransform transform = CGAffineTransformMakeScale(1.0, -1.0);
CGContextSetTextMatrix(c, transform);
CGContextTranslateCTM(c, 0, messageRect.size.height/2);

// 描画
CGFloat x = 100.0f;
CGFloat y = 100.0f;
CGContextShowGlyphsAtPoint(c, x, y, glyphs, length);

2011年5月29日日曜日

iPad 用スタイラス Bamboo Stylus を試してみた



つい先日いよいよ発売された Bamboo Stylus, 早速買って試してみました。


■Bamboo Stylus とは?

http://wacom.jp/jp/company/news_detail.php?id=355
http://japanese.engadget.com/2011/04/19/bamboo-stylus/

B004XF0FQWWacom iPad/IPad2/iPhone4対応 描画、ポインティングに最適なタッチペン Bamboo Stylus CS-100/K0
ワコム 2011-05-27

by G-Tools

PC向けペンタブレットではおそらく業界最大手の Wacom が、満を持して投入してきた静電誘導式タッチディスプレイ用のスタイラスペンです。ペンタブレットの開発でおそらくこの手のスタイラスペンに関しては相当なノウハウがあるのではないかと思われ、非常に期待が持てます。プレスリリースで、特に以下のような点が既存のスタイラスとは異なると強調されています。
  • 手に持った時の心地よさと書きやすさを両立する重さと重心のバランス。
  • 上質なサテン調のブラックとシルバー色を採用し、高い質感を実現。
  • ペン先は主な他社製品よりも直径が約25%小さく、より自然で直感的な描画が可能。
  • スムーズで快適な書き心地を実現するペン先の実現。(磨耗した際には交換が可能)
  • iPadカバーに留めたり、ポケットに留めたりするなど、携帯や保管にも便利なクリップ。(脱着可能)

■最初に結論

良い点
  • 第二世代のシリコンペン先を採用している他のスタイラスペンと比べ、細くて滑りが良くしっかりとしたペン先。
  • サイズの割に軸がやや太めで、しっかりと握ることができる。
  • 上記の理由により、実物のペンと非常に近い筆書感覚が得られる。
  • クリップが丈夫で脱着可能。取り外してストラップを代わりにつけることもできる。
悪い点
  • 同サイズのスタイラスペンの中では圧倒的に重く、書く際に手が疲れる。
  • 第二世代のシリコンペン先を採用している他のスタイラスペンと比べ、感度がやや悪い。
  • 3000円という、やや高価な値段設定。

■外見、持った感触

まずは外見から。値段設定がやや高めで、かつBambooブランドの名前を冠していることからもわかるとおり、iPad向けスタイラスとしてはハイエンドを狙っている物と思われます。そのためデザインにも力が入っており、表面がiPadやMacのように梨地加工されています。単なる金属の棒という体の パワーサポート スマートペンプリンストンテクノロジー 専用タッチペン に比べ断然高級感があり美しく感じられます。

長さは12cmということで、他社のスタイラスと比べても数ミリ長い程度で標準的だと思います。軸は他社の製品に比べてやや太く、良くコンビニなどで売っているボールペンよりわずかに細い程度になっています。そのため手に持った際の感覚が非常にペンらしいです。また、重さが非常に特徴的で、他のスタイラスと比べて明らかに密度のある重さがあります。体感だと パワーサポート スマートペン より二倍近く重いように感じます。

上部とペン先はねじになっていて、それぞれクリップとペン先が取り外せるようになっており、メンテナンスのことを考えた作りになっているのが心憎いです。消耗品ではなく、長期間使わせる物という印象を受けます。また、取り外した上部のクリップの部分にストラップをつけて持ち歩くことも出来そうです。


長さ比較。左から右に短い物順。pogo sketch, スマートペン, Bamboo Stylus, oStylus


太さ比較。先ほどと同じ順序。pogo sketch, スマートペン, Bamboo Stylus, oStylus


■動作、書き味

買って最初に一筆書いてみた時にすぐ気づいたのが、まず感度が悪いです。ほとんど載せるだけで反応する超感度の パワーサポート スマートペン に対して、ほんの少しだけ力を込めないと反応しません。しかしながらペン先が細くてしっかりしており、iPadの表面を非常になめらかに滑ります。滑りがよいので実際に紙で書いているよりちょっと違う感じがしますが、ペンタブレットに慣れている人にはちょうどいいぐらいだと思います。先端がふにゃふにゃしてやや抵抗がある滑りの パワーサポート スマートペン とは対照的な印象です。

個人的には長時間書いていると疲れます。感度が悪く抵抗が弱いのと重いペンが相まって、腕の力を使うためだと思います。


実際に Noteshelf アプリ上で書いてみました。実際のペン同様、非常になめらかに書けています。


■まとめ

WacomとしてはiPad向けスタイラスのハイエンドモデルとして位置づけているこの商品ですが、実際に使ってみるとむしろ パワーサポート スマートペン と対になるような感じの印象を受けました。すなわち、以下のように棲み分けられます。

Bamboo Stylus をオススメする人
  • ペンに近い持ち味、書き味が欲しい
  • 細くてよく滑るペン先が何より欲しい!
  • 値段は高くても気にしない
パワーサポート スマートペン をオススメする人
  • 軽くて、書いていて疲れないペンが欲しい
  • 感度の良くて、実際のペンみたいに少し抵抗がある滑りのペン先が何より欲しい!
  • 値段は安い方がいい、または初めてスタイラスを試してみるので失敗しても後悔しない値段の方がいい
私自身はどっちを選ぼうか悩んでますが、せっかく買ったし、しばらくはBamboo Stylusを使ってみようかと考えてます。

2010年2月8日月曜日

Mercurial と git の branch にまつわるちょっとした tips 3選

3選とか言ってますが大した内容ではございません>< すみませんすみません><


■1:gitのbranchは跡形もなく消せる
ほとんど常識ですが、以下のコマンドでgitのbranchは消せます。
git branch -D
このコマンドを実行すれば、たとえHEADに対してマージされていなくてもそのままブランチを消すことができます。ということで、ちょっとしたテストコードなどはブランチを切ってそこで実験し、後からブランチごとたたき落とす運用が可能です。

余談ですが、gitのbranchはSubversionと使い勝手や実装が似ている感じがします。Subversionのbranchもタダのディレクトリコピーなので、好き勝手に作って消してが可能ですから。


■2:Mercurialのbranchは基本消せない、「未使用」か「クローズ」にはできる
問題はここから。Mercurialのbranchは、基本的に完全に跡形もなく削除することができません。また、マージしていないブランチがあるとpushの際に怒られます。そのため、ちょっとしたテストコードをブランチ切ってそこで作成し、いざいらなくなったので削除しようと思うとえらい大変なことになります。

一応、ブランチを「クローズ」するコマンドを使うことで常用範囲内からは消す事が可能ですが、完全に情報が消えたわけではないのでリポジトリのサイズは小さくなりません。ゴミが溜まります。もしMercurialでテストコードを管理しようと思うならば、branchを切るのではなくリポジトリごとcloneしてそちらでテストする運用にすればよいです。


■3:Mercurialのbranchに数字で名前を付けてはならない
gitと違ってMercurialのリビジョンには、簡便のため、リビジョンのハッシュIDの他にリビジョン番号が1番から採番されるようになっています。で、各種操作の際に、このリビジョン番号を代わりに指定して実行することが可能です。

ここまではOK。

問題はbranchに数字の名前が付けられることで、もしbranchに数字の名前を付けてしまった場合、Mercurialはその数字をリビジョン番号として解釈してしまい、ブランチ名指定の処理ができなくなってしまいます。
hg branch 255
hg commit -m "created branch 255 for ticket #255"
hg update 255
# ブランチ255ではなくリビジョン番号255番に対してアップデートしようとする・・・
ただそれだけなのですが、チケットシステムなどと連携しているとよくチケット名でブランチを切る事があると思いますので、そのような際にはticket255などという風に指定するほうがよいかと思います。

2012年1月2日月曜日

2011年のふりかえりなど

あけましておめでとうございます。年も開けましたので、2011年のふりかえりをやってみて、2012年の抱負を考えてみたいと思います。

■やってみたこと
2010年の途中からiOSアプリの開発担当になったのですが、2011年は始めて一年中iOSアプリの開発に携わることができました。ということでダイジェスト。
  • 1月は前年度から引き続きアプリの修正案件を行なってました。ずいぶんひどく炎上した一年のスタートになったのですが、炎上したプロジェクトでしか学べないものというのはたくさんあるものだと痛感させられました。主に案件がどうして燃えるのかとか、何が死亡フラグか、など。自分一人ではどうにもならないので、急遽助っ人に助けてもらいましてなんとか収拾。助っ人の方々、あの時は本当にありがとうございました><
  • 2月ぐらいからBPRという自社開発のフレームワークとそれを使ったアプリの開発などをしていました。外部に公開するライブラリを組むのは初めてということで、実に勉強になりました。中身の実装もなかなかうまくいったと思ってます。
  • その後ちょっとした案件をこなしてましたが、ここでは複数案件の並行進行を余儀なくされたため、またも自分一人ではどうしようもならない事態に。うまい具合にアルバイトの人に仕事をお願いしたりする必要に迫られるなどしました。
  • 5月〜6月が今年一番の正念場でした。それぐらい難しい案件と他の案件を並行で進めていたのですが、ずいぶん自分の設計からミスをしてしまい、自分の限界を知ることになった気がします。主にCore DataまわりとAPI通信実装まわりの限界がこの案件ではっきりと分かりました。それだけではなく、グループで仕事するときの死亡フラグとか、炎上案件の燃え方とか、これまた大いに学ばされました。
  • 7月からはずっと一本のアプリに集中して新規実装および修正を行なっていました。これまでの案件と打って変わってあまりにもサクサク進んだもので、受託開発での「お客さんの力量」の大事さを実感。むしろ自分のほうがお世話になりっぱなしで申し訳ない気持ちでした。この案件では試しにこれまでの社内ライブラリや設計の基本をすべて捨てて新しい方式でやってみたのですが、大いに成功したところもあればひどく失敗したところもあり、結果としてこの挑戦は正解だったと思っています。またとある理由でopensslのコードを読んだりしたなど、より低レイヤーな部分の知識が限定的ながら得られてきました。
まとめると、これまでにない難易度のアプリの開発に携われて成長できたのと、自分一人ではどうにもならない場面を何度も経験し、仲間の力の偉大さに気付かされたのが今年の収穫だったと思います。

■Keep
去年良かったので続けたかったことはこんなところ。
  • いろんな技術に手を出す。一年以上積み重ねてきたおかげで、ずいぶんと「一番いいやり方」と思われるiOSアプリの開発手法がわかってきたのですが、その方法に固執するとよりよい方法を見落としたり、時代についていけなくなると思ったのであえて別の方法を試し、結果としてよりよいライブラリやプラクティスを学ぶことができたのが実に良かったので、今年もチャレンジしていきたいところです。またClojureをvの人に薦められてやってみて、これまたずいぶんと刺激を受けたので、良いとされる言語やフレームワークに手を出してみてその設計思想を学び取るのは今年もやっていきたいですね。
  • 積極的に仲間に頼る。去年はずいぶんと仲間のみんなに助けて頂きました>< お陰様でずいぶんとスタンドプレーで無理やり解決しようとしてソウルジェムが濁るようなことがなくなったかと思います。今年も自分以外の周りに視野を広げつつ、困ったときに助けていただけるようにもっと人間的に良い人になりたいなーと思ってます><

■Problem
去年よくなかった、またはまだまだ改善の余地があるのはこんなところですかね。
  • 基礎力が足りない。低レイヤーな部分の知識が絶望的に足りない。これはPython温泉の際に指摘されたことで、実に悔しいのですがたしかに通信はHTTPより下のレイヤが殆どわかっておらず、言語はObjective-CがわかってもC/C++がわかってない。アルゴリズムの知識も足りなければ、基本的な数学知識も甘い。
  • さらなる設計力の向上が必要。ずいぶんとよくはなりましたが、それでもまだライブラリやSDKの実装のために必要なレベルの設計力がまだ不足しているように感じられます。
  • グループでの開発手法。自分一人の案件というのがほとんどだったので、たまにチーム戦をやるとひどくミスを連発して、見積もりを間違えたりレビュー不足からひどい炎上を招いたりと失敗が続いているため、グループでの開発のやり方を本気で考えなければならないと痛感させられています。
  • テストの仕方。単体テストと、結合テスト。テストの自動化事態はGHUnitのおかげでずいぶんと進んできたのですが、通信を含んだりファイル操作をするテストが単体テストに紛れ込んでいたりして、自動化の妨げとなっています。SenTestKitとモックを使ったほんとうの意味での単体テストと、それ以外のテストをきちんと分ける開発手法を編み出していきたいです。

■Try
ということでそれを踏まえて、今年はこんな感じのことに挑戦したいです。
  • より低レイヤーな知識の学習。TCP/IPとUDPは必修、できればTCP上でmsgpackあたりのRPCプロトコルを自分で実装できる程度にはなりたいと考えています。CとC++の知識も増やしていきたいです。
  • グループでの開発手法を学びたいです。あとはコミュ力向上。私は自分勝手で人の話を聞かず一度良いと感じたらその手法を信じこんでやまない(人に押し付ける傾向がある)ので、たとえその手法が実際に良かったとしても、チーム全体になじまずマイナスの影響を及ぼしている可能性があります。そういったことをなくすにはどうするか?ということで複数の開発手法を学ぶというのと、人の話を聞いてそれを採用する、これに尽きるでしょう。
  • テストの仕方の改善とCIの導入。まずは単体テストの完全自動化と、リリースビルドの日次生成からはじめ、徐々に取得する集計データの量を増やしていく、例えばカバレッジの計測は簡単に思いつきますし、GoogleやMSはバグが発生しやすい箇所を予め計算する方程式を持っているそうで、そういうのを日次で集計できれば全体の品質に寄与できる可能性があります。