How to Improve the offline use experience of iOS apps and the offline use of iOS apps
App Analysis in App Store
The App is inseparable from us. No matter on the subway, on the bus, or on the venue, you can always see a lot of people taking out their mobile phones, refreshing Weibo, and reading news.
According to incomplete statistics, nearly half of users open apps in non-Wifi environments. The following is umeng background data for a typical iPhone and Android App (50 W + users:
1234- (
void
)viewDidLoad {
[self getArticleList:0 length:SECTION_LENGTH useCacheFirst:YES];
}
Then, in viewDidAppear, request the latest data from the server, as shown in figure
12345678 |
- ( void )viewDidAppear:( BOOL )animated { [super viewDidAppear:animated]; //... [self getArticleList:0 length:SECTION_LENGTH useCacheFirst:NO] } |
Of course, the getArticleList interface here has the useCacheFirst parameter. We need the network request module to support this. The following describes these libraries and tools. (Using some tools, you can easily do this without having to build your own wheels. Follow the principle of "Everything should be the simplest, but not the simplest". Here we will sort it out to facilitate the use of the project ).
1. NSMutableURLRequest
Sample (refer to Qilin's article cache for iOS development (1): memory cache to use NSURLCache ):
1234567891011121314151617181920212223242526272829303132 |
NSString *paramURLAsString= @ "http://www.baidu.com/" ; if ([paramURLAsString length] == 0){ NSLog(@ "Nil or empty URL is given" ); return ; } NSURLCache *urlCache = [NSURLCache sharedURLCache]; /* Set the cache size to 1 MB */ [urlCache setMemoryCapacity:1*1024*1024]; // Create an nsurl NSURL *url = [NSURL URLWithString:paramURLAsString]; // Create a request NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0f]; // Obtain the cache output from the request NSCachedURLResponse *response = [urlCache cachedResponseForRequest:request]; // Determine whether a cache exists if (response != nil){ NSLog(@ "If there is a cache output, obtain data from the cache" ); [request setCachePolicy:NSURLRequestReturnCacheDataDontLoad]; } self.connection = nil; /* Create NSURLConnection */ NSURLConnection *newConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES]; self.connection = newConnection; [newConnection release]; |
However, NSMutableURLRequest is not easy to use. In actual projects, I seldom use it, instead of ASIHTTPRequest.
2. ASIHTTPRequest
You can refer to the introduction here: parse HTTP requests library, which is also an open-source project in Github. However, maintenance has been stopped since iOS 5.0. Available for future projectsAFNetworkingOrMKNetworkKitReplace ASIHTTPRequest.
Introduction to ASIHTTPRequest is as follows:
ASIHTTPRequest is an easy to use wrapper around the CFNetwork API that makes some of the more tedious aspects of communicating with web servers easier. It is written in Objective-C and works in both Mac OS X and iPhone applications.
It is suitable performing basic HTTP requests and interacting with REST-based services (GET / POST / PUT / DELETE). The included ASIFormDataRequest subclass makes it easy to submit POST data and files usingmultipart/form-data.
The API design of the ASIHTTPRequest library is simple and easy to use, and supports multiple functions such as block, queue, and gzip. This is the main reason why this open-source project is so popular.
The ASIHTTPRequest Library provides the ASIWebPageRequest component for requesting Web pages and can request external resources from the web page together. However, I found a serious Bug in my actual project, so it is not recommended to use it.
The introduction of the ASIHTTPRequest Library also mentions that it supports REST-based services. However, when dealing with Restfull APIs, we often use the RestKit described below.
Sample:
12345678910111213141516171819202122232425262728293031323334353637383940 |
NSMutableString *requestedUrl = [[NSMutableString alloc] initWithString:self.url]; // If local data is preferentially used ASICachePolicy policy = _useCacheFirst ? ASIOnlyLoadIfNotCachedCachePolicy : (ASIAskServerIfModifiedCachePolicy | ASIFallbackToCacheIfLoadFailsCachePolicy); asiRequest = [ASIHTTPRequest requestWithURL: [NSURL URLWithString:[requestedUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]; [asiRequest setDownloadCache:[ASIDownloadCache sharedCache]]; [asiRequest setCachePolicy:policy]; [asiRequest setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy]; // Connection if (_connectionType == ConnectionTypeAsynchronously) { [asiRequest setDelegate:self]; [asiRequest startAsynchronous]; // Tell we're receiving. if (!_canceled && [_delegate respondsToSelector:@selector(downloaderDidStart:)]) [_delegate downloaderDidStart:self]; } else { [asiRequest startSynchronous]; NSError *error = [asiRequest error]; if (!error) { [self requestFinished:asiRequest]; } else { [self requestFailed:asiRequest]; } } [requestedUrl release]; |
3. RestKit
Official Website: http://restkit.org/,githubsource project, which deals with Restfull API Web Services. This library is very convenient and provides a complete Cache mechanism.
Sample:
123456789101112 |
+ ( void )setCachePolicy:( BOOL )useCacheFirst { RKObjectManager* objectManager = [RKObjectManager sharedManager]; if (useCacheFirst) { objectManager.client.cachePolicy = RKRequestCachePolicyEnabled; // Use the local Cache. If no Cache is available, request the server } else { objectManager.client.cachePolicy = RKRequestCachePolicyLoadIfOffline|RKRequestCachePolicyTimeout; // Use the local Cache when it is offline or timed out } } |
123456789101112 |
+ ( BOOL )getHomeTimeline:(NSInteger)maxId length:(NSInteger)length delegate:(id<RKObjectLoaderDelegate>)delegate useCacheFirst:( BOOL )useCacheFirst { if (delegate == nil) return NO; [iKnowAPI setCachePolicy:useCacheFirst]; //... } |
Cache requests are just the most basic functions of RestKit, And the really powerful part of RestKit is that it is very easy to work with RESTful web services (https://github.com/RestKit/RestKit/wiki ), restKit can also Cache the data model to Core Data:
Core Data support. Building on top of the object mapping layer, RestKit provides integration with Apple's Core Data framework. This support allows RestKit to persist remotely loaded objects directly back into a local store, either as a fast local cache or a primary data store that is periodically synced with the cloud. RestKit can populate Core Data associations for you, allowing natural property based traversal of your data model. It also provides a nice API on top of the Core Data primitives that simplifies configuration and querying use cases through an implementation of the Active Record access pattern.
But in fact, RKRequestCachePolicy has solved most of the Cache requirements.
4. SDWebImage
SDWebImage is a Github Open-Source Project: Development.
Asynchronous image downloader with cache support with an UIImageView category.
SDWebImage is provided as a Category of UIImageView, so it is very simple to use:
123 |
// Here we use the new provided setImageWithURL: method to load the web image [imageView setImageWithURL:[NSURL URLWithString:@ "http://www.domain.com/path/to/image.jpg" ] placeholderImage:[UIImage imageNamed:@ "placeholder.png" ]]; |
AFNetworking also provides similar functions (UIImageView + AFNetworking ):
12 |
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)]; [imageView setImageWithURL:[NSURL URLWithString:@ "http://i.imgur.com/r4uwx.jpg" ] placeholderImage:[UIImage imageNamed:@ "placeholder-avatar" ]]; |
5. Image Cache in UIWebView
If you want to use UIWebView to display the content offline, You need to implement the following two points:
- Cache Html page
- Cache images and other elements
Using the network components described above to Cache Html pages is more convenient. Then, you can use webView loadHTMLString to load the local Html page, the Cache image needs to be replaced with the NSURLCache public instance as the custom NSURLCache (UIWebView uses the + [NSURLCache sharedURLCache]):
12345 |
// Set the custom Cache Mechanism LocalSubstitutionCache *cache = [[[LocalSubstitutionCache alloc] init] autorelease]; [cache setMemoryCapacity:4 * 1024 * 1024]; [cache setDiskCapacity:10 * 1024 * 1024]; [NSURLCache setSharedURLCache:cache]; |
Custom NSURLCache:
12345678910 |
#import <Foundation/Foundation.h> @interface LocalSubstitutionCache : NSURLCache { NSMutableDictionary *cachedResponses; } + (NSString *)pathForURL:(NSURL*)url; @end |
For details, see LocalSubstitutionCache. h/. m in NewsReader and viewDidLoad in WebViewController. m. The News Reader open source project here references: http://cocoawithlove.com/2010/09/substituting-local-data-for-remote.html
Introduction to the iOS News Reader open source project in NewsReader this article introduces the open source project to improve the offline user experience:
Use all cached data without a network: articles, images, audios, and so on. The main solutions used are described above. For details, see the source code: https://github.com/cubewang/newsreader.
The NewsReader project is already huge because of its historical evolution and needs to be further reconstructed. In subsequent projects, our client structure will be simplified.