极客班 iOS 应用开发实战(五)

学以致用,不如说:用到再学。这样可以保证能用到。记得 Introduction to HTTP 以前就看多,但是没有认真的分析,只是达到了了解的地步。现在 iOS 开发,也就认真仔细,尤其是能用到的部分。

老师讲的比较快,真的每个关键词都查文档,才会完成明白每一步什么意思。顺便加了注释(没忍住再次吐槽了段老师的代码没注释😅)。

Option 键快被我按坏了,今天才发现三只轻拍也可以 Quick Help。

提炼常用类,可以拆分代码,方便复用,逻辑清晰。

可以按照 MVC 或者其他方式整理项目文件。

24. 网络编程

项目源码

主要内容

  • Web service 应用开发流程:网络数据的获取>解析>生成>上传
  • Http 网络通信: NSURLConnection/NSURLConnectionDataDelegate (Get/Post)
  • XML 数据解析: NSXMLParser/NSXMLParserDelegate
  • JSON 数据解析:NSJSONSerialization
  • 上传&下载:参考 Http 网络通信

安装配置 XAMPP

以微博 API:users/show 说明 Http 通信过程。

整个过程还算清晰主要是下载和登陆类添加代理,方便与对应的控制器通信。

服务器比较简陋,无法 POST 什么信息,都能从 http://localhost/login.xml 返回 user 信息。

解析 JSON

看 CS193P 更优雅的方案,就是 NSJSONSerialization:转化为字典或数组,顺序如下面代码所示,最后使用 valueForKeyPath: 访问数组即可。

1
2
3
4
5
6
7
8

NSURL *url = [NSURL URLWithString:@"http://ax.itunes.apple.com/WebObjects/MZStoreServices.woa/ws/RSS/topfreeapplications/limit=10/json"];

NSData *jsonRequests = [NSData dataWithContentsOfURL:url];

NSDictionary *appListRequests = [NSJSONSerialization JSONObjectWithData:jsonRequests options:0 error:NULL];

NSArray *appEntry = [appListRequests valueForKeyPath:@"feed.entry"];

POST 网络请求的过程

GWLoginRequest.m 文件代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//
// GWLoginRequest.m
// BLDemo05 L21
//
// Created by Will Ge on 8/21/15.
// Copyright (c) 2015 gewill.org. All rights reserved.
//

#import "GWLoginRequest.h"
#import "GWMutipartForm.h"
#import "GWLoginRequestsParsers.h"

@implementation GWLoginRequest

/**
* 发送登陆用户名密码请求的方法
*
* @param userName 用户名
* @param password 密码
* @param delegate 登陆请求的代理方法
*/
- (void)sendLoginRequestWithUserName:(NSString *)userName
password:(NSString *)password
delegate:(id<GWLoginRequestDelegate>)delegate {

[self.URLConnection cancel];

self.delegate = delegate;
NSString *URLString = @"http://localhost/login.xml";

// POST
// 转化为合法的 URL 格式
NSString *encodeURLString = [URLString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *URL = [NSURL URLWithString:encodeURLString];

// 转化为 NSMutableURLRequest ,方便调用存取方法更改属性
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
request.HTTPMethod = @"POST";
request.timeoutInterval = 60;
request.cachePolicy = NSURLRequestReloadIgnoringCacheData;

// 发送的数据转换为表单
GWMutipartForm *form = [[GWMutipartForm alloc] init];
[form addValue:userName forField:@"username"];
[form addValue:password forField:@"password"];

request.HTTPBody = [form httpBody];

// 更新 URLConnection
self.URLConnection = [[NSURLConnection alloc] initWithRequest:request
delegate:self startImmediately:YES];

}

/**
* 取消登陆请求的方法
*/
- (void)cancelRequest {

if (self.URLConnection) {

// 取消异步请求,并置空
[self.URLConnection cancel];
self.URLConnection = nil;
}

}


#pragma mark - NSURLConnectionDataDelegate methods
// 作为 NSURLConnection 补充,来处理网络请求的过程

// URL 响应请求状态
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {

// 转化为 NSHTTPURLResponse ,来访问 HTTP 状态码
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;

// 根据状态码分情况处理
if (httpResponse.statusCode == 200) { //连接成功

self.receviedData = [NSMutableData data]; // 设置为一个空的 NSData

} else {

// 连接失败,待处理
}
}

// 接受数据增量
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {

[self.receviedData appendData:data];
}

// 完成连接或加载
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {

// 整理收到的数据,并解析
NSString *string = [[NSString alloc ] initWithData:self.receviedData
encoding:NSUTF8StringEncoding];
NSLog(@"%@", string);

GWLoginRequestsParsers *parser = [[GWLoginRequestsParsers alloc] init];
GWUser *user = [parser parseXML:self.receviedData];

// 检查接收者是非实现代理方法,并响应代理方法:
if ([_delegate respondsToSelector:@selector(requestSuccess:user:)]) {

[_delegate requestSuccess:self user:user];
}
}

// 连接失败
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {

NSLog(@"%@", error);

// 检查接收者是非实现代理方法,并响应代理方法:
if ([self.delegate respondsToSelector:@selector(requestFailed:error:)]) {

[self.delegate requestFailed:self error:error];
}
}

@end

延伸阅读:

  • Introduction to HTTP by Tealeaf Academy

  • HTTP Methods: GET vs. POST

  • 浅谈HTTP中Get与Post的区别

    Http定义了与服务器交互的不同方法,最基本的方法有4种,分别是GET,POST,PUT,DELETE。URL全称是资源描述符,我们可以这样认为:一个URL地址,它用于描述一个网络上的资源,而HTTP中的GET,POST,PUT,DELETE就对应着对这个资源的查,改,增,删4个操作。到这里,大家应该有个大概的了解了,GET一般用于获取/查询资源信息,而POST一般用于更新资源信息。

  • HTTP status codes

  • JSON

    JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate.

  • Design Patterns: Delegation by Bart Jacobs

    @property (weak, nonatomic) id<AddItemViewControllerDelegate> delegate;
    We declare a class, AddItemViewController, which extends UIViewController. The class declares a property, delegate, of type id. Note that the property is marked as weak, which means that an AddItemViewController instance keeps a weak reference to its delegate.

  • nil / Nil / NULL / NSNull by Mattt Thompson

1
2
3
4
5
// 举个例子,这个表达...
if (name != nil && [name isEqualToString:@"Steve"]) { ... }

// …可以被简化为:
if ([name isEqualToString:@"steve"]) { ... }
Symbol Value Meaning
NULL (void *)0 literal null value for C pointers
nil (id)0 literal null value for Objective-C objects
Nil (Class)0 literal null value for Objective-C classes
NSNull [NSNull null] singleton object used to represent null