Simple use of iOS NSURLConnection

Recently, there are some offline breakpoint download requirements involved in the process of doing the project, and the two objects of NSURLConnection and NSRULSession are used. Most of the working time is dealing with AFNetworking, so they are not so proficient in using these two objects. Therefore, I want to sort them out today for future viewing

We all know that after iOS 7, the NSURLSession object basically replaces the NSURLConnection for network development. After iOS 9, the relevant methods of NSURLConnection are completely abandoned. The iOS system has the feature of downward compatibility. Although NSURLConnection has been abandoned, its methods can still be used in development, and if it needs to be compatible with a very low version of iOS system, Sometimes you have to use the NSURLConnection class.

Network requests are divided into synchronous and asynchronous. Synchronization means that the program code will be stuck at the request before the request result is returned, and the subsequent code will not be executed. Asynchronous means that after sending the request, the code will be executed while receiving the returned data in the sub thread. When the returned data is received, the main thread will be notified to handle it by callback.

Synchronous Get request using NSURLConnection

// 1. Determine the request path
NSURL *url = [NSURL URLWithString:@"http://xxxx.htm?username=gzp&pwd=123&type=JSON"];
    
// 2. Create a request object. The request method -- > defaults to GET
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:url];

// 3. Real type of sending request: nshtturlresponse
NSHTTPURLResponse *response = nil;
/*
  First parameter: request object
  Second parameter: response header information
  Third parameter: error message
  Return value: response body, only
*/
// The method is blocked, that is, if the method is not executed, the subsequent code will not be executed
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
    
// 4. Parse the response data --- > string
NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);

Asynchronous Post request using NSURLConnection

There is a big disadvantage of using synchronization to make a request, that is, when making a network request, the return of data often takes a certain time and cannot be completed in an instant. Using synchronization will cause the interface to get stuck. In this case, the user experience is very bad
The NSURLConnection class provides two ways to perform asynchronous request operations.

Use block to make asynchronous requests

// 1. Determine the request path
NSURL *url = [NSURL URLWithString:@"http://xxx.htm"];

// 2. Create variable request object
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    
// 3. Modify the request method. POST must be in uppercase
request.HTTPMethod = @"POST";

// request.HTTPMethod = @"HEAD"; Only the first part of the page is requested.

// You can set some properties of the request header, such as request timeout
request.timeoutInterval = 10;

// Set the request header user agent note: the key s must be consistent (used to transfer data to the background)
[request setValue:@"ios 10.1" forHTTPHeaderField:@"User-Agent"];
    
// Can be used for breakpoint continuation
// [request setValue:[NSString stringWithFormat:@"bytes=%zd-",self.currentSize] forHTTPHeaderField:@"Range"];

// 4. Set request body information, string -- > nsdata
request.HTTPBody = [@"username=gzp&pwd=123&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];

// 3. Send asynchronous request
/*
  First parameter: request object
  The second parameter: the queue determines the calling thread of the code block completionHandler
  The third parameter: completionHandler calls back when the request is completed (success | failure)
    response:Response header
    data:Responder
    connectionError:error message
*/
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {

    // 6. Parse the data, nsdata -- > nsstring, but the data can only be obtained after the response is completed
    NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
    }];

This method is simpler than the following proxy method, but the block method cannot monitor the progress of the response

Asynchronous request mode using proxy callback

First, you need to comply with the protocol and declare that a variable NSData is used to receive data:

@interface ViewController ()<NSURLConnectionDataDelegate>
{
    NSMutableData * _data;
}
@end

Send request

// 1. Determine the request path
NSURL *url = [NSURL URLWithString:@"http://xxx?username=123&pwd=123&type=JSON"];
    
// 2. Create request object
NSURLRequest *request = [NSURLRequest requestWithURL:url];

// 3. Set up agent and send request
// [NSURLConnection connectionWithRequest:request delegate:self];

[[NSURLConnection alloc]initWithRequest:request delegate:self];

Callback method

#pragma mark ----------------------
#pragma mark NSURLConnectionDataDelegate
//1. Called when the server response is received
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    NSLog(@"%s",__func__);
}

//2. Call when receiving the data returned by the server. Call multiple times
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
     NSLog(@"%s",__func__);
    
    //Splicing data (if the returned data is large, this method will cause memory to soar)
    [self.resultData appendData:data];
}
//3. Called when the request fails
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
     NSLog(@"%s",__func__);
}

//4. Called at the end of the request
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"%s",__func__);
    
    NSLog(@"%@",[[NSString alloc]initWithData:self.resultData encoding:NSUTF8StringEncoding]);
}

In the didReceiveData: method, splice all the received data. After all the data is obtained, analyze and process it in the connectionDidFinishLoading: method In addition, when doing network development, we must consider the processing of network delay, which can be handled by setting the request timeout

NSMutableURLRequest request request object

NSMutableURLRequest is a subclass of NSURLRequest. The common methods are

Set the request timeout waiting time. If no response is received from the server within the specified time, the request is considered to have failed. The default is 60s, but it is recommended to be between 15 and 30s

-(void)setTimeoutInterval:(NSTimeInterval)seconds;

cachePolicy cache policy
NSURLRequestUseProtocolCachePolicy = 0, default policy
Nsurlrequestreloadignorenglocalcachedata = 1. The local cache is ignored every time it is loaded from the server. It is generally used in applications with high real-time requirements, such as stock / 12306, etc.

The following two are generally used in developing offline applications. Offline applications generally need two databases, one is the local database Sqlite3 and the other is the server database.
NSURLRequestReturnCacheDataElseLoad = 2. If there is a cache, the cached data will be returned. If there is no cache, it will be loaded from the server.
NSURLRequestReturnCacheDataDontLoad = 3. If there is a cache, the cached data will be returned. If there is no cache, it will not be loaded

Set the request method (such as GET, POST or Head)

- (void)setHTTPMethod:(NSString *)method;

Set request body

- (void)setHTTPBody:(NSData *)data;

Set request header

- (void)setValue:(NSString *)value forHTTPHeaderField:(NSString *)field;

Nshtturlresponse response object

statusCode: status code, which can be used to judge whether the request is wrong.
allHeaderFields: get the content of the response body
URL: it is generally used in redirection. If redirection is not required, the response URL is the same as the request URL.
MIMEType: the server tells the client the type of data returned
Textencoding Name: the server tells the client the encoding format of the returned content
expectedContentLength: the length of the data returned by the server. The client can obtain the file size through this attribute
suggestedFilename: the name that the server suggests the client use to save the file

Well, after talking so much, I finally posted a section of code downloaded from offline breakpoints:

@interface ViewController ()<NSURLConnectionDataDelegate>
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;

@property (nonatomic, assign) NSInteger totalSize;//Total file size

@property (nonatomic, assign) NSInteger currentSize;//Downloaded size

@property (nonatomic, strong) NSString *fullPath;/** Sandbox path */

@property (nonatomic, strong) NSURLConnection *connect;/** Connection object */

@property (nonatomic, strong) NSOutputStream *stream;/** Output stream*/
@end
@implementation ViewController

/**Start downloading*/
- (IBAction)startBtnClick:(id)sender {
    [self download];
}
/**Cancel Download*/
- (IBAction)cancelBtnClick:(id)sender {
    [self.connect cancel];
}
/**Continue downloading*/
- (IBAction)goOnBtnClick:(id)sender {
    [self download];
}


-(void)download
{
    NSURL *url = [NSURL URLWithString:@"http://www.33lc.com/article/UploadPic/2012-10/2012102514201759594.jpg"];
    
    //2. Create request object
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    
    //Set the request header information and continue the transmission at the offline breakpoint.
    NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentSize];
    [request setValue:range forHTTPHeaderField:@"Range"];
    
    //3. Send request
    NSURLConnection *connect = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    self.connect = connect;
}

#pragma mark ----------------------
#pragma mark NSURLConnectionDataDelegate
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    //1. Get the total size of the file (the total size of the file data requested this time! = the total size of the file)
    // self.totalSize = response.expectedContentLength + self.currentSize;
    
    if (self.currentSize >0) {
        return;
    }
    
    self.totalSize = response.expectedContentLength;
    
    //2. Write data into sandbox
    self.fullPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject]stringByAppendingPathComponent:@"123.jpg"];
    
    //3. Create output stream
    /*
     First parameter: path of file
     Second parameter: YES append
     Features: if there is no file at the address pointed to by the output stream, an empty file will be automatically created
     */
    NSOutputStream *stream = [[NSOutputStream alloc]initToFileAtPath:self.fullPath append:YES];
    
    //Stream output on
    [stream open];
    self.stream = stream;
}

-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    //Write data to the output stream
    [self.stream write:data.bytes maxLength:data.length];
    
    //3. Accumulate downloaded data;
    self.currentSize += data.length;
    
    //Progress = total size of downloaded / files
    self.progressView.progress = 1.0 *  self.currentSize/self.totalSize;
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    
    //After downloading, close the output stream
    [self.stream close];
    self.stream = nil;
    NSLog(@"%@",self.fullPath);
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
}

When downloading some large files, in order to avoid soaring memory, we often use
The nsoutstream output stream or NSFileHandler file handle is used for downloading. The above code uses the output stream method. Be sure to remember to open the output stream before downloading and close it after downloading.

supplement

Request method of HTTP protocol:
HTTP request methods are not only GET and POST requests, but these two requests are more commonly used. In HTTP/1.1 protocol, eight methods for sending HTTP requests are defined: OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE and CONNECT.

So here we focus on some HEAD requests
The essence of HEAD and GET is the same. The difference is that HEAD does not contain rendering data, but only HTTP header information. It is often used to test the validity, usability and recent modifications of hyperlinks.

Added by gammaman on Thu, 03 Mar 2022 20:06:30 +0200