iOS Programming Web Services and UIWebView
The work is divided into two parts. The first is connecting to and collecting data from a web service and using that data to create model objects. The second part is using the UIWebView class to display web content.
工作分为两部分,第一,连接和手机数据从一个web service 并使用这些数据创建model objects。第二部分是使用UIWebView 类展示web content .
Nerdfeed object diagram
1 Web Services
Your web browser uses the HTTP protocol to communicate with a web server.
你的web 浏览器通过HTTP协议与一个web server 交流。
In the simplest interaction, the browser sends a request to the server specifying a URL.
最简单的方式是浏览器发送一个请求到指定url 的服务器。
The server responds by sending back the requested page (typically HTML and images), which the browser formats and displays.
浏览器响应通过返回给请求的页面浏览器的格式和显示。
In more complex interactions, browser requests include other parameters, like form data. The server processes these parameters and returns a customized, or dynamic, web page.
You can write a client application for iOS that leverages the HTTP infrastructure to talk to a web- enabled server. The server side of this application is a web service. Your client application and the web service can exchange requests and responses via HTTP.
Because the HTTP protocol does not care what data it transports, these exchanges can contain complex data.
因为HTTP protocol不关心它传送的数据是什么,这些exchanges 可能包含复杂的数据。
This data is typically in JSON (JavaScript Object Notation) or XML format.
这些数据一般是JSON(JavaScript Object Notation)或XML format。
If you control the web server as well as the client, you can use any format you like; if not, you have to build your application to use whatever the server supports.
如果你能像client 一样能控制web server,你可以使用你喜欢的格式。如果不是,你必须构建你的应用基于server支持的。
2 Starting the Nerdfeed application
(1)Create a new Empty Application for the iPad Device Family. Name this application Nerdfeed
(2)Create a new NSObject subclass and
name it BNRCoursesViewController. In BNRCoursesViewController.h, change the superclass to UITableViewController.
@interface BNRCoursesViewController : UITableViewController
In BNRCoursesViewController.m, write stubs for the required data source methods so that you can
build and run as you go through this exercise.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
return nil;
}
In BNRAppDelegate.m, create an instance of BNRCoursesViewController and set it as the root view controller of a navigation controller. Make that navigation controller the root view controller of the window.
#import "BNRCoursesViewController.h"
BNRCoursesViewController *cvc =
[[BNRCoursesViewController alloc] initWithStyle:UITableViewStylePlain];
UINavigationController *masterNav =
[[UINavigationController alloc] initWithRootViewController:cvc];
self.window.rootViewController = masterNav;
3 NSURL, NSURLRequest, NSURLSession, and
NSURLSessionTask
(1)An NSURL instance contains the location of a web application in URL format.
NSURL实例用URL格式包含了一个web application的位置。
For many web services, the URL will be composed of the base address, the web application you are communicating with, and any arguments that are being passed.
对于很多web services,URL 由基础地址,你交流的web application和被传送的任何参数。
(2)An NSURLRequest instance holds all the data necessary to communicate with a web server.
NSURLRequest实例保持了所有与wb server 交流的任何必要的数据。
This includes an NSURL object as well as a caching policy, a limit on how long you will give the web server to respond, and additional data passed through the HTTP protocol.
这包括一个NSURL对象,一个caching策略,你给web server 响应的时间限制和通过HTTP协议传送的附加的数据。
(3)An NSURLSessionTask instance encapsulates the lifetime of a single request.
NSURLSessionTask实例概况了单个request的生命周期。
It tracks the state of the request and has methods to cancel, suspend, and resume the request.
它跟踪了request 的状态。并有方法cancel,suspend ,resume request。
Tasks will always be subclasses of NSURLSessionTask, specifically NSURLSessionDataTask, NSURLSessionUploadTask, or NSURLSessionDownloadTask.
tasks总是NSURLSessionTask的子类,有NSURLSessionDataTask,NSURLSessionUploadTask,NSURLSessionDownloadTask。
(4)An NSURLSession instance acts as a factory for data transfer tasks.
NSURLSession实例就像是一个data transfer tasks的工厂。
It is a configurable container that can set common properties on the tasks it creates.
它是一个可配置的容器,能设置它创建的task的 公共属性。
Some examples include header fields that all requests should have or whether requests work over cellular connections.
一些例子包括所有request的header fields 或者request 工作是否cellular 连接。
4 Formatting URLs and requests格式化URL和request
The form of a web service request varies depending on who implements the web service; there are no set-in-stone rules when it comes to web services.
web service request 的form 依赖于谁实现了web service 而变化。当遇到web services时没有特定的规则。
You will need to find the documentation for the web service to know how to format a request.
你需要为web service 的文档来知道怎样 format 一个request.
As long as a client application sends the server what it wants, you have a working exchange.
只要client application 给server 发送了他想要的,你就可以工作了。
http://bookapi.bignerdranch.com/courses.json
that the base URL is bookapi.bignerdranch.com
the web application is located at
courses on that server's filesystem, followed by the data format (json) you expect the response in.
base url 是bookapi.bignerdranch.com,web application 坐落在server的filesystem的courses,紧跟着是你期望响应的数据格式。
Web service requests come in all sorts of formats, depending on what the creator of that web service is trying to accomplish.
web service requests请求可以是各种格式,取决于web application 的创造者想完成什么。
The courses web service, where each piece of information the web server needs to honor a request is broken up into arguments supplied as path components, is somewhat common.
这种web server 需要的每个信息片段作为一个request 被分割成参数成为path 组成部分是 很普遍的。
This particular web service call says, "Return all of the courses in a json format."
这个特殊的web service call 说返回所有的courses 用json 格式。
Another common web service format looks like this:
另一个普遍的web service 格式是
http://baseURL.com/serviceName?argumentX=valueX&argumentY=valueY
for example, you might imagine that you could specify the month and year for which you wanted a list of the courses having a URL like this:
http://bookapi.bignerdranch.com/courses.json?year=2014&month=11
you will need to make a string "URL-safe." For example, space characters and quotes are not allowed in URLs; They must be replaced with escape-sequences.
你需要一个string URL-safe .例如空格和引用不在URL里。你必须用escape-sequences 取代。
NSString *search = @"Play some \"Abba\"";
NSString *escaped =
[search stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
// escaped is now "Play%20some%20%22Abba%22"
If you need to un-escape a percent-escaped string, NSString has the method:
如果你想un-escape 一个percent-escaped string ,你可以用:
- (NSString *)stringByRemovingPercentEncoding;
When the request to the Big Nerd Ranch courses feed is processed, the server will return JSON data that contains the list of courses.
当request 经过处理,server 将返回json data 。
5 Working with NSURLSession
NSURLSession refers both to a specific class as well as a name for a collection of classes that form an API for network requests.
NSURLSession 既可以一个特殊的类,也可以指那些形成了network request 的api的容器类。
the book will refer to the class as just NSURLSession, or an instance thereof, and the API as the NSURLSession API.
In BNRCoursesViewController.m add a property to the class extension to hold onto an instance of NSURLSession.
@interface BNRCoursesViewController ()
@property (nonatomic) NSURLSession *session;
@end
Then override initWithStyle: to create the NSURLSession object.
- (instancetype)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style]; if (self) {
self.navigationItem.title = @"BNR Courses";
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
_session = [NSURLSession sessionWithConfiguration:config
delegateQueue:nil];
delegate:nil
}
return self; }
In BNRCoursesViewController.m, implement the fetchFeed method to create an NSURLRequest that connects to bookapi.bignerdranch.com and asks for the list of courses.
Then, use the NSURLSession to create an NSURLSessionDataTask that transfers this request to the server.
- (void)fetchFeed
{
NSString *requestString =
@"http://bookapi.bignerdranch.com/courses.json";
NSURL *url = [NSURL URLWithString:requestString]; NSURLRequest *req = [NSURLRequest requestWithURL:url];
NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:req
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
NSString *json = [[NSString alloc] initWithData:data
NSLog(@"%@", json); }];
[dataTask resume]; }
NSURLSession's job is to create tasks of a similar nature.
NSURLSession的工作就是创建一个自然相近的任务。
For example, if your application had a set of requests that all required the same header fields, you could configure the NSURLSession with these additional header fields. Similarly, if a set of requests should not connect over cellular networks, then an NSURLSession could be configured to not allow cellular access. These shared behaviors and attributes are then configured on the tasks that the session creates.
例如,你的应用有一些requests,他们有相同的header fields,你可以配置在NSURLSession与这些额外的header fields。这些共同的表现和属性能够配置到session 创建的tasks上。
A project may have multiple instances of NSURLSession
一个project可能有多个NSURLSession 变量。
By giving the session a request and a completion block to call when the request finishes, it will return an instance of NSURLSessionTask.
通过给定session 一个请求和一个completion block 去调用当request 完成时,它将返回一个NSURLSessionTask.
Since Nerdfeed is requesting data from a web service, the type of task will be an NSURLSessionDataTask.
由于Nerdfeed 从一个web service 请求数据,task的类型是NSURLSessionDataTask。
Tasks are always created in the suspended state, so calling resume on the task will start the web service request.
任务总是创建为suspended 状态,所以调用resume 在这个任务上讲开始web service request。
Kick off the exchange whenever the BNRCoursesViewController is created.
在BNRCoursesViewController创建后开始exchange。
In BNRCoursesViewController.m, update initWithStyle:.
[self fetchFeed];
6 JSON data
7 Parsing JSON data
Apple has a built-in class for parsing JSON data, NSJSONSerialization.
apple 有一个内建的类为解析JSON data , NSJSONSerialization.
You can hand this class a bunch of JSON data and it will create instances of NSDictionary for every JSON object, NSArray for every JSON array, NSString for every JSON string, and NSNumber for every JSON number.
你能用这个类处理JSON data,它将创建NSDictionary 为每个JSON object,NSArray 为每个JSON array,NSString为每个JSON string,NSNumber 为每个JSON number。
In BNRCoursesViewController.m, modify the NSURLSessionDataTask completion handler to use the NSJSONSerialization class to convert the raw JSON data into the basic foundation objects.
NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0
NSLog(@"%@", jsonObject);
In BNRCoursesViewController.m, add a new property to the class extension to hang on to that array, which is an array of NSDictionary objects that describe each course.
@property (nonatomic, copy) NSArray *courses;
Then, in the same file, change the implementation of the NSURLSessionDataTask completion handler:
self.courses = jsonObject[@"courses"];
NSLog(@"%@", self.courses);
Now, still in BNRCoursesViewController.m, update the data source methods so that each of the course titles are shown in the table. You will also want to override viewDidLoad to register the table view cell class.
- (void)viewDidLoad
{
[super viewDidLoad];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"UITableViewCell"];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 0;
return [self.courses count]; }
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
return nil; UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:@"UITableViewCell" forIndexPath:indexPath];
NSDictionary *course = self.courses[indexPath.row]; cell.textLabel.text = course[@"title"];
return cell; }
7 The main thread
Modern iOS devices have multi-core processors that enable the devices to run multiple chunks of code concurrently.
现在的iOS设别都有多个核心处理器,这样能够让设备同时运行多行代码。
Fittingly, this is referred to as concurrency, and each chunk of code runs on a separate thread.
这被成为concurrency,每块代码运行在分开的线程里。
So far in this book, all of our code has been running on the main thread.
到目前为止,我们所有的代码运行在main thread .
The main thread is sometimes referred to as the UI (user interface) thread, as any code that modifies the UI has to run on the main thread.
主线程有时也被成为UI thread,因为任何修改UI的代码都运行在main thread 里。
When the web service completes, you need to reload the table view data.
当web service 完成后,你需要加载table view数据。
By default, NSURLSessionDataTask runs the completion handler on a background thread.
默认情况下,NSURLSessionDataTask完全competion handler 在background thread。
You need a way to force code to run on the main thread in order to reload the table view, and you can do that easily using the dispatch_async function.
你需要一种方式强迫代码运行在main thread 为了加载table view ,你可以轻松的完成这些,通过dispatch_async 函数。
In BNRCoursesViewController.m, update the completion handler to reload the table view data on the main thread.
dispatch_async(dispatch_get_main_queue(), ^{ [self.tableView reloadData];
});