More Related Content Similar to iOS App Design Class 13: Network Data Retrieval and Parsing/TITLE Similar to iOS App Design Class 13: Network Data Retrieval and Parsing/TITLE (20) iOS App Design Class 13: Network Data Retrieval and Parsing/TITLE2. 課程⼤大綱
• 擷取網路資料
• QV128:擷取網路上的 plist
• NSURLConnection
• QV129:載⼊入網路的⼤大圖檔,並能夠顯⽰示下載進度
• QV130:使⽤用 post 傳遞
• NSXMLParser
• QV135:RSS 讀取網路資料
• Samples:SeismicXML, LazyTableImages
• URL Scheme
• QV121:呼叫系統內建的 App
• QV122:⾃自訂 URL Scheme
• QV123 + QV124 :openURL 功能測試『qrapp://』
• Samples:LaunchMe
3. 擷取網路資料
• initWithContentsOfURL:
⼀一種⽅方法,屬於 NSString, NSArray,
NSDictionary, ... 的⽅方法,直接將資料轉成
特定的資料型態
• NSURLConnection class
⼀一個類別,進⾏行 http 資料傳送 (get, post)
4. 物件擷取網路資料的⽅方法
• NSString, NSMutableString
+ stringWithContentsOfURL: encoding: error:
- initWithContentsOfURL: encoding: error:
• NSArray, NSMutableArray
+ arrayWithContentsOfURL:
- initWithContentsOfURL:
• NSDictionary, NSMutableDictionary
+ dictionaryWithContentsOfURL:
- initWithContentsOfURL:
• NSData, NSMutableData
+ dataWithContentsOfURL:
+ dataWithContentsOfURL: options: error:
- initWithContentsOfURL:
- initWithContentsOfURL: options: error:
6. ViewController.h
@interface ViewController : UIViewController
{
NSDictionary *dataDict;
IBOutlet UILabel *display;
}
- (IBAction) btnStart:(id)sender;
- (IBAction) btnStart2:(id)sender;
- (void) loadFile;
- (void) didDownloadDictionary: (NSNotification *)info;
@end
7. ViewController.m (1/3)
⽅方法⼀一:直接讀取網路資料
(同步⽅方式)
- (IBAction) btnStart:(id)sender
{
display.text = @"......";
NSURL *url = [NSURL URLWithString:DictionaryURL];
dataDict = [[NSDictionary alloc] initWithContentsOfURL:url];
display.text = [dataDict description];
}
注意:等資料下載完畢後才會顯⽰示
8. ViewController.m (2/3)
⽅方法⼆二:建⽴立訊息通知
- (IBAction) btnStart2:(id)sender
{
display.text = @"......";
// 產⽣生通知的物件
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didDownloadDictionary:)
name:DidDownloadedNotification object:nil];
// 呼叫
[self performSelectorInBackground:@selector(loadFile) withObject:nil];
display.text = @"downloading......";
}
能在要求下載後繼續進⾏行程序
9. ViewController.m (3/3)
- (void) loadFile
{
NSURL *url = [NSURL URLWithString:DictionaryURL];
dataDict = [NSDictionary dictionaryWithContentsOfURL:url];
[[NSNotificationCenter defaultCenter]
postNotificationName:DidDownloadedNotification
object:self userInfo:dataDict];
}
- (void) didDownloadDictionary: (NSNotification *)info
{
// 接收結果
dataDict = [info userInfo];
display.text = [dataDict description];
}
下載完畢後通知執⾏行
12. 同步化 request
// 初始化請求
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
// 設置URL
[request setURL:[NSURL URLWithString:urlStr]];
// 設置HTTP⽅方法 (GET 或 POST)
[request setHTTPMethod:@"GET"];
// 發送同步請求, 這裡的returnData就是傳回的資料內容
NSData *returnData = [NSURLConnection sendSynchronousRequest:request
returningResponse:nil error:nil];
13. ⾮非同步化 request
NSMutableData*
buf
=
[[NSMutableData
alloc]
initWithLength:0];
NSURLConnection*
connection
=
[[NSURLConnection
alloc]
initWithRequest:req
delegate:self];
//
連線建⽴立成功收到回應時,
會觸發
-‐
(void)connection:(NSURLConnection
*)connection
didReceiveResponse:
(NSURLResponse
*)
aResponse;
//
每收到⼀一次網路封包資料,會觸發
-‐
(void)connection:(NSURLConnection
*)connection
didReceiveData:(NSData
*)data
{
[buf
appendData:data];
}
//
網路錯誤時觸發
-‐
(void)connection:(NSURLConnection
*)connection
didFailWithError:(NSError
*)error;
//
全部資料接收完畢時觸發
-‐
(void)connectionDidFinishLoading:(NSURLConnection
*)connection;
16. ViewController.m (1/4)
建⽴立連線讀取網路資料
- (void)viewDidLoad
{
downloadProgress.progress = 0.0;
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:
[NSURL URLWithString:@"http://web.trtc.com.tw/img/ALL/Route2200/051.jpg"]];
[request setHTTPMethod:@"GET"];
[request setValue:@"application/x-www-form-urlencoded"
forHTTPHeaderField:@"Content-Type"];
[request setValue:@"Mobile Safari 1.1.3 (iPhone; U; CPU like Mac OS X; en)"
forHTTPHeaderField:@"User-Agent"];
tempData = [NSMutableData alloc];
connect = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[super viewDidLoad];
}
17. ViewController.m (2/4)
錯誤處理
- (void)connection:(NSURLConnection *)connection didFailWithError:
(NSError *)error
{
//發⽣生錯誤
! NSLog(@"發⽣生錯誤");
}
連線建⽴立成功處理
- (void)connection: (NSURLConnection *)connection didReceiveResponse:
(NSURLResponse *)aResponse
{
//連線建⽴立成功,取得狀態
NSInteger status = (NSInteger)[(NSHTTPURLResponse *)aResponse
statusCode];
NSLog(@"%d", status);
expectedLength = [aResponse expectedContentLength]; //儲存檔案⻑⾧長度
}
18. ViewController.m (3/4)
封包接收
-(void) connection:(NSURLConnection *)connection didReceiveData:
(NSData *) incomingData
{
//收到封包,將收到的資料塞進緩衝中並修改進度條
! [tempData appendData:incomingData];
double ex = expectedLength;
downloadProgress.progress = [tempData length] / ex;
}
19. ViewController.m (4/4)
檔案下載完成處理
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//檔案下載完成
//取得可讀寫的路徑
NSArray *pathList =
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
! NSString *path = [pathList objectAtIndex:0];
//加上檔名
path = [path stringByAppendingPathComponent: @"00.jpg"];
NSLog(@"儲存路徑:%@", path);
!
! //寫⼊入檔案
[tempData writeToFile:path atomically:NO];
tempData = nil;
img.image = [UIImage imageWithContentsOfFile:path]; //顯⽰示圖⽚片在畫⾯面中
}
23. 建⽴立連線 (post) ⽅方法 ViewController.m (1/3)
- (void)viewDidLoad
{
downloadProgress.progress = 0.0;
NSString *post = @"submit=a &select2=030";
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding
allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:@"%d", [postData length]];
!
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL
URLWithString:@"http://web.trtc.com.tw/c/TicketALLresult.asp"]];
[request setHTTPMethod:@"POST"];
[request setValue:@"application/x-www-form-urlencoded"
forHTTPHeaderField:@"Content-Type"];
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
[request setValue:@"Mobile Safari 1.1.3 (iPhone; U; CPU like Mac OS X; en)"
forHTTPHeaderField:@"User-Agent"];
!
[request setHTTPBody:postData]; // 加上 post 的資料
!
tempData = [NSMutableData alloc];
connect = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[super viewDidLoad];
}
24. ViewController.m (2/3)
錯誤處理 連線建⽴立成功處理 封包接收
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// 發⽣生錯誤
! NSLog(@"發⽣生錯誤");
}
- (void)connection: (NSURLConnection *)connection didReceiveResponse:
(NSURLResponse *)aResponse
{
// 連線建⽴立成功
// 取得狀態
NSInteger status = (NSInteger)[(NSHTTPURLResponse *)aResponse statusCode];
NSLog(@"%d", status);
expectedLength = [aResponse expectedContentLength]; //儲存檔案⻑⾧長度
}
-(void) connection:(NSURLConnection *)connection didReceiveData: (NSData *)
incomingData
{
// 收到封包,將收到的資料塞進緩衝中並修改進度條
! [tempData appendData:incomingData];
double ex = expectedLength;
downloadProgress.progress = [tempData length] / ex;
}
25. ViewController.m (3/3)
檔案下載完成處理
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// 內容取得完成
NSString *loadData = [[NSMutableString alloc]
initWithData:tempData encoding:NSUTF8StringEncoding];
[webView loadHTMLString:loadData baseURL:nil];
NSLog(@"%@", loadData);
tempData = nil;
}
30. RSS 之內容範例
需要解析的格式
<item>
<title></title>
<link></link>
<description></description>
</item>
32. NSXMLParser 的代理⽅方法
parser:(NSXMLParser *)parser
檢測到tag的開始 didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict
parser:(NSXMLParser *)parser
檢測到內部的資料 foundCharacters:(NSString *)string
parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
檢測到tag的結尾 namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
33. RSSParser 類別
將 rss ⽂文件轉換為⼀一個陣列
主程式 RSSParser 類別
NSMutableArray didStartElement:
*result;
title
foundCharacters:
link
description
title
link didEndElement:
description
title
link
description
34. #import <Foundation/Foundation.h>
RSSParser.h
// ⽤用來定義標籤的⾃自定義變數
// tagTitle = <title>
// tagLink = <link>
// tagDescription = <description>
// tagunknown = 未知,不處理項⺫⽬目
typedef enum { tagTitle, tagLink, tagDescription, tagUnknown } RSSTag;
@interface RSSParser : NSObject <NSXMLParserDelegate>
{
NSMutableDictionary *item; // 存放每筆news資料
RSSTag currentTag; // ⺫⽬目前正在解析的標籤
BOOL startItem; // 是否開始解析
}
// 解析後所有的news
@property (nonatomic, retain) NSMutableArray *result;
- (id) initWithData:(NSData *) data;
解析內容後,將⽂文章條⺫⽬目
@end
(含title, description, link)
以陣列儲存
35. // 找到某個標籤的開始
- (void) parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:
(NSDictionary *)attributeDict
{
if([elementName isEqualToString:@"item"])
{
startItem = YES;
RSSParser.m (1/3)
item = [NSMutableDictionary new];
}
// 發現 item 後才開始解析
if(startItem)
{
if([elementName isEqualToString:@"title"])
{
currentTag = tagTitle;
}
else if([elementName isEqualToString:@"description"])
{
currentTag = tagDescription;
}
else if([elementName isEqualToString:@"link"]) (1) 當標籤開始時,分
{
currentTag = tagLink; 別對內容為第⼀一層的
}
else <item>,或是 <title>,
{
currentTag = tagUnknown; <description>, <link>
}
}
進⾏行處理
}
36. // XML 的內⽂文
- (void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
// 將空⽩白及換列符號去除
NSString *value = [string stringByTrimmingCharactersInSet:[NSCharacterSet
whitespaceAndNewlineCharacterSet]];
// 若沒有任何內容或標籤未知則不處理
if([value length]==0 || currentTag==tagUnknown) RSSParser.m (2/3)
{
return;
}
// ⽐比對正在解析的標籤,並儲存該標籤內的⽂文字於 item 內
switch (currentTag)
{
case tagTitle:
[item setValue:value forKey:@"title"];
break;
case tagDescription:
[item setValue:value forKey:@"description"];
break;
(2) 處理標籤的內容:
case tagLink:
[item setValue:value forKey:@"link"]; 若之前的標籤是 <title>,
break;
<description>, <link> 之
default:
break; ⼀一 ,則存⼊入陣列內
}
}
37. RSSParser.m (3/3)
// 找到某個標籤的結尾
- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if([elementName isEqualToString:@"item"])
{
NSDictionary *parsedItem = [[NSDictionary alloc] initWithDictionary:item];
[self.result addObject:parsedItem];
item = nil;
startItem = NO;
}
}
(3) 讀取到標籤的結尾時,
便可將單筆項⺫⽬目記錄於
NSDictionary 內
38. MasterViewController.h
主程式部分
#import <UIKit/UIKit.h>
#import "RSSParser.h"
@class DetailViewController;
@interface MasterViewController : UITableViewController
{
NSArray *result;
}
@property (strong, nonatomic) DetailViewController *detailViewController;
@end
39. MasterViewController.m (1/2)
- (void)viewDidLoad
{
[super viewDidLoad];
// 抓取資料
NSURL *url = [[NSURL alloc]
initWithString:@"http://udn.com/udnrss/udpopular.xml"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:nil error:nil];
RSSParser *parser = [[RSSParser alloc] initWithData:data];
result = [[NSArray alloc] initWithArray:parser.result];
parser = nil;
****** 省略部分程式 ******
} 以同步⽅方式取得
全部原始資料
40. MasterViewController.m (2/2)
#pragma mark - Table View
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:
(NSInteger)section
{
// return _objects.count;
return [result count]; 資料已存在物件內,直接取⽤用
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath
{
****** 省略部分程式 ******
//NSDate *object = [_objects objectAtIndex:indexPath.row];
//cell.textLabel.text = [object description];
NSDictionary *item = [result objectAtIndex:indexPath.row];
cell.textLabel.text = [item objectForKey:@"title"];
return cell;
}
41. 範例觀摩:SeismicXML
The SeismicXML sample application
demonstrates how to use NSXMLParser to
parse XML data. When you launch the
application it downloads and parses an
RSS feed from the United States
Geological Survey (USGS) that provides
data on recent earthquakes around the
world. It displays the location, date, and
magnitude of each earthquake, along with
a color-coded graphic that indicates the
severity of the earthquake. The XML
parsing occurs on a background thread
using NSOperation and updates the
earthquakes table view with batches of
parsed objects.
42. 範例觀摩:LazyTableImages
This sample demonstrates a multi-stage
approach to loading and displaying a
UITableView. It begins by loading the relevant
text from an RSS feed so the table can load
as quickly as possible, and then downloads
the images for each row asynchronously so
the UI is more responsive
43. iOS 上使⽤用 JSON 參考資料
• Getting Started with JSON in iOS5
http://blogs.captechconsulting.com/blog/nathan-jones/getting-started-json-ios5/
• JavaScript Object Notation Serialization
http://pragprog.com/magazines/2011-11/inside-ios-/
• 不要使⽤用 SBJSON (json-framework)
http://blog.devtang.com/blog/2012/05/05/do-not-use-sbjson/
45. URL Scheme
• 以 openURL: 的⽅方法開啟外部的應⽤用程式
• 認識內建 App 的 URL Scheme
• ⾃自建 URL Scheme
48. ViewController.h
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
- (IBAction) openSafari:(id)sender;
- (IBAction) openGoogleMap1:(id)sender;
- (IBAction) openGoogleMap2:(id)sender;
- (IBAction) openPhone:(id)sender;
- (IBAction) openSMS:(id)sender;
- (IBAction) openMail:(id)sender;
- (IBAction) openMailGood:(id)sender;
- (IBAction) openIBooks:(id)sender;
@end
49. ViewController.m (1/5)
- (IBAction) openSafari:(id)sender
{
NSString *stringURL = @"http://www.apple.com.tw/";
NSURL *url = [NSURL URLWithString:stringURL];
[[UIApplication sharedApplication] openURL:url];
}
50. ViewController.m (2/5)
- (IBAction) openGoogleMap1:(id)sender
{
NSString *stringURL = @"http://maps.google.com/maps?q=Taiwan";
NSURL *url = [NSURL URLWithString:stringURL];
[[UIApplication sharedApplication] openURL:url];
}
網址
- (IBAction) openGoogleMap2:(id)sender
{
NSString *title = @"台北101";
NSDecimalNumber *latitude = [NSDecimalNumber
decimalNumberWithString:@"25.033671"]; // 緯度
NSDecimalNumber *longitude = [NSDecimalNumber
decimalNumberWithString:@"121.564427"]; // 經度
NSInteger zoomValue = 12;
NSString *stringURL = [NSString stringWithFormat:@"http://
maps.google.com/maps?q=%@@%1.6f,%1.6f&z=%d", [title
stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding], [latitude
floatValue], [longitude floatValue], zoomValue];
NSURL *url = [NSURL URLWithString:stringURL];
[[UIApplication sharedApplication] openURL:url];
}
51. ViewController.m (3/5)
- (IBAction) openPhone:(id)sender
{
NSString *stringURL = @"tel:+8860212345678"; 電話
NSURL *url = [NSURL URLWithString:stringURL];
[[UIApplication sharedApplication] openURL:url];
}
- (IBAction) openSMS:(id)sender
{
NSString *stringURL = @"sms:+12345678901"; 簡訊
NSURL *url = [NSURL URLWithString:stringURL];
[[UIApplication sharedApplication] openURL:url];
}
52. ViewController.m (4/5)
- (IBAction) openMail:(id)sender
{
NSString *stringURL = @"mailto:happy@mail.com"; 郵件
NSURL *url = [NSURL URLWithString:stringURL];
[[UIApplication sharedApplication] openURL:url];
}
- (IBAction) openMailGood:(id)sender
{
// 預先填⼊入內⽂文
NSString *subject = @"標題";
NSString *msgBody = @"信件內⽂文";
NSString *emailAddress = @"hello@mail.com";
NSString *ccAddr = @"world@mail.com";
NSString *bccAddr = @"good@mail.com";
NSString *stringURL = [NSString stringWithFormat:@"mailto:%@?cc=
%@&bcc=%@&subject=%@&body=%@", emailAddress, ccAddr, bccAddr,
subject, msgBody];
NSURL *url = [NSURL URLWithString:stringURL];
[[UIApplication sharedApplication] openURL:url];
}
53. ViewController.m (5/5)
- (IBAction) openIBooks:(id)sender
{
NSString *stringURL = @"itms-books://";
NSURL *url = [NSURL URLWithString:stringURL];
[[UIApplication sharedApplication] openURL:url];
}
57. @interface AppDelegate : UIResponder <UIApplicationDelegate,
UIAlertViewDelegate>
{
UIAlertView *usageAlertView;
}
AppDelegate.h
@end
先做⼀一個彈出視窗
(解說⽤用) AppDelegate.h
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSString *message =
@"請按『Go』開啟Safari,並在Safari輸⼊入⼀一個開頭為 testurl:// 的 URL";
usageAlertView = [[UIAlertView alloc] initWithTitle:@"Usage"
message:message delegate:self cancelButtonTitle:@"Go"
otherButtonTitles:nil];
[usageAlertView show];
return YES;
}
58. AppDelegate.m (1/2)
再開出 Safari 應⽤用程式
(解說⽤用)
- (void) alertView:(UIAlertView *)alertView didDismissWithButtonIndex:
(NSInteger)buttonIndex
{
if(buttonIndex != -1)
{
[[UIApplication sharedApplication]
openURL:[NSURL URLWithString:@"http://www.apple.com"]];
}
}
59. 系統呼叫執⾏行後 AppDelegate.m (2/2)
處理傳⼊入的訊息
- (BOOL) application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
if (!url) return NO;
NSString *URLString = [url absoluteString];
if (!URLString) return NO;
NSString *message = [NSString stringWithFormat:@"收到訊息: %@n誰呼叫
我: %@", URLString, sourceApplication];
UIAlertView *openAlert = [[UIAlertView alloc] initWithTitle:@"啟動訊
息" message:message delegate:nil cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[openAlert show];
return YES;
}
62. ViewController.xib
指定呼叫的
ViewController.m URL Scheme
- (IBAction) openMyApp:(id)sender
{
NSString *stringURL = @"qrapp://HelloWorld";
NSURL *url = [NSURL URLWithString:stringURL];
[[UIApplication sharedApplication] openURL:url];
}
65. AppDelegate.m
- (BOOL) application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:
(id)annotation
{
if (!url) return NO;
NSString *URLString = [url absoluteString];
if (!URLString) return NO;
NSString *message = [NSString stringWithFormat:@"收到訊息: %@n誰呼叫
我: %@", URLString, sourceApplication];
UIAlertView *openAlert = [[UIAlertView alloc] initWithTitle:@"啟動訊
息" message:message delegate:nil cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[openAlert show];
return YES;
}