现如今单机版本的 APP 几乎不复存在,我们需要掌握如何使用 iOS/web/Android 如何发送 http/https 请求给后端服务,根据响应数据渲染页面。本文主要介绍如何使用 iOS 建立网络请求。

# 一、网络请求基础使用

# 1. 网络请求主流程

要实现网络请求需要经历如下几个步骤:

  1. 构造请求的 URL
  2. 创建一个网络请求,网络请求不指定方法默认是 GET
  3. 创建网络管理
  4. 创建一个网络请求任务,并处理响应信息;如有更新 UI 需要回到主线程
  5. 开启网络请求任务任务

一个请求百度首页图片的例子:

- (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 并天气列表。主要步骤如下:

  1. 构建一个 url,带中文的 url 进行转换
  2. 创建一个网络请求(url;
  3. 创建网络管理
  4. 创建一个网络任务
    • 网络错误判断
    • 将 JSON 数据 NSData 转换成 NSDictionary(因为服务器返回的是一个{}格式的)
    • 获取数据创建 Model
    • 回到主线程更新数据和 UI
  5. 开启任务

#import "ViewController.h"
#import "Weather.h"
#import "WeatherCell.h"

@interface ViewController ()<UITableViewDataSource, UITableViewDelegate>

@property (nonatomic, strong) 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 的使用详解 (opens new window)

# 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 的文件下载

最简单的方式:


NSURL* url = [NSURL URLWithString:@"http://localhost:8080/AppTestAPI/wall.png"];
    
    // 得到session对象
NSURLSession* session = [NSURLSession sharedSession];
    
    // 创建任务
NSURLSessionDownloadTask* downloadTask = [session downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
   // 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);
}];
    // 开始任务
[downloadTask resume];

可以监听下载进度的方式

使用原生的方式实现文件下载主要有如下几个步骤:

  1. 确定需要下载的文件 url
  2. 创建去请求对象
  3. 使用sessionWithConfiguration创建 Session 对象,并设置请求代理NSURLSessionTaskDelegate和队列
    • 实现代理方法监听下载进度: downloadTask bytesWritten totalBytesWritten
    • 实现代理方法实现下载完成存储: downloadTask didFinishDownloadingToURL
  4. 使用downloadTaskWithRequest下载文件
  5. 启动 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 的文件上传功能

原生的文件上传功能比较复杂,主要分为如下几个步骤:

    1. 设置上传的 url
    1. 创建请求对象
    • 设置请求头 Content-Type
    • 设置请求方法为 POST , 一般上传走的是 post 请求
    1. 创建请求 Session 代理, NSURLSessionTaskDelegate并实现如下方法
    • 监听上传进度: task didSendBodyData totalBytesSent
    • 监听是否上传完成: task didCompleteWithError
    1. 创建上传 Task: uploadTaskWithRequest
    • 并封装上传数据 fromData 包括启动标记,文件参数和结束标记
    1. 启动 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]];
    // \r\n换行符
    [data appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    // Content-Disposition: form-data; name="myfile"; filename="wall.jpg"
    [data appendData:[@"Content-Disposition: form-data; name=\"myfile\"; filename=\"123.jpg\"" dataUsingEncoding:NSUTF8StringEncoding]];
    // \r\n换行符
    [data appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    // Content-Type 上传文件的MIME
    [data appendData:[@"Content-Type: image/jpeg" dataUsingEncoding:NSUTF8StringEncoding]];
    // 两个换行符
    [data appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    [data appendData:[@"\r\n" 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:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    [data appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];



    //                      添加结束标记

    // \r\n换行符
    [data appendData:[@"------WebKitFormBoundaryUFNaH6losNxu4xDq--" dataUsingEncoding:NSUTF8StringEncoding]];
    // boundary
    [data appendData:[@"\r\n" 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 (opens new window)

# 普通网络请求

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 (opens new window)

# 基础使用


#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 (opens new window)

MJRefresh 实在是太强大了,不仅仅是下拉刷新,还有上拉加载,指定位置刷新等,建议看官方文档

参考

【未经作者允许禁止转载】 Last Updated: 10/14/2021, 11:20:21 AM