(iOS)サーバーから取得した情報をローカルにキャッシュしておき、オフラインの時はキャッシュ情報を参照する

この記事の追加ネタです。
iOSで自作WebAPIにアクセスする - おかひろの雑記

外出先からスマホタブレットでサーバーにアクセスしようとしても、3GやLTE回線が繋がらなかったり、
繋がったとしても社内システムに接続する場合はVPN接続が必要だったりと、
情報を見ようとしてもオフラインだから見られないというケースも結構あると思います。

なので、一度アクセスした情報はローカルにキャッシュしておき、オンラインであればサーバーから最新の情報を取得してキャッシュに保存、
オフラインの場合はキャッシュがあればその情報を表示・・・という実装を考えてみました。

サーバーから取得してローカルにキャッシュするデータは、下記2パターンを考えます。

  • テキストデータ(JSON
  • ファイル(PDF,JPG,Word,Excel,PowerPointなど。UIWebViewで開く。)


JSONデータの場合
ローカルへの保存はSQLiteでもいいですが、NSUserDefaultsで十分だと思います。
NSArrayやNSDictionaryを保存することもできますが、要素が複雑だとダメな場合もあるみたいなので、
サーバーから取得したNSDataをNSUserDefaultsに保存し、読みだすときにJSON変換するようにします。

	[SVProgressHUD show];
	
	NSString *url = [NSString stringWithFormat:@"http://sample.example.com/webapi/test"];
	
	[AsyncURLConnection request:url completeBlock:^(NSData *data){
		NSError *jsonError = nil;
		self.dataArray = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&jsonError];
		if(!self.dataArray)
		{
			NSLog(@"Error : %@",jsonError);
		
			UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"エラー" message:@"JSONエラー"
			                                              delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
			[alert show];
			[alert release];
		
			[SVProgressHUD dismiss];
		
			return ;
		}
			
		// UserDefaultsに値を保存
		[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"testDataArray"];
		
		dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
		dispatch_async(queue, ^{
		
			dispatch_async(dispatch_get_main_queue(),^{
				// 処理
				...
				
				[SVProgressHUD dismiss];
			});
		});
	}
	errorBlock:^(NSError *error){
		NSError *jsonError = nil;
		NSData *udData = [[NSUserDefaults standardUserDefaults] dataForKey:@"testDataArray"];

		if(udData)
		{
			self.dataArray = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&jsonError];
								 
			// 処理
			...
		}
		else
		{
			NSLog(@"%@",error);
			UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"エラー" message:@"ネットワークエラー"
												   delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
			[alert show];
			[alert release];
		}

		[SVProgressHUD dismiss];
	}];

ファイルの場合
ファイルはUIWebViewで開くのが簡単だと思います。
まずはサーバーにアクセスし、ファイルを取得できたらローカルに保存します。
UIWebViewは、ローカルに保存したファイルを常に開くようにします。
ローカルに保存するときのファイルの拡張子は注意が必要で、これがないとUIWebViewが正しく認識してくれません。

	
	NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://sample.example.com/webapi/testfile.pdf"]];
	NSURLRequest *request = [NSURLRequest requestWithURL:url];
	
	// ローカルに保存するファイルパス
	NSString *fileName = [dictionary objectForKey:@"testfile.pdf"];
	NSString *filePath = [NSString stringWithFormat:@"%@/%@",
						  [NSHomeDirectory() stringByAppendingPathComponent:@"tmp"],
						  fileName];
	
	NSURLResponse *response = nil;
	NSError *error = nil;
	NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
	
	if(error)
	{
		NSLog(@"Error.");
	}
	else
	{
		// ローカルに保存
		NSFileManager *fileManager = [NSFileManager defaultManager];
		[fileManager createFileAtPath:filePath contents:[NSData data] attributes:nil];
		NSFileHandle *file = [[NSFileHandle fileHandleForWritingAtPath:filePath] retain];
		[file writeData:data];
	}
	
	// URLエンコード
	filePath = [filePath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
	NSURLRequest *webViewRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:filePath]];
	// WebViewで開く
	[viewController.webView loadRequest:webViewRequest];