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 还有不少不完善的地方,若是你们有兴趣,让他变的更加完善。

相关文章
相关标签/搜索
本站公众号
   欢迎关注本站公众号,获取更多信息