武之新
xibのローカライズでハマる(2)

以前もハマったxibのローカライズ。またもハマりました。

どうやらxibをローカライズ前(言語環境毎のファイルを作る前)に実機転送してしまうと、その状態を覚えてしまうのか、その後どうやってもxibの内容が反映されなくなるという状態に陥りました。わけわかんない。
他にも、今回はNSLocalizedStringでいままで取れていた日本語リソースが、ある時点を境に英語しか取れなくなったりと、ローカライズ関連でハマりまくりです。

NSLocalizedStringのほうはどうやらフォルダの名前に問題があった模様。古いXcodeで作ったprojectなので、English.lproj や Japanese.lproj とかになっていたのですが、これを en.lproj 、 ja.lproj に変更したら日本語が取れるようになりました。

xibのほうは最後まで解決策がわからず。
仕方ないのでファイル名をrenameしました。renameするととりあえず反映されるようになりました。ホントにもう、わけわかんない。 

EXC_BAD_ACCESSでハマる

iPhoneで開発してて、よく突き当たるのがEXC_BAD_ACCESS。
大抵は多重releaseしてしまうなどの単純ミスですが、稀に不可解な
EXC_BAD_ACCESSに陥ることがありますよね。今回たまたま不可解なEXC_BAD_ACCESSが解決できたので、その顛末を参考までに書いてみます。

続きを読む…

UIWebViewでリンク長押し時のActionSheet

UIWebViewはデフォルトで、リンクを長押ししてるとActionSheetが出てきます。選択肢がOpenとCopyしかないので、実質あまり意味がなかったりしますが。
なのでこのActionSheetのボタンをアプリでカスタマイズしたい諸兄も多いことでしょう。

そんなときのTips。

続きを読む…

UIWebViewでcanGoBackが効かない

UIWebViewではページの読込ませ方によってcanGoBack, canGoForwardの挙動が変わるようです。

  • canGoBackがYESになる
    loadRequest: 
  • canGoBackがNOのまま
    loadData:MIMEType:textEncodingName:baseURL:
    loadHTMLString:baseURL:

これはcanGoBackだけでなく、実際にgoBackしても動作しなくなります。historyがきちんと更新されるのはloadRequest:だけのようです。

Javascriptでhistoryがいじれるといいのですが、試しにやってみたところ期待通りにはいきませんでした。できそうなものなんですけどね、、

アプリの申請でハマる

はじめてiPhoneアプリをAppleに申請してみました。
が、そこで思いっきりハマりまくりでした。

ハマったのは次の2点。

  • 証明書を取らないとダメ
    申請するアプリはCode Signされてないとダメで、Code Signは個人証明書を取得してないとダメなわけです。と言ってもこれは比較的有名なハマりポイントらしい。
  • スクリーンショット不足
    初めてのくせにiPhone, iPadのユニバーサルアプリにしてみたので、iPad用スクリーンショットもアップロードしないとmissing screenshot状態でストップしてしまいます。追加すればよいのですが、Localizeしてると、言語毎にスクリーンショットも独立しているので、全ての言語にiPad用スクリーンショットをアップロードしないといけません。

どハマりしたのはスクリーンショットで、アップロードしたのに状態が変わらず焦りました。こんな状態になるのはユニバーサルかつLocalizeしたときくらいでしょうからレアケースだと思いますが。

xibのローカライズでハマる

設定画面など、あちこちに文字列を埋め込んだ画面はいちいちコードでNSLocalizedStringするのが面倒なので、xibをローカライズしてしまうのが簡単だと思います。

はじめてxibをローカライズしてみたのですが、色々ハマりました。

  • xibはサブフォルダの中でローカライズしてはいけないらしい。
    たまたまユニバーサルアプリでやっていたので、xibがiPhoneサブフォルダの中に出来てしまいました。そこに置いたままローカライズすると認識されない模様。 文字列リソースをローカライズするとプロジェクトフォルダの直下にJapanese.lprojができますが、そこに入らないとダメみたい。
  • サブフォルダの残骸はFinderで削除しないとダメみたい。
    一回xibをサブフォルダで作ったあと、サブフォルダにできたJapanese.lprojの中にxibが残ったままにすると、Xcode上でxibを削除しても、ビルド時に掴んでしまうくさい。
    サブフォルダに作ったxibと同じ名前でプロジェクトフォルダ直下に作り直しても、残骸が残ったままだとそっちが優先して使われているようで、うまく動かなくなりました。

それにしても、Xcodeはメジャーバージョンが変わるたびに作法がガラリと変わるので、ネットで調べてもどれがいまも使える方法なのかがわかりづらいですね。

UIWebViewでフォントサイズをいじる

相変わらずUIWebViewと格闘中です。

AppStoreにあるブラウザアプリで、フォントサイズが変更できるものがあったので、真似してみることに。

やり方は簡単、次のようにやるだけ。

script = @"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust='120%';";

[webView stringByEvaluatingJavaScriptFromString:script];

jQueryなどでスタイルのfont-sizeをいじっても、HTML上のスタイル定義のされ方によって適用されたりされなかったり安定しません。webkitTextSizeAdjustを使えばそういうときでもイチコロです。

元ネタはこちら。
http://stackoverflow.com/questions/589177/how-to-increase-font-size-in-uiwebview 

続:NSURLCacheでGoogleのトップページをキャッシュすると落ちる件

結局解決できていません、本件。

あれからさらにいろいろ試して、見えてきたのは[NSURLRequest cachePolicy]。

そもそもは、デフォルトのNSURLCache実装がstoreCachedResponseでデータを渡されていてもcachedResponseForRequestで返すものと返さないものが存在している、というところでした。

なぜか?
データは違えども、NSCachedURLResponse、NSURLResponseの状態は概ね同じ。違うのは[NSURLRequest cachePolicy]。デフォルトのNSURLCacheはNSURLRequestReturnCacheDataElseLoad、NSURLRequestReturnCacheDataDontLoadの場合はキャッシュを返すけど、NSURLRequestUseProtocolCachePolicyの場合は返さないようです。

相変わらず理由はわかりませんが、真似してみることに。

ふむ、落ちなくなった

前回も落ちなくなった後、他の箇所をいじっていたら再発したので安心できませんが、とりあえず改善。どうやらものすごく微妙なバランスで動いているようです、NSURLCache。 

ただ、割といろんなリソースがNSURLRequestUseProtocolCachePolicyでリクエストされているので、NSURLRequestUseProtocolCachePolicyだとキャッシュを返しちゃだめとなるとローカル保存からWebページを再現するのは難しいのかもしれません。。

NSURLCacheでGoogleのトップページをキャッシュすると落ちる件

原因が特定できなくて困っていた、Googleのトップページでだけ発生する落ちバグがあったわけですが、なんとなく原因が特定できたくさい。
ちなみにこれは次のような現象。

  • Googleのトップページ(http://www.google.co.jp/)の内容を保存しておき、通信しないでUIWebViewで表示しようとすると落ちる。
  • キャッシュの仕方はNSURLCacheでやろうと、別の方法を使っても発生する現象は同じ。
  • 落ちるのはHTTPProtocol::createStreamのあたり。起きているのはEXC_BAD_ACCESS

あちこちのサイトをキャッシュしてみても、上記現象が起きるのはなぜかGoogleだけ。Googleとは言っても、ニュース(http://news.google.co.jp/)では大丈夫だったりします。

さらに、EXC_BAD_ACCESSは通常過剰releaseによって解放済みのメモリにアクセスすると起きるものですが、いくらretainかましても改善しませんでした。過剰releaseならGoogle以外でも発生するだろうし、ランダム要素があるなら http://www.google.co.jp/ で100%発生するというのはおかしいので、やはりGoogleさんのサイト構造に起因するものとして調べてみました。

とはいってもソースもないライブラリの中で起きてる例外なので調べようもなく、できることといえばファイルの構成をよくよくみてみるくらい。じーっ、とよく見てみると、試してみたサイトの中で、唯一 http://www.google.co.jp/ だけHTML5のmanifestファイルが含まれていました!

試しにmanifest指定を削ってみます。 

落ちない! これか!

理屈はわかりませんが、とりあえずmanifest指定を削ってしまうとよいようです。

(2/21追記)

いろいろいじってるうちにまた落ちるようになりました。違うらしい。

[iPhone]サイトをローカル保存するための格闘

iPhoneを使っていて、最もイライラするのは通信状況。電波の受信感度はバッチリでも通信できないということが珍しくありません。
iPhoneのせい、というよりは、Softbankの電波のせいなわけですが。

iPhoneのSafariは(というか普通のブラウザはみな)一回みたページに戻る場合でも 最新かどうかを判断するため通信しようとするので、通信状況が不安定だとブラウジングがものすごくイライラします。

そこで一度みたページをすべてキャッシュし、キャッシュがあれば最新チェックもなにもかもすっ飛ばして表示してくれるアプリを作ろうとしています。が、これがなかなかに難儀しています。

いままでチャレンジしてみたこととしては、

(1) webView:shouldStartLoadWithRequest:navigationType: で保存すべきURLがわかるか?

  1. ドキュメントのURLはこれでわかるけど、画像やCSSなどのリソースについては呼び出されないのでわからない。さらにJavascriptでXHRとかしてるとそれは呼び出されてしまい、なにがページでなにがXHRかわからない。

(2) いっそのこと webView:shouldStartLoadWithRequest:navigationType: に飛んで来たURLからHTMLを取得し、中身をparseして画像やCSSなどのリンクをたどればできるんじゃないか?

  1. HTML、CSSのparseはできた。
  2. GoogleやYahooなどではCSSをJavascriptで後から追加するので、HTMLだけparseしても正しく表示できない。
  3. Javascriptのparseはさすがにちょっと無理。

(3) uiWebView:resource:willSendRequest:redirectResponse:fromDataSource: でなら画像やCSSも含めて呼び出されているっぽいからいけるんじゃない?

  1. 概ねこれで保存対象がわかるようではあるが、一部のリソースは呼ばれない模様。NSURLCacheのcachedResponse:forRequest: をチェックしているとwillSendRequestに飛んで来てないURLがcachedResponse:forRequest: されていたりしている。
  2. キャッシュしているリソースであればrequestのURLをfile://に書き換えたいが、それをすると落ちる。特にGoogleのトップページとか。
  3. 2が回避不能なのでこれだけでは解決できず。

(4) NSURLCacheでどうよ?

  1. キャッシュを返しても落ちない!キャッシュを返すのはこれでイケる。
  2. UIWebViewWebViewDelegateよりも呼ばれる数が多いので、こちらが呼ばれたものが本当に通信しにいっているリソースだと思われる。なので、これで保存対象がわかる。 
  3. storeCachedResponse:forRequest: はredirectされていても呼ばれてしまう模様。NSCachedURLResponseだけではredirectされたものかどうかの判断ができそうにないので、保存は無理。

という流れ。
結論としては、(4)でなんとかなりそう。
ただしstoreCachedResponse:forRequest: はredirectの絡みで使えないため、

  • cachedResponse:forRequest: で保存対象を特定
  • 自力で保存
  • 自力保存したリソースが要求されたらそれをcachedResponse:forRequest: で返す

という実装になりそうである。なかなか大変。