近期写完了暑假最后一个任务——天气预报,在里面用到了简单的网络数据请求以及json数据的解析,特此记录博客总结
JSON是一种轻量级的数据格式,一般用于数据交互。目前JSON的使用非常广泛,绝大多数网络请求都采用了JSON格式。
举个例子:
{
"code": "200",
"updateTime": "2021-11-15T16:35+08:00",
"fxLink": "http://hfx.link/2ax1",
"daily": [
{
"fxDate": "2021-11-15",
"sunrise": "06:58",
"sunset": "16:59",
"moonrise": "15:16",
"moonset": "03:40",
"moonPhase": "盈凸月",
"moonPhaseIcon": "803",
"tempMax": "12",
"tempMin": "-1",
"iconDay": "101",
"textDay": "多云",
"iconNight": "150",
"textNight": "晴",
},
{
"fxDate": "2021-11-16",
"sunrise": "07:00",
"sunset": "16:58",
"moonrise": "15:38",
"moonset": "04:40",
"moonPhase": "盈凸月",
"moonPhaseIcon": "803",
"tempMax": "13",
"tempMin": "0",
"iconDay": "100",
"textDay": "晴",
"iconNight": "101",
"textNight": "多云",
}
],
"refer": {
"sources": [
"QWeather",
"NMC",
"ECMWF"
],
"license": [
"QWeather Developers License"
]
}
}
这就是我们利用API请求到的json数据,接下来我们对其进行解释
JSON的格式很像Objective-C中的字典和数组,标准JSON格式key必须用双引号包裹起来。像上述数据中的code,updateTime都是字典中的key。我们用一张图来解释我们的json数据:
例如在上面的json数据中,daily就相当于我们的NSArray,数组中的每一个元素的类型都是NSDictionary(字典)。
搞清楚json数据中每个数据的类型后,我们就要去得到对应的数据,具体怎么得到将会在下文讲解完简单的网络数据请求后一并解释,在此我们只需要知道json数据在OC中对应的数据类型即可
在学习网络数据请求之前,我们有必要先了解两个概念:URL和API
API(应用程序编程接口)和URL(统一资源定位器)之间有密切的关系,但它们代表了不同的概念。
URL是用于定位资源在网络上的地址,它描述了资源的位置和访问方式。在Web中,URL通常用于标识和定位网页、图片、视频或其他文件。一个标准的URL通常由协议(例如HTTP或HTTPS)、主机名(例如www.example.com)、可选的端口号、文件路径和查询参数组成。例如:https://www.example.com/api/get_data。
API是一组规定的规则和协议,用于不同软件应用程序之间的通信和交互。API定义了应用程序如何请求和交换信息。在Web开发中,API通常以URL的形式提供,通过HTTP请求发送给服务器,并以JSON或XML等格式返回数据。API允许开发人员访问远程服务的功能或数据,而不需要了解底层的实现细节。
因此,URL可以被视为API的一部分。URL是实现访问API的重要组成部分,它是指向API的网络地址,用于标识和定位API的特定功能和资源。通过构建和发送HTTP请求到API的URL,可以实现与API的交互,请求数据或执行操作。
API一般是通过URL的形式进行访问,API相当于一种接口,而URL则是我们访问相应API所必须的参数
接下来笔者简单讲解一下iOS进行简单的网络数据请求的六步:
// 引入头文件
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 1. 创建API的URL
NSString *urlString = @"https://api.example.com/data"; // 请替换为实际的API地址
// 2. 创建NSURL对象
NSURL *url = [NSURL URLWithString:urlString];
// 3. 创建NSURLRequest
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 4. 创建NSURLSession
NSURLSession *session = [NSURLSession sharedSession];
// 5. 创建NSURLSessionDataTask
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
// 处理请求出错
NSLog(@"Error: %@", error.localizedDescription);
} else {
// 6. 处理请求结果(这里简单地将返回的数据转为字符串并输出)
NSString *responseData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"Response: %@", responseData);
// 如果需要解析JSON数据,可以使用NSJSONSerialization类进行解析
// NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
}
}];
// 7. 启动数据任务
[dataTask resume];
}
@end
需要注意的是这里只是最简单的GET请求,后续还会有POST请求等知识,笔者以后学到再进行补充
同时我们注意我们的第四步的方法也可以用
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id
这个方法来实现,其与[NSURLSession sharedSession]
方法的区别在于后者只能用于简单的网络请求,而前者可以允许您使用自定义配置来创建会话,并且可以设置代理来处理相关事件
假设我们的API需要进行HTTP基本认证(Basic Authentication),我们需要在请求中包含用户名和密码。在这种情况下,我们可以在 NSURLSessionDelegate 中实现URLSession:task:didReceiveChallenge:completionHandler:
方法来处理认证。
#import "ViewController.h"
@interface ViewController () <NSURLSessionDelegate>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 1. 创建API的URL
NSString *urlString = @"https://api.example.com/data"; // 请替换为实际的API地址
// 2. 创建NSURL对象
NSURL *url = [NSURL URLWithString:urlString];
// 3. 创建NSURLSessionConfiguration
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
// 4. 创建NSURLSession并设置代理
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:[NSOperationQueue mainQueue]];
// 5. 创建NSURLSessionDataTask
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
// 处理请求出错
NSLog(@"Error: %@", error.localizedDescription);
} else {
// 处理请求结果(这里简单地将返回的数据转为字符串并输出)
NSString *responseData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"Response: %@", responseData);
}
}];
// 6. 启动数据任务
[dataTask resume];
}
#pragma mark - NSURLSessionDelegate
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
// 这里我们处理HTTP基本认证(Basic Authentication)
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic]) {
// 在这里设置您的用户名和密码
NSString *username = @"your_username";
NSString *password = @"your_password";
// 创建NSURLCredential对象
NSURLCredential *credential = [NSURLCredential credentialWithUser:username password:password persistence:NSURLCredentialPersistenceForSession];
// 调用完成处理程序,将凭据传递给会话
completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
}
}
@end
同样的,在第五步中,我们同样可以使用两种方法来进行我们的数据请求
dataTaskWithRequest:completionHandler: 方法需要您自己创建和配置一个 NSURLRequest 对象,然后将其作为参数传递给该方法,以实现更多自定义和配置的选项。
dataTaskWithURL:completionHandler: 方法更为简洁,只需要传递一个 NSURL 对象,即请求的目标URL,而不需要自己创建和配置 NSURLRequest 对象。这个方法是发送GET请求的快捷方式,适用于简单的数据获取。
但是在实际代码中我们更推荐使用第一种方法进行请求,因为我们的POST与GET一般结合在一起使用,也许GET方法可以不用到Request,但是POST方法一定会用到,这里的讲解等后面学到了具体写博客进行讲解,这里给出POST的栗子:
- (void)sendPOSTRequest {
// 1. 创建API的URL
NSString *urlString = @"https://api.example.com/post_endpoint"; // 请替换为实际的POST请求的URL
// 2. 创建NSURL对象
NSURL *url = [NSURL URLWithString:urlString];
// 3. 创建NSMutableURLRequest对象,并设置HTTP方法为POST
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"POST"];
// 4. 如果有需要,设置HTTP请求头,例如Content-Type等
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
// 5. 如果有参数或需要发送数据,可以将其添加到HTTP请求体中
NSDictionary *parameters = @{@"key1": @"value1", @"key2": @"value2"};
NSError *error;
NSData *postData = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:&error];
if (!error) {
[request setHTTPBody:postData];
} else {
NSLog(@"Error creating POST data: %@", error.localizedDescription);
return;
}
// 6. 创建NSURLSession,并发送POST请求
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
// 处理请求出错
NSLog(@"Error: %@", error.localizedDescription);
} else {
// 处理请求结果
NSString *responseData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"Response: %@", responseData);
}
}];
// 7. 启动数据任务
[dataTask resume];
}
讲解完我们的基本的网络数据请求的步骤,我们以我们上面的以我们上面的json数据为例来进行一个简单的网络数据请求:
{
"code": "200",
"updateTime": "2021-11-15T16:35+08:00",
"fxLink": "http://hfx.link/2ax1",
"daily": [
{
"fxDate": "2021-11-15",
"sunrise": "06:58",
"sunset": "16:59",
"moonrise": "15:16",
"moonset": "03:40",
"moonPhase": "盈凸月",
"moonPhaseIcon": "803",
"tempMax": "12",
"tempMin": "-1",
"iconDay": "101",
"textDay": "多云",
"iconNight": "150",
"textNight": "晴",
},
{
"fxDate": "2021-11-16",
"sunrise": "07:00",
"sunset": "16:58",
"moonrise": "15:38",
"moonset": "04:40",
"moonPhase": "盈凸月",
"moonPhaseIcon": "803",
"tempMax": "13",
"tempMin": "0",
"iconDay": "100",
"textDay": "晴",
"iconNight": "101",
"textNight": "多云",
}
],
"refer": {
"sources": [
"QWeather",
"NMC",
"ECMWF"
],
"license": [
"QWeather Developers License"
]
}
}
例如我们想要的到json数据中的fxDate,首先我们需要使用
+ (nullable id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError **)error;
将我们的访问API得到的二进制数据 data 解析为 NSDictionary 对象(或 NSArray 对象)。根据上面的知识我们可以知道我们需要将其解析为NSDictionary。于是我们使用如下代码先来解析我们的整个json数据
NSDictionary *weatherData = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
然后我们可以知道我们字典中对应的“daily”键的类型实际上是一个NSArray对象,我们要想得到fxDate,首先需要访问daily,于是我们用如下代码得到daily NSArray *dailyArray = weatherData[@"daily"];
由于我们的daily数组中的每个对象类型都NSDictionary,于是我们可以使用如下代码得到我们数组中所有的fxDate
for (NSDictionary *currentDayData in dailyArray) {
NSString *timeString = currentDayData[@"fxDate"]; // 时间
NSLog(@"%@", timeString);
}
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
// 请求出错处理
} else {
NSDictionary *weatherData = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
if (error) {
// 解析数据出错处理
} else {
// 解析数据成功
NSArray *dailyArray = weatherData[@"daily"];
for (NSDictionary *currentDayData in dailyArray) {
NSString *timeString = currentDayData[@"fxDate"]; // 时间
NSLog(@"%@", timeString);
}
NSLog(@"无法获取当前时间的天气数据。");
}
}
}
}];
笔者在这里简单了讲解了一下基本的网络数据请求,实际上随着iOS的发展出现了许多第三方库例如JSONModel等,可以让我们更加方便的解析json以及请求网络数据,另外还有网络数据请求的POST用法笔者还不甚了解,后面学到会一并总结
json数据来源:
和风每日天气