Swift 中處理 JSON 數據有不少種方式,可使用原生的 NSJSONSerialization,也可使用不少第三方庫。原生的 NSJSONSerialization 方式這篇文章中介紹過。此次咱們介紹一個第三方庫 SwiftyJSON
而且用它來製做一個有趣的 APP.git
首先,咱們來了解一下什麼是 SwiftyJSON
, 而且咱們爲何要用這個庫。好比咱們要解析這個比特幣實時價格的接口:github
這個接口的數據格式以下:swift
{ "time": { "updated": "Jul 20, 2015 13:14:00 UTC", "updatedISO": "2015-07-20T13:14:00+00:00", "updateduk": "Jul 20, 2015 at 14:14 BST" }, "disclaimer": "This data was produced from the CoinDesk Bitcoin Price Index (USD & CNY respectively).", "bpi": { "USD": { "code": "USD", "rate": "278.3400", "description": "United States Dollar", "rate_float": 278.34 }, "CNY": { "code": "CNY", "rate": "1,717.4683", "description": "Chinese Yuan", "rate_float": 1717.4683 } } }
若是咱們使用原生的 NSJSONSerialization
方式,獲得比特幣的人民幣價格的話,咱們寫出的代碼大概就是這樣的:api
var url = "http://api.coindesk.com/v1/bpi/currentprice/CNY.json" if let jsonData = NSData(contentsOfURL: NSURL(string: url)!) { if let jsonObj: NSDictionary = NSJSONSerialization.JSONObjectWithData(jsonData, options: .MutableLeaves, error: nil) as? NSDictionary { if let bpi:NSDictionary = jsonObj["bpi"] as? NSDictionary { if let cny:NSDictionary = bpi["CNY"] as? NSDictionary { print(cny["rate"]!) } } } }
那麼咱們再來看一下,咱們用 SwiftyJSON 來達到一樣的目的要寫的代碼:數組
let url = "http://api.coindesk.com/v1/bpi/currentprice/CNY.json" if let jsonData = NSData(contentsOfURL: NSURL(string: url)!) { let json = JSON(data: jsonData) print(json["bpi"]["CNY"]["rate"]) }
是否是感受精簡了不少呢,對,就是這個效果。SwiftyJSON
的以大好處就是,不用你來處理 Swift 中的類型轉換,它會自動幫你處理類型等開發語言相關的問題,讓你專一於 JSON 數據的處理中。怎麼樣,挺好用的把。網絡
關於 SwifyJSON 的更多介紹,你們還能夠參看它的 Github 主頁:app
https://github.com/SwiftyJSON/SwiftyJSON異步
下面咱們就以一個例子來繼續瞭解 SwiftyJSON。async
咱們今天要作的是一個比特幣實時價格的 APP,這裏咱們會用到 SwiftyJSON 來解析服務端的數據。
首先咱們建立一個項目, Single View Application
類型:
而後設置好項目的基本信息:
而後就是要引入 SwiftyJSON
庫,
另外還能夠下載咱們預配置好的項目來進行開發:bitprice-start.zip
如今咱們就進入主題吧,首先咱們開始構建 UI 界面,打開 Main.storyboard
進行編輯。
storyboard
中拖入三個 UILabel
[構建 storyboard 界面]((http://www.swiftcafe.io/images/swifty-json/3.png)
其中第一個 Label 的 text
屬性設置爲 "當前價格", 後兩個 Label 的 text
設置爲空,用做顯示比特幣的價格。
UILabel
連接到主控制器的 Outlet
中,在打開 storyboard 視圖的同時,按住 Option
並點擊 ViewController.swift
。這樣編輯界面上同時顯示了 storyboard
和控制器的代碼,而後咱們在 storyboard
中選中 Label,而後按住 control
拖動到控制器的代碼中:[創建連接]((http://www.swiftcafe.io/images/swifty-json/4.jpg)
隨後會彈出一個變量名稱提示框,咱們將第一個 UILabel 命名爲 priceLabel
,將第二個 UILabel 命名爲 differLabel
。
[變量命名]((http://www.swiftcafe.io/images/swifty-json/5.jpg)
最後,咱們在給 ViewController
創建一個新的屬性 lastPrice
, 存儲上次更新的價格,用於計算當前價格相對於上次的漲跌幅。
這樣咱們的 ViewController
的屬性定義以下:
class ViewController: UIViewController { @IBOutlet var priceLabel: UILabel! @IBOutlet var differLabel: UILabel! var lastPrice:Double = 0.0 }
兩個 IBOutlet
連接的 UILabel
, 還有一個 Double
變量用於存放上次的價格。
基礎結構設置好後,咱們就能夠開始構建應用的邏輯了,咱們首先定義一個方法 getLatestPrice()
,用於獲取比特幣最新的價格:
func getLatestPrice() -> String?{ let url = "http://api.coindesk.com/v1/bpi/currentprice/CNY.json" if let jsonData = NSData(contentsOfURL: NSURL(string: url)!) { let json = JSON(data: jsonData) return json["bpi"]["CNY"]["rate"].stringValue }else { return nil } }
這裏面咱們首先經過 NSData
的構造方法從指定的 URL 地址讀取了比特幣價格數據,而後用到了 SwiftyJSON
來讀取和解析返回的 JSON
數據
let json = JSON(data: jsonData) return json["bpi"]["CNY"]["rate"].stringValue
只有兩行代碼,就完成了數據的提取,很方便吧。
數據讀取方法寫好了,那麼咱們須要另一個方法來調度這個,由於咱們這個 getLatestPrice
的網絡操做時同步的,因此咱們的調度方法須要把它放到另外的線程中,咱們使用 GCD
進行這個處理:
func reloadPrice() { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { () -> Void in let price = self.getLatestPrice() dispatch_async(dispatch_get_main_queue(), { () -> Void in NSTimer.scheduledTimerWithTimeInterval(3, target: self, selector: Selector("reloadPrice"), userInfo: nil, repeats: false) if let p = price { var nsPrice = p as NSString nsPrice = nsPrice.stringByReplacingOccurrencesOfString(",", withString: "") let doublePrice = nsPrice.doubleValue let differPrice = doublePrice - self.lastPrice self.lastPrice = doublePrice; self.priceLabel.text = NSString(format: "¥ %.2f", doublePrice) as? String if differPrice > 0 { self.differLabel.textColor = UIColor.redColor() self.priceLabel.textColor = UIColor.redColor() self.differLabel.text = NSString(format: "+%.2f", differPrice) as? String }else{ self.differLabel.text = NSString(format: "%.2f", differPrice) as? String self.differLabel.textColor = UIColor.greenColor() self.priceLabel.textColor = UIColor.greenColor() } } }) }); }
咱們這裏首先使用 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),...)
來調度異步線程,在這個線程中,咱們調用了 getLatestPrice()
方法來獲取當前的比特幣價格,讀取成功後,咱們要用這個數據來更新 UI 顯示了。而 UI 的操做時不能在異步線程中進行的。因此咱們隨後又調用了 dispatch_async(dispatch_get_main_queue(),...)
方法將處理調度到主線程中。
因爲服務端返回的數據格式是字符串類型的諸如這樣的價格數據
1,273.203
因此咱們還須要對這個數據進行一下轉換:
var nsPrice = p as NSString nsPrice = nsPrice.stringByReplacingOccurrencesOfString(",", withString: "") let doublePrice = nsPrice.doubleValue
首先咱們將字符串中的 ,
字符清除掉,而後使用 NSString 的 doubleValue
將字符串轉換成 Double 類型。
接下來,咱們用當前的價格減去上次讀取的價格,計算出差價,就能夠顯示出相對於上次讀取數據的漲跌幅度了。計算完成後,咱們就從新將當前的價格存入 self.lastPrice
中,以便於下次的計算。
let differPrice = doublePrice - self.lastPrice self.lastPrice = doublePrice;
最後,咱們計算出了這些數據,再將他們顯示的 UILabel 上面。
self.priceLabel.text = NSString(format: "¥ %.2f", doublePrice) as? String if differPrice > 0 { self.differLabel.textColor = UIColor.redColor() self.priceLabel.textColor = UIColor.redColor() self.differLabel.text = NSString(format: "+%.2f", differPrice) as? String }else{ self.differLabel.text = NSString(format: "%.2f", differPrice) as? String self.differLabel.textColor = UIColor.greenColor() self.priceLabel.textColor = UIColor.greenColor() }
咱們首先將當前價格設置到 self.priceLabel
, 而後根據漲跌幅度是正數仍是負數設置 self.differLabel
的文字,若是是正數要在前面放一個 +
號。同時咱們根據漲跌幅設置文本的顏色,若是是漲就設置爲紅色,若是是跌就設置爲綠色。
最後還有一行代碼咱們要注意:
NSTimer.scheduledTimerWithTimeInterval(3, target: self, selector: Selector("reloadPrice"), userInfo: nil, repeats: false)
咱們用 NSTimer
又調度了一下這個方法,在 3 秒鐘以後,從新請求最新價格。這樣咱們的價格就能每隔 3 秒刷新一次。
數據讀取方法弄好以後,咱們就能夠在 viewDidLoad()
裏面調用它了
override func viewDidLoad() { super.viewDidLoad() reloadPrice() }
接下來能夠運行一下項目,咱們就會看到報價比特幣的最新價格顯示在界面上了。而後還能夠不停的刷新。
最新報價的現實邏輯咱們實現完了,咱們還能夠作更多的事情,仔細研究 coindesk
的數據,咱們發現還有一個接口能夠實現查詢比特幣的報價歷史:
http://api.coindesk.com/v1/bpi/historical/close.json?start=2015-07-15&end=2015-07-24¤cy=CNY
訪問這個接口咱們就能夠看到諸如這樣的數據返回:
{ "bpi": { "2015-07-15": 1756.5732, "2015-07-16": 1719.6188, "2015-07-17": 1723.7974, "2015-07-18": 1698.9991, "2015-07-19": 1686.3934, "2015-07-20": 1723.3102, "2015-07-21": 1702.5693, "2015-07-22": 1710.3503 }, "disclaimer": "This data was produced from the CoinDesk Bitcoin Price Index. BPI value data returned as CNY.", "time": { "updated": "Jul 23, 2015 09:53:17 UTC", "updatedISO": "2015-07-23T09:53:17+00:00" } }
咱們看到,這個接口返回了從起始日期到結束日期的比特幣價格信息,咱們可使用這個數據來顯示歷史數據,好比從當天往前 5 天以內的歷史數據。
那麼咱們先寫一個網絡讀取和解析數據的方法:
func getLastFiveDayPrice() -> Array<(String,String)> { var curDate = NSDate() var calendar = NSCalendar.currentCalendar() let startDate = calendar.dateByAddingUnit(NSCalendarUnit.CalendarUnitDay, value: -6, toDate: curDate, options: nil) let endDate = calendar.dateByAddingUnit(NSCalendarUnit.CalendarUnitDay, value: -1, toDate: curDate, options: nil) let formatter = NSDateFormatter() formatter.dateFormat = "yyyy-MM-dd" let url = "http://api.coindesk.com/v1/bpi/historical/close.json?start=\(formatter.stringFromDate(startDate!))&end=\(formatter.stringFromDate(endDate!))¤cy=CNY" var result = Array<(String,String)>() if let jsonData = NSData(contentsOfURL: NSURL(string: url)!) { let json = JSON(data: jsonData) let bpiDict:JSON = json["bpi"] for (key,val) in bpiDict { result.append((key,val.stringValue)) } } return result }
這個方法會返回一個數組,咱們仔細看一下這個數組的定義 Array<(String,String)>
,數組中的類型是 (String,String)
, 這種類型定義叫作 元組(Tuple) 是 Swift中的一個語言特性,關於元組,簡而言之就是一個包含了多個元素的類型,好比咱們這裏的元組包含了兩個 String
類型的值。
下面展現了元組類型的簡單用法:
let tuple = ("2012-2-21","1,232.23") //能夠經過索引來引用元組的元素 print("\(tuple.0) price is \(tuple.1)") //還能夠爲元組的項制定名稱 let (date,price) = tuple print("\(date) price is \(price)")
咱們看到,咱們能夠經過索引的方式,也能夠經過爲元組項指定名稱的方式來引用元組中的值。這裏簡單介紹一下元組的概念,更詳細的內容你們能夠參考相關資料。
接下來,咱們看一下這個方法的內容,首先咱們經過格式化 NSDate
輸出的方式拼接出 URL,這裏咱們用到了 NSCalendar
,這個類能夠經過 dateByAddingUnit
方法操做 NSDate
的各個日期屬性,好比將當前的日期減去多少天,咱們用這個方法獲得當前日期往前 5 天和 1 天的日期值,用於獲得這個期間的比特幣價格。
咱們還用到了 NSDateFormatter
,這個類能夠將 NSDate
的值進行格式化輸出,獲得咱們須要的日期輸出格式。咱們這裏須要相似 2012-03-12
的這種日期格式,因此咱們將日期格式定義爲 yyyy-MM-dd
。
最後經過 NSDateFormatter
的 stringFromDate
方法輸出格式化後的日期值:
var curDate = NSDate() var calendar = NSCalendar.currentCalendar() let startDate = calendar.dateByAddingUnit(NSCalendarUnit.CalendarUnitDay, value: -6, toDate: curDate, options: nil) let endDate = calendar.dateByAddingUnit(NSCalendarUnit.CalendarUnitDay, value: -1, toDate: curDate, options: nil) let formatter = NSDateFormatter() formatter.dateFormat = "yyyy-MM-dd" let url = "http://api.coindesk.com/v1/bpi/historical/close.json?start=\(formatter.stringFromDate(startDate!))&end=\(formatter.stringFromDate(endDate!))¤cy=CNY"
拼接好 URL 以後,咱們就能夠開始請求數據了,看一看下面的代碼:
var result = Array<(String,String)>() if let jsonData = NSData(contentsOfURL: NSURL(string: url)!) { let json = JSON(data: jsonData) let bpiDict:JSON = json["bpi"] for (key,val) in bpiDict { result.append((key,val.stringValue)) } }
首先咱們定義了一個 result
數組,用於返回咱們的價格列表。而後咱們使用 NSData
的構造方法來請求接口的數據。請求到數據後,咱們使用 SwiftyJSON
的 JSON
類進行解析,隨後的 for
循環中,咱們遍歷了 bpi
節點中的全部的鍵值,將這些鍵值經過元組的方式添加到 result
列表中。
result.append((key,val.stringValue))
注意條語句,咱們構造元組的方式 (key,val.stringValue)
, 由於咱們的元組定義爲 (String,String)
類型,在 for
循環中,咱們的 key
變量是 String
類型的,因此咱們能夠直接用這個值來構建元組的第一項,而 val
不是 String
類型的。咱們必須使用 SwiftyJSON
中的 stringValue
方法取得這個節點的 String
類型的值來構建元組的第二項。
到此爲止咱們的歷史數據讀取方法也完成了。
數據讀取方法構造完成後,咱們就能夠開始處理 UI 界面了,咱們建立了 buildHistoryLabels
方法:
func buildHistoryLabels(priceList: Array<(String,String)>) { var count = 0.0 var labelTitle = UILabel(frame: CGRectMake(CGFloat(30.0), CGFloat(220.0), CGFloat(200.0), CGFloat(30.0))) labelTitle.text = "歷史價格" self.view.addSubview(labelTitle) for (date, price) in priceList { var labelHistory = UILabel(frame: CGRectMake(CGFloat(30.0), CGFloat(250 + count * 40.0), CGFloat(200.0), CGFloat(30.0))) labelHistory.text = "\(date) \(price)" self.view.addSubview(labelHistory) count++ } }
這個方法接受一個數組做爲參數,這個數組的內容就是咱們的價格列表。首先咱們這裏構建了這組 UILabel 的標題:
var labelTitle = UILabel(frame: CGRectMake(CGFloat(30.0), CGFloat(220.0), CGFloat(200.0), CGFloat(30.0))) labelTitle.text = "歷史價格" self.view.addSubview(labelTitle)
而後咱們經過一個 for
循環來遍歷價格列表,取出元組的兩項內容,分別以 date
和 price
來命名,並用這些數據構建出 UILabel
並添加到 UI 視圖中:
for (date, price) in priceList { var labelHistory = UILabel(frame: CGRectMake(CGFloat(30.0), CGFloat(250 + count * 40.0), CGFloat(200.0), CGFloat(30.0))) labelHistory.text = "\(date) \(price)" self.view.addSubview(labelHistory) count++ }
如今咱們能夠運行 APP 了,咱們看到當前的價格,以及近期的價格都展現在了界面中:
[價格列表]((http://www.swiftcafe.io/images/swifty-json/7.png)
到此爲止,咱們利用 SwiftyJSON
完成的讀取了 JSON 數據。咱們的比特幣查詢 APP 也基本完成了。固然這個示例 APP 還有不少不完善的地方,若是你們有興趣,讓他變的更加完善。