iOS开发非完全指南之: 网络请求
现如今单机版本的 APP 几乎不复存在,我们需要掌握如何使用 iOS/web/Android 如何发送 http/https 请求给后端服务,根据响应数据渲染页面。本文主要介绍如何使用 iOS 建立网络请求。
一、网络请求基础使用
1. 网络请求主流程
要实现网络请求需要经历如下几个步骤:
- 构造请求的 URL
- 创建一个网络请求,网络请求不指定方法默认是 GET
- 创建网络管理
- 创建一个网络请求任务,并处理响应信息;如有更新 UI 需要回到主线程
- 开启网络请求任务任务
一个请求百度首页图片的例子:
- (void) network{// 1. 创建一个url
NSURL *url= [NSURL URLWithString: @"https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png"];
// 2. 创建一个网络请求,网络请求不指定方法默认是GET
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
// request.HTTPMethod = @"GET";
// 自定义请求配置
// NSURLSessionConfiguration *config = [[NSURLSessionConfiguration alloc] init];
// config.timeoutIntervalForRequest= 20;// 请求超超时时间
// //...还有很多参数
// NSURLSession *session = [NSURLSession sessionWithConfiguration: config];
// 3. 创建网络管理
NSURLSession *session = [NSURLSession sharedSession];
// 4. 创建一个网络任务
/*
第一个参数 : 请求对象
第二个参数 :
completionHandler回调 ( 请求完成 ["成功"or"失败"] 的回调 )
data : 响应体信息(期望的数据)
response : 响应头信息,主要是对服务器端的描述
error : 错误信息 , 如果请求失败 , 则error有值
*/
NSURLSessionDataTask *task= [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// data就是服务器返回的数据,response为服务器的响应
if(!error){
// 强转为NSHTTPURLResponse
NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
if(res.statusCode == 200){
// 更新UI必须回到主线程
dispatch_async(dispatch_get_main_queue(), ^{
self.imgView.image = [UIImage imageWithData:data];
});
}
}else{
NSLog(@"报错啦,%@",error);
}
}];
// 5. 开启任务
[task resume];
}
如果你把上面的 https 请求更换成 http,你会发现如下报错:
报错啦,Error Domain=NSURLErrorDomain Code=-1022 "The resource could not be loaded because the App Transport Security policy requires the use of a secure connection." UserInfo={NSUnderlyingError=0x60000246d4d0 {Error Domain=kCFErrorDomainCFNetwork Code=-1022 "(null)"}, NSErrorFailingURLStringKey=http://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png, NSErrorFailingURLKey=http://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png, NSLocalizedDescription=The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.
其主要原因是 iOS 默认使用 https 请求,而不是 http;所以会拦截 http 请求,所以会报错。
2. ATS 配置保证访问 Http
要保证 iOS 能够访问 http 请求;需要在 Info.plist 文件中新增如下配置:
<key>NSAppTransportSecurity</key><dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
3. GET 请求 JSON 并解析
这里使用一个真实的环境,请求 json 数据,返回转换成 Model 并天气列表。主要步骤如下:
- 构建一个 url,带中文的 url 进行转换
- 创建一个网络请求(url;
- 创建网络管理
- 创建一个网络任务
- 网络错误判断
- 将 JSON 数据 NSData 转换成 NSDictionary(因为服务器返回的是一个{}格式的)
- 获取数据创建 Model
- 回到主线程更新数据和 UI
- 开启任务
#import "ViewController.h"
#import "Weather.h"
#import "WeatherCell.h"
@interface ViewController ()<UITableViewDataSource, UITableViewDelegate>
@property (nonatomic, span) NSMutableArray<Weather *> * weathers;
@property (weak, nonatomic) IBOutlet UITableView *weatherTableView;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *indicator;
@end
@implementation ViewController
-(NSMutableArray<Weather *> *)weathers{
if (_weathers == nil) {
// 需要初始化weathers,防止不存在数据,调用weathers.count等报错
_weathers = [NSMutableArray arrayWithCapacity:7];
}
return _weathers;
}
- (void)viewDidLoad {
[super viewDidLoad];
// 设置行高,防止展示高度不足
self.weatherTableView.rowHeight = 100.0;
// 删除Tableview底部的空白区域
self.weatherTableView.tableFooterView = [[UIView alloc]init];
[self weather];
}
-(void)weather{
//1.创建一个url
NSString *net = @"http://v.juhe.cn/weather/index?format=2&cityname=重庆&key=2d2e6e836dbdffac56814bc4d449d507";
//带中文的url进行转换
net = [net stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
NSURL *url = [NSURL URLWithString:net];
//2.创建一个网络请求(url)
NSURLRequest *request = [[NSURLRequest alloc]initWithURL:url];
//request.HTTPMethod = @"POST";
// 自定义请求配置
// NSURLSessionConfiguration *config = [[NSURLSessionConfiguration alloc] init];
// config.timeoutIntervalForRequest= 20;// 请求超超时时间
// //...还有很多参数
// NSURLSession *session = [NSURLSession sessionWithConfiguration: config];
//3.创建网络管理,
NSURLSession *session = [NSURLSession sharedSession];
//4.创建一个网络任务
NSURLSessionDataTask * task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
NSLog(@"有错误");
}
else {
//需要转换成NSHTTPURLResponse
NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
NSLog(@"%ld", (long)res.statusCode);
/** NSJSONReadingOptions
* NSJSONReadingMutableContainers = (1UL << 0),
* 容器可变,NSMutableDictionary 或NSMutableArray。
*
* NSJSONReadingMutableLeaves = (1UL << 1),
* 叶子可变,返回的 JSON 对象中字符串的值为 NSMutableString。
*
* NSJSONReadingAllowFragments = (1UL << 2)
* 允许 JSON 字符串最外层既不是 NSArray 也不是 NSDictionary,但必须是有效的 JSON 片段
*/
//JSON(字典)转模型
NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
//取未来7天天气
NSArray *future = dic[@"result"][@"future"];
for(int i = 0; i < future.count; i++){
NSDictionary *wd = future[i];
// 此处创建Weather还有优化空间,详情请见下一节 使用KVC的方式优化json转换Model
Weather *w = [[Weather alloc]init];
w.temperature = wd[@"temperature"];
w.weather = wd[@"weather"];
w.wind = wd[@"wind"];
w.week = wd[@"week"];
w.date_y = wd[@"date"];
[self.weathers addObject:w];
}
NSLog(@"%ld", self.weathers.count);
//默认网络请求在自线程 更新界面要回到主线程
dispatch_async(dispatch_get_main_queue(), ^{
// 模拟加载数据2s,加一点进度条;现实情况是不需要的
[NSThread sleepForTimeInterval:2.0];
//刷新界面
[self.weatherTableView reloadData];
[self.indicator stopAnimating];
});
}
}];
//5.开启任务
[task resume];
}
- (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
WeatherCell *cell = [tableView dequeueReusableCellWithIdentifier:@"weather"];
cell.w = self.weathers[indexPath.row];
return cell;
}
- (NSInteger)tableView:(nonnull UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.weathers.count;
}
@end
上面的代码展示了如何请求网络并展示列表,列表如何展示详情请见:iOS 开发-非完全指南之: TableView 的使用详解
4. 使用 KVC 的方式优化 json 转换 Model(优化)
此处的案例是基于上面的 json 请求;前面再获取到 json 数据转换成 NSDictonaray,需要自己手动的创建 Weather 并读取 Key 赋值,在真实的场景下很容易将字段的 key 值写错,所以我们需要使用到 KVC 的方式赋值。
优化前的代码
for(int i = 0; i < future.count; i++){NSDictionary *wd = future[i];
Weather *w = [[Weather alloc]init];
w.temperature = wd[@"temperature"];
w.weather = wd[@"weather"];
w.wind = wd[@"wind"];
w.week = wd[@"week"];
w.date_y = wd[@"date"];
[self.weathers addObject:w];
}
优化后的代码
for(int i = 0; i < future.count; i++){NSDictionary *wd = future[i];
Weather *w = [[Weather alloc] initWithDictionary:wd];
[self.weathers addObject:w];
}
需要在 Weath 中构造一个initWithDictionary
初始化函数:
//// Weather.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Weather : NSObject
@property (nonatomic, copy) NSString * temperature;
@property (nonatomic, copy) NSString * weather;
@property (nonatomic, copy) NSString * wind;
@property (nonatomic, copy) NSString * week;
@property (nonatomic, copy) NSString * date_y;
- (instancetype)initWithDictionary:(NSDictionary *) dic; // 新增的
@end
NS_ASSUME_NONNULL_END
然后在 Weather.m 中实现其函数
//// Weather.m
#import "Weather.h"
@implementation Weather
-(instancetype)initWithDictionary:(NSDictionary *)dic {
if (self = [super init]) {
// 使用KVC的方式给属性赋值,直接将key给Model中的key
[self setValuesForKeysWithDictionary:dic];
}
return self;
}
//属性与字典不匹配时进行改正,不改的话不会崩溃但拿不到值
- (void)setValue:(id)value forKey:(NSString *)key{
//在这里更改key
if([key isEqualToString:@"date"]){
key = @"date_y";
}
[super setValue:value forKey:key];
}
//冗错处理,如果有未定义的字段的话就会走到这里,不重写的话会引起崩溃
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{
NSLog(@"value:%@,undefineKey:%@",value,key);
}
@end
总结:
可以使用
setValuesForKeysWithDictionary
将字典中的 key 和 value 映射到我们的 Model 中;setValuesForKeysWithDictionary
使用前提是返回的 json 和 Model 的属性相同/类似的情况;- 在映射的过程中必须处理字段不同
setValue,forKey
以及不需要的字段forUndefinedKey
- 当服务器返回的字段为 iOS 中的关键字也需要使用
setValue,forKey
进行转换 - 使用 KVC 的方式能够更好的封装 dic to model 的转换;避免在代码中直接的获取和赋值
5. 发送 POST 请求
发送 POST 网络请求,主要流程和 GET 请求类似,主要差异在构建NSURLRequest
的部分;一个简单的例子如下:
//1.创建可变的请求对象
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//2.修改请求方法为POST
request.HTTPMethod = @"POST";
//4.设置请求体信息,字符串--->NSData
request.HTTPBody = [@"username=mrgaogang&pwd=123" dataUsingEncoding:NSUTF8StringEncoding];
6. 原生文件下载
写在前面: 建议使用 AFNetworking 的文件下载
使用原生的方式实现文件下载主要有如下几个步骤:
- 确定需要下载的文件 url
- 创建去请求对象
- 使用
sessionWithConfiguration
创建 Session 对象,并设置请求代理NSURLSessionTaskDelegate
和队列- 实现代理方法监听下载进度:
downloadTask bytesWritten totalBytesWritten
- 实现代理方法实现下载完成存储:
downloadTask didFinishDownloadingToURL
- 实现代理方法监听下载进度:
- 使用
downloadTaskWithRequest
下载文件 - 启动 Task
一个简单的例子:
#import "ViewController.h"@interface ViewController () <NSURLSessionDownloadDelegate>
@property (weak, nonatomic) IBOutlet UIProgressView *downloadProgress;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
// 1.确定URL
NSURL *url = [NSURL URLWithString:@"http://localhost:8080/AppTestAPI/wall.png"];
// 2.创建请求对象
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 3.创建会话对象
//NSURLSession *session = [NSURLSession sharedSession];
// Configuration:配置信息,用默认的即可;将下载的代理方法设置为self并设置为主队列
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithRequest:request];
[downloadTask resume];// 执行Task
}
/**
1.写数据(监听下载进度)
session 会话对象
downloadTask 下载任务
bytesWritten 本次写入的数据大小
totalBytesWritten 下载的数据总大小
totalBytesExpectedToWrite 文件的总大小
*/
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
self.downloadProgress.progress = 1.0 * totalBytesWritten / totalBytesExpectedToWrite;
}
/**
3.当下载完成的时候调用
location 文件的临时存储路径,在沙盒中的tmp目录下
*/
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
// 1.拼接文件全路径
// downloadTask.response.suggestedFilename 文件名称
NSString *fullPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:downloadTask.response.suggestedFilename];
// 2.剪切文件
[[NSFileManager defaultManager]moveItemAtURL:location toURL:[NSURL fileURLWithPath:fullPath] error:nil];
NSLog(@"%@",fullPath);
}
@end
7. 原生的文件上传
写在前面:建议使用 AFNetworking 的文件上传功能
原生的文件上传功能比较复杂,主要分为如下几个步骤:
- 设置上传的 url
- 创建请求对象
- 设置请求头
Content-Type
等 - 设置请求方法为 POST , 一般上传走的是 post 请求
- 创建请求 Session 代理,
NSURLSessionTaskDelegate
并实现如下方法
- 监听上传进度:
task didSendBodyData totalBytesSent
- 监听是否上传完成:
task didCompleteWithError
- 创建请求 Session 代理,
- 创建上传 Task:
uploadTaskWithRequest
- 并封装上传数据
fromData
包括启动标记,文件参数和结束标记
- 创建上传 Task:
- 启动 Task
一个简单的例子:
#import "ViewController.h"
@interface ViewController ()<NSURLSessionTaskDelegate>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *url = [NSURL URLWithString:@"上传的url"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:url];
NSString *httpHead = [NSString stringWithFormat:@"multipart/form-data;boundary=----WebKitFormBoundaryUFNaH6losNxu4xDq"];
//设置请求的头 告诉服务器我要上传数据
[request setValue:httpHead forHTTPHeaderField:@"Content-Type"];
request.HTTPMethod = @"POST";
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
//fromData:就是要上传的数据
NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:[self getData] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"%@", [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
}];
[task resume];
}
-(NSData *)getData
{
/******************************************************************/
// 设置请求体
// 设置请求体
// 给请求体加入固定格式数据 这里也是使用的也是可变的,因为多嘛
NSMutableData *data = [NSMutableData data];
// 开始标记
// boundary
[data appendData:[@"------WebKitFormBoundaryUFNaH6losNxu4xDq" dataUsingEncoding:NSUTF8StringEncoding]];
// rn换行符
[data appendData:[@"rn" dataUsingEncoding:NSUTF8StringEncoding]];
// Content-Disposition: form-data; name="myfile"; filename="wall.jpg"
[data appendData:[@"Content-Disposition: form-data; name="myfile"; filename="123.jpg"" dataUsingEncoding:NSUTF8StringEncoding]];
// rn换行符
[data appendData:[@"rn" dataUsingEncoding:NSUTF8StringEncoding]];
// Content-Type 上传文件的MIME
[data appendData:[@"Content-Type: image/jpeg" dataUsingEncoding:NSUTF8StringEncoding]];
// 两个换行符
[data appendData:[@"rn" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:[@"rn" dataUsingEncoding:NSUTF8StringEncoding]];
// 上传文件参数
//图片数据 并且转换为Data
UIImage *image = [UIImage imageNamed:@"wall.jpg"];
NSData *imagedata = UIImageJPEGRepresentation(image, 1.0);
[data appendData:imagedata];
//如果是PNG图片需要修改上面几个地方 数据格式如下
// UIImage *image2 = [UIImage imageNamed:@"wall2"];
// NSData *imagedata2 = UIImagePNGRepresentation(image2);
// [data appendData:imagedata2];
//
//如果上传的是zip压缩包
//NSString *path = [[NSBundle mainBundle] pathForResource:@"wall.zip" ofType:nil];
//[data appendData:[NSData dataWithContentsOfFile:path]];
// 两个换行符
[data appendData:[@"rn" dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:[@"rn" dataUsingEncoding:NSUTF8StringEncoding]];
// 添加结束标记
// rn换行符
[data appendData:[@"------WebKitFormBoundaryUFNaH6losNxu4xDq--" dataUsingEncoding:NSUTF8StringEncoding]];
// boundary
[data appendData:[@"rn" dataUsingEncoding:NSUTF8StringEncoding]];
return data;
}
/*
只要给服务器上传数据就会调用 (一次或多次)
bytesSent: 当前这一次发送的数据长度
totalBytesSent: 总共已经发送的数据长度
totalBytesExpectedToSend: 需要上传的文件的总大小
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
NSLog(@"%lld", 100 * totalBytesSent / totalBytesExpectedToSend);
}
/*
判断是否上传成功,如果失败error是具有值
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
NSLog(@"%s, %@", __func__, error);
}
@end
二、常见第三方库使用
1. AFNetworking 的使用
git 地址: AFNetworking
普通网络请求
AFNetworking 的使用相比原生请求主要有如下差异:
- 使用了
NSURLSessionConfiguration
并自定义了 NSURLSession - 使用
AFURLSessionManager
封装了文件上传,下载及断点续传和普通的网络请求 - 对于普通网络请求直接返回响应后转换的数据 responseObject; 无需自己通过 NSData 转换成 JSON
- 无需手动回到主线程执行 UI 更新操作
-(void)weather{
//创建管理者
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
//创建网络请求
NSString *net = @"http://v.juhe.cn/weather/index?format=2&cityname=重庆&key=2d2e6e836dbdffac56814bc4d449d507";
net = [net stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
NSURL *URL = [NSURL URLWithString:net];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
//创建任务
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request uploadProgress:^(NSProgress * _Nonnull uploadProgress) {
} downloadProgress:^(NSProgress * _Nonnull downloadProgress) {
} completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
if (error) {
NSLog(@"Error: %@", error);
} else {
//需要转换成NSHTTPURLResponse
NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
NSLog(@"%ld", (long)res.statusCode);
NSLog(@"%@ %@", response, responseObject);
// 网络请求直接返回响应后转换的数据responseObject,无需自己手动转换
//取未来7天天气
NSArray *future = responseObject[@"result"][@"future"];
for(int i = 0; i < future.count; i++){
NSDictionary *wd = future[i];
Weather *w = [[Weather alloc]initWithDictionary:wd];
[self.weathers addObject:w];
}
//刷新界面,无需回到主线程
[self.weatherTableView reloadData];
[self.indicator stopAnimating];
}
}];
//启动任务
[dataTask resume];
}
文件上传
注意点:
- 主要使用
uploadTaskWithRequest
完成上传可以使用 process 监听上传进度
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:@"http://example.com/upload"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURL *filePath = [NSURL fileURLWithPath:@"file://path/to/image.png"];
NSURLSessionUploadTask *uploadTask = [manager uploadTaskWithRequest:request fromFile:filePath progress:nil completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
NSLog(@"Error: %@", error);
} else {
NSLog(@"Success: %@ %@", response, responseObject);
}
}];
[uploadTask resume];
文件下载
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:@"https://dss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2529366712,3648628205&fm=26&gp=0.jpg"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
// 此处将文件存储在沙盒中的Document中名称就使用建议的名称,也就是文件默认的名称
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
return [documentsDirectoryURL URLByAppendingPathComponent:[response suggestedFilename]];
// 或者使用NSSearchPathForDirectoriesInDomains的方式
// NSString *documentsDirectoryURL = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
// NSString *fileUrl = [documentsDirectoryURL stringByAppendingPathComponent:response.suggestedFilename];
// NSURL *url = [NSURL fileURLWithPath:fileUrl]; // 注意此处必须是fileURLWithPath
// 此处将文件存储在沙盒中的Document中名称就使用建议的名称,也就是文件默认的名称
// return url;
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
NSLog(@"File downloaded to: %@", filePath);
}];
[downloadTask resume];
请求参数序列化
请求支持 NSDictionary 不需要再使用?拼接;也支持 POST 请求 HTTPBody
NSString *URLString = @"http://example.com";NSDictionary *parameters = @{@"foo": @"bar", @"baz": @[@1, @2, @3]};
JSON 类型
[[AFJSONRequestSerializer serializer] requestWithMethod:@"POST" URLString:URLString parameters:parameters error:nil];
POST http://example.com/Content-Type: application/json
{"foo": "bar", "baz": [1,2,3]}
query 参数类型
[[AFHTTPRequestSerializer serializer] requestWithMethod:@"GET" URLString:URLString parameters:parameters error:nil];
GET http://example.com?foo=bar&baz[]=1&baz[]=2&baz[]=3
表单类型
[[AFHTTPRequestSerializer serializer] requestWithMethod:@"POST" URLString:URLString parameters:parameters error:nil];
POST http://example.com/Content-Type: application/x-www-form-urlencoded
foo=bar&baz[]=1&baz[]=2&baz[]=3
检测网络状态(Network Reachability Manager)
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {switch (status) {
case AFNetworkReachabilityStatusNotReachable: //没网络
break;
case AFNetworkReachabilityStatusReachableViaWiFi://WIFI代码
break;
case AFNetworkReachabilityStatusReachableViaWWAN: //蜂窝移动数据
break;
default:
break;
}
}];
[[AFNetworkReachabilityManager sharedManager] startMonitoring];
网络状态 status 有:
- AFNetworkReachabilityStatusNotReachable : 没网络
- AFNetworkReachabilityStatusReachableViaWWAN: 蜂窝移动数据
- AFNetworkReachabilityStatusReachableViaWiFi: WiFi
- AFNetworkReachabilityStatusUnknown: 未知网络
2. SDWebImage 的使用
SDWebImage 是一个 iOS 端的图片加载库。
git 地址: SDWebImage
基础使用
#import "SDWebImage/UIImageView+WebCache.h"
//通过SDWebImage加载图片
NSURL *url = [NSURL URLWithString:@"https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1571140150,993479906&fm=26&gp=0.jpg"];
[self.icon sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:@"rain"]];
Gift 图片
SDAnimatedImageView *imageView = [SDAnimatedImageView new];
SDAnimatedImage *animatedImage = [SDAnimatedImage imageNamed:@"image.gif"];
imageView.image = animatedImage;
3. 下拉刷新 MJRefresh
git 地址: MJRefresh
MJRefresh 实在是太强大了,不仅仅是下拉刷新,还有上拉加载,指定位置刷新等,建议看官方文档
参考
- iOS 开发高级-网络编程
以上是 iOS开发非完全指南之: 网络请求 的全部内容, 来源链接: utcz.com/a/31278.html