Swift 中使用 SwiftyJSON 製做一個比特幣價格 APP

Swift 中處理 JSON 數據有不少種方式,可使用原生的 NSJSONSerialization,也可使用不少第三方庫。原生的 NSJSONSerialization 方式這篇文章中介紹過。此次咱們介紹一個第三方庫 SwiftyJSON 而且用它來製做一個有趣的 APP.git

關於 SwiftyJSON

首先,咱們來了解一下什麼是 SwiftyJSON, 而且咱們爲何要用這個庫。好比咱們要解析這個比特幣實時價格的接口:github

http://api.coindesk.com/v1/bpi/currentprice/CNY.jsonjson

這個接口的數據格式以下: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 進行編輯。

  1. 首先,咱們在 storyboard 中拖入三個 UILabel

[構建 storyboard 界面]((http://www.swiftcafe.io/images/swifty-json/3.png)

其中第一個 Label 的 text 屬性設置爲 "當前價格", 後兩個 Label 的 text 設置爲空,用做顯示比特幣的價格。

  1. 而後,咱們將兩個用於顯示價格的 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&currency=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!))&currency=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

最後經過 NSDateFormatterstringFromDate 方法輸出格式化後的日期值:

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!))&currency=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 的構造方法來請求接口的數據。請求到數據後,咱們使用 SwiftyJSONJSON 類進行解析,隨後的 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 循環來遍歷價格列表,取出元組的兩項內容,分別以 dateprice 來命名,並用這些數據構建出 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 還有不少不完善的地方,若是你們有興趣,讓他變的更加完善。

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息