こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

iPhoneアプリの開発についてです。

ウェブサイトをローカルに保存しオフラインでUIWebViewに表示したいです。

http://blog.livedoor.jp/sionami/archives/3551091.htmlのサイトを参考にし実行してみましたが、オフラインでは表示されませんでした。また、表示されたサイトは(オンライン時)スマートフォン向けだったページがPC版の表示で表示されました。

以下が実行したプログラムです。コードの一部が間違っているのでしょうか。それともやり方そのものが間違っているのでしょうか。ご指摘お願いします。(エラー時のプログラムは省略しました)

NSString *filePath;

- (void)downloadHTML:(NSString *)html
{
NSString *directoryPath = [NSHomeDirectory() stringByAppendingPathComponent:@"tmp"];
//ダウンロードファイルの保存先
NSString *fileName = [html lastPathComponent];

filePath = [[directoryPath stringByAppendingPathComponent:fileName] stringByStandardizingPath];//保存したファイルへのパス

NSURL *url = [NSURL URLWithString:html];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [
NSURLConnection
sendSynchronousRequest : request
returningResponse : &response
error : &error
];
//ファイル書き込み
NSFileManager *fm = [NSFileManager defaultManager];
[fm createFileAtPath:filePath contents:[NSData data] attributes:nil];
NSFileHandle *file = [NSFileHandle fileHandleForWritingAtPath:filePath];
[file writeData:data];
}

- (IBAction)refresh:(UIButton *)sender {
NSString *htmlStr = [NSString stringWithContentsOfFile:filePath];
[_myWebView loadHTMLString:htmlStr baseURL:nil];
}

また、http://qiita.com/EntreGulss/items/8da6847e4dae59bfb1ebのサイトの方法も試しましたが同じくオフラインで表示されませんでした。

投稿日時 - 2014-05-12 14:20:30

QNo.8591946

困ってます

質問者が選んだベストアンサー

> iOSシミュレータでは機内モードができずずっとPCのWIFIを切って実行していたのでそれが原因だったと思います。

シミュレータなら、その方法でオフラインにできるはずです。
サンプルプロジェクトのRNCachingURLProtocol.mのuseCacheメソッド内の
reachableがNOになればオフラインと認識されるわけですが、
私の方でシミュレータを使ってWi-FiをOFFにしたらreachableはNOになりました。
うまく行かないならオフラインモードとオンラインモードを切り替えるスイッチを
画面内に作り、useCacheメソッドでそのスイッチの値を返却するよう
サンプルプロジェクトを改造してもよいと思います。

で、このサンプルプロジェクトがちゃんと動作しているのかもう少し調べてみました。
すると、cnn.com、bbc.comとwww.yahoo.co.jpはちゃんとキャッシュできるのですが、
yahoo.comとwww.google.co.jpは一部しかキャッシュされてなく、正しく動作して
いないことがわかりました。
(www.yahoo.co.jpとwww.yahoo.co.jpは独自で追加したものです。)

その原因を調べたところ、yahoo.comとwww.google.co.jpは内部でhttpsに
リダイレクトされているようで、このサンプルプロジェクトがhttpsのページを
キャッシュするようにできていないためでした。

問題は、このサンプルプロジェクトのRNCachingURLProtocol.mの
canInitWithRequestメソッド内で[[request URL] scheme]が
@"http"の場合だけYESを返すようになっているところです。
ここを@"http"または@"https"の場合とするよう条件式を変更するだけで、
httpsのページにも対応でき、yahoo.comとwww.google.co.jpも
正しくキャッシュできるようになりました。

ここを修正して、うまくオフライン表示できるか試してみてはどうでしょうか?


> ここから特定のキャッシュのパスを取得するにはどうしたらいいでしょうか?
> またパスを取得できたとして
> [self.webView loadRequest:[[NSURLRequest alloc] initWithURL:[NSURL URLWithString:path]]];
> でサイトはオフラインで開けるのでしょうか?

この質問の意味があまりちゃんと理解できてないのですが、
サンプルプロジェクトでキャッシュしたデータはどこに保存されているか?
という意味でしたら、
RNCachingURLProtocol.mのcachePathForRequestメソッドで
リクエストURLに対応するキャッシュファイル名を求めているので
その処理を確認すればよいと思います。

また、このキャッシュファイルにデータを書きだす処理は
RNCachingURLProtocol.mのconnection:willSendRequestメソッドの
[NSKeyedArchiver archiveRootObject:cache toFile:cachePath];
の部分です。
そして、キャッシュファイルからデータを読み込む処理は
RNCachingURLProtocol.mのstartLoadingメソッドの
RNCachedData *cache = [NSKeyedUnarchiver unarchiveObjectWithFile:[self cachePathForRequest:[self request]]];
になります。
このあたりが理解できればキャッシュを読み書きする処理も
自分の好きなように作れると思います。

ただ、このサンプルプロジェクトはかなり難しいので、本当に理解するには
「URLローディングシステムプログラミングガイド」
https://developer.apple.com/jp/devcenter/ios/library/documentation/URLLoadingSystem.pdf
をちゃんと読んだ方がよいと思います。

投稿日時 - 2014-05-16 18:08:12

お礼

質問が分かりにくくなってしまってすみません。
でもなんとか意図は伝わっているようなので良かったです。
わざわざそちらでテストまでしてもらってありがとうございます。
あとは何とか自分でやってみようと思います。

投稿日時 - 2014-05-16 18:30:53

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(2)

ANo.1

質問に書かれているdownloadHTMLメソッドとrefreshメソッドを使って
ごく簡単なHTMLファイルをキャッシュし、オフラインで表示してみましたが
問題なく表示されましたよ。

ただ、このコードは、HTMLファイル内の画像リンク(img srcタグ)等、
別ファイルで定義されている情報を自動的に読み込んでキャッシュする
機能はありません。
したがって、画像ファイルのあるWebページやCSSファイル/JavaScriptファイルを
使ったページ等、要するに一般的なWebサイトには対応できないと思います。

また、
> 表示されたサイトは(オンライン時)スマートフォン向けだったページがPC版の表示で表示されました。

この原因は、NSURLRequestでリクエストする時のUser-Agentが
「User-Agent: (アプリ名)/(ver.) CFNetwork/(ver.) Darwin/(ver.)」
という形式になるため、iPhoneのブラウザからのアクセスではなく
アプリからの独自リクエストと認識されたためだと思います。
(User-Agentのことを知らなければ検索してください)


で、一般的なWebサイトの情報をiPhoneからアクセスした形でローカルに
保存したいということであれば、質問の最後に書かれてあった
http://qiita.com/EntreGulss/items/8da6847e4dae59bfb1eb
の方法で実装する必要があると思います。
質問では、この方法でもオフラインで表示されなかったとのことですが、
この記事は概要が書かれてあるだけで、実際にはその記事から
リンクされている
https://github.com/EntreGulss/CachedWebpage
のサンプルプロジェクトをダウンロードして実行するだけだと思います。

実際に私の方でダウンロード/実行してみたところ、古いプロジェクトなので
AutoLayoutの使用でエラーが出たりしましたが、AutoLayoutの使用をOFFに
すれば、普通に実行できて、一般的なyahoo等のWebページをキャッシュし
オフラインで表示できました。

なお、このサンプルは通信経路があるかどうかを「Reachability」という
クラスを使って判断し、通信経路がない場合だけキャッシュから読み込みますから
一度Webサイトにアクセスした後、機内モードに変更する等した上で
再読み込みボタンを押す必要があります。
そうするとキャッシュから読まれます。

あと、JavaScriptを使ってページ内で追加データを取得するようなケースには
このサンプルでも当然対応できませんから、うまく表示されない時は
問題のWebページがどのような内容になっているか調べる必要があると思います。

投稿日時 - 2014-05-13 10:46:07

補足

回答ありがとうございます。
iOSシミュレータでは機内モードができずずっとPCのWIFIを切って実行していたのでそれが原因だったと思います。
私はまだ実機でテストできる環境ではないのでキャッシュを読み込めるならhttp://qiita.com/EntreGulss/items/8da6847e4dae59bfb1ebのやり方で作業の方を進めていこうと思います。
そこで、取得した特定のキャッシュをNSUserDefaultなどに入れておき自由に取り出せるようキャッシュのパスを取得→保存→取り出し→読み込みをしたいと思います。
http://d.hatena.ne.jp/ntaku/20110104/1294146555にキャッシュが保存されるというキャッシュディレクトリのパスがありました。
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *dir = [paths objectAtIndex:0];
ここから特定のキャッシュのパスを取得するにはどうしたらいいでしょうか?
またパスを取得できたとして
[self.webView loadRequest:[[NSURLRequest alloc] initWithURL:[NSURL URLWithString:path]]];
でサイトはオフラインで開けるのでしょうか?
差し支えなければ回答お願いします。

投稿日時 - 2014-05-16 00:10:33

あなたにオススメの質問