用Swift实现一款天气预报APP(二)
这个系列的目录:
上篇中主要讲了界面的一些内容,这篇主要讨论网络请求,获得天气的数据。具体的说是HTTP请求天气站点的API,得到返回的JSON数据。解析这些数据,并更新到界面内容中。 让用户知道当前的和之后几个小时的天气状况。
发起HTTP请求主要用到的是SDK的NSURLSession这个类,使用这个类对象可以创建请求任务并在这个任务中处理请求之后由服务器返回的JSON数据。在NSURLSession之前主要用到的是NSURLConnection。这两个类比较类似。只是在NSURLSession中增加了后台执行的请求。发起网络请求的时候,使用NSURLSession创建对应的NSURLSessionTask,并由这个Task请求服务器和处理返回的数据。
下面大体的看看我们怎么做HTTP请求的。本文将主要叙述如何发起HTTP请求。先讲讲使用最基本的iOS的SDK发请求,然后叙述如何用现在比较流向的AFNetworking框架请求。或许你也听说过一个叫做ASIHttpRequest的框架,但是这个已经很久没有人维护了。所以,这里就不再提及。
使用iOS SDK发起HTTP网络请求:
1. 准备访问服务器的NSURL对象。这个对象需要一个url字符串,比如百度的地址字符串就是“http://www.baidu.com”,我们这里需要一个指向天气服务器的字符串。
var weatherUrl = "http://api.openweathermap.org/data/2.5/forecast?lat=\(latitude)&lon=\(longitude)"
var url = NSURL(string: weatherUrl)
第一句的问好后面的部分?lat=\(latitude)&lon=\(longitude)是为url指定用户当前的经纬度。之后根据这个url字符串生成NSURL对象。
2. 创建NSURLSession对象。NSURLSession有一个类方法创建实例。
self.urlSession = NSURLSession.sharedSession()
一般用到shareXXX的方式命名的方法是一个单例方法。也就是这个方法在被调用的时候会判断需要的实例是否已经创建,如果是的话返回创建好的实例,如果没有创建则初始化一个并保存起来以备下次使用。关于使用Swift实现单例模式,请参考这里。
3. 创建NSURLSessionDataTask,并设置好如何处理请求返回的数据。然后开始HTTP请求。
var task = self.urlSession.dataTaskWithURL(url!, completionHandler: {(data:NSData!, response: NSURLResponse!, error: NSError!) in
if error != nil {
println("http request error \(error)")
return
}
println("\(response)")
var httpResponse = response as NSHTTPURLResponse
var statusCode: NSInteger = httpResponse.statusCode
println("status code: \(statusCode)") var error: NSError?
var jsonDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments, error: &error) as NSDictionary
if error != nil {
println("json error")
return
} println("json \(jsonDictionary)")
self.jsonLabel.text = jsonDictionary.description
})
task.resume()
self.urlSession.dataTaskWithURL这个方法创建了一个DataTask。completionHandler后面的就是指定的处理返回数据的方法。这里使用了Swit的闭包。闭包的语法可以简单的概括为{(参数列表。。。)->闭包的返回类型 in 功能代码在这里}具体的参考上面的代码示例。那么具体的,我们应该如何处理返回的数据呢。第一步,先查看返回的错误error是否为空。如果为空就是没有错,否则,就是有错了。这个时候就可以提示用户后直接return,不再处理后面的代码了。
下面就是检查response的statusCode。状态码最直观的就是大家都见过的404,虾米都木有找到的时候的提示。如果是200,那么就是请求服务器成功。否则,也可以提示用户后返回了。
最后就是解析用户数据了。首先需要把服务器返回的JSON格式的数据转换为Swift可以直接访问的NSDictionary。记住,这里是NSDictionary不是Swift基础数据类型中的泛型Dictionary<KeyType, ValueType>。服务器的JSON数据转换成NSDictionary后就可以取出需要的数据并更新到主界面上了。
这里你会发现很多的代码调用都是通过NSError的实例是否为空判断某函数的执行是否有错误发生的。Swift没有try-catch的异常处理模式。只有这样的error的方式。这个大家需要习惯。用这种方式处理错误是为了去掉代码的二意性。有其他语言编程经理的都知道,有时候就用try-catch来做代码的某些判断了。这是不对的。
最后调用task的resume方法开始HTTP请求。
前文已经简单的提到过定位的功能。本文在这里之前都在讨论HTTP请求的功能。如前面提到的,请求天气数据到时候需要用到经纬度的数据作为url参数。所以HTTP请求只能在定位成功获取到用户当前的经纬度之后进行。所以,在代码实现的时候,网络请求在Location Manager的定位成功的代理方法中发起。
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!){
println("get location")
var location:CLLocation = locations[locations.count-] as CLLocation if (location.horizontalAccuracy > ) {
self.locationManager.stopUpdatingLocation()
println(location.coordinate) self.textLabel.text = "latitude \(location.coordinate.latitude) longitude \(location.coordinate.longitude)" // 在这里发起HTTP请求
self.updateWeatherWith(location.coordinate.latitude, longitude: location.coordinate.longitude)
}
}
到此为止,从获取用户位置到使用用户的经纬度数据请求天气服务器获取天气的JSON数据的功能都已经衔接在一起了。
那么,我们来讨论一下如何使用AFNetworking这个框架(framework)。在这之前,用户需要配置cocoaPods。具体的步骤可以参考这里。这里必须吐槽一下,Ruby什么的编程之类的网站多要墙真是不可理喻啊。配置好之后,亲,你一定要点击的时候workspace那个后缀的文件,不是项目文件了。否则会出错。
要使用AFNetworking框架就涉及到一个Objective-C和Swift交互的问题了。
let manager = AFHTTPRequestOperationManager()
这行代码直接编译不通过。。。稍微深究机会发现,在Swift中没有办法直接使用OC(Objective-C)的代码。翻翻项目,找到SwiftWeather-Bridging-Header.h头文件,然后在里面添加对于AFNetworking框架的引用。
#import <AFNetworking/AFNetworking.h>
添加后,编译你的项目。上面那行出错的代码就可以用了。
使用AFNetworking框架确实会很方便。不用像使用NSURLSession里那样写那么多的代码。这个通过一个简单的感官比较就会得出结论。先在上AFNetworking的HTTP请求代码。
let manager = AFHTTPRequestOperationManager() let url = "http://api.openweathermap.org/data/2.5/forecast"
println(url) let params = ["lat":latitude, "lon":longitude, "cnt":]
println(params) manager.GET(url,
parameters: params,
success: { (operation: AFHTTPRequestOperation!,
responseObject: AnyObject!) in
//println("JSON: " + responseObject.description!) self.updateUISuccess(responseObject as NSDictionary!)
},
failure: { (operation: AFHTTPRequestOperation!,
error: NSError!) in
println("Error: " + error.localizedDescription) self.loading.text = "Internet appears down!"
})
初始化一个AFHTTPRequestOperationManager来处理请求和数据返回等的处理,一个类就够了。不用task什么的了。指定要访问的url字符串,这里是字符串也不需要NSURL的实例了。然后把需要给url字符串添加的参数放在一个Dictionary<String, String>泛型字典中。然后用manager发出HTTP请求,并指定了请求的方式为GET,函数的名字就是HTTP请求的方式。HTTP请求还有除GET之外的很多中,其中最常用的是POST。然后可以看到GET方法中的sucess和failure,都分别是在指定请求成功的处理代码和失败的处理代码。
请求数据不是总能成功。这在代码中也有体现。但是不成功的数据请求并不只是请求不到数据,比如在网络不通的时候。还包括请求到了数据,但是数据表明这个请求是错误的。所以,在网络连接失败而造成的网络请求失败时提醒用户“Internet apears down”。在数据解析后发现服务器返回数据提示说数据错误,这个时候也要提醒用户错误。这里只是点到,不做其他处理。读者在实际的开发中需要注意这一点。
数据请求完成后,调用方法updateUISuccess把数据显示在界面元素中。从上到下,依次是用户所在地(文字),天气(图片),温度(文字)。然后在下面,从左到右,依次显示这一天中其他几个小时 的天气预报。
func updateUISuccess(jsonResult: NSDictionary) {
self.loading.text = nil
self.loadingIndicator.hidden = true
self.loadingIndicator.stopAnimating() if let tempResult = ((jsonResult["list"]? as NSArray)[]["main"] as NSDictionary)["temp"] as? Double { // If we can get the temperature from JSON correctly, we assume the rest of JSON is correct.
var temperature: Double
var cntry: String
cntry = ""
if let city = (jsonResult["city"]? as? NSDictionary) {
if let country = (city["country"] as? String) {
cntry = country
if (country == "US") {
// Convert temperature to Fahrenheit if user is within the US
temperature = round(((tempResult - 273.15) * 1.8) + )
}
else {
// Otherwise, convert temperature to Celsius
temperature = round(tempResult - 273.15)
} // FIXED: Is it a bug of Xcode 6? can not set the font size in IB.
//self.temperature.font = UIFont.boldSystemFontOfSize(60)
self.temperature.text = "\(temperature)°"
} if let name = (city["name"] as? String) {
self.location.font = UIFont.boldSystemFontOfSize()
self.location.text = name
}
} if let weatherArray = (jsonResult["list"]? as? NSArray) {
for index in ... {
if let perTime = (weatherArray[index] as? NSDictionary) {
if let main = (perTime["main"]? as? NSDictionary) {
var temp = (main["temp"] as Double)
if (cntry == "US") {
// Convert temperature to Fahrenheit if user is within the US
temperature = round(((temp - 273.15) * 1.8) + )
}
else {
// Otherwise, convert temperature to Celsius
temperature = round(temp - 273.15)
} //FIXED: Is it a bug of Xcode 6? can not set the font size in IB.
//self.temperature.font = UIFont.boldSystemFontOfSize(60)
if (index == ) {
self.temp1.text = "\(temperature)°"
}
if (index == ) {
self.temp2.text = "\(temperature)°"
}
if (index == ) {
self.temp3.text = "\(temperature)°"
}
if (index == ) {
self.temp4.text = "\(temperature)°"
}
}
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "HH:mm"
if let date = (perTime["dt"]? as? Double) {
let thisDate = NSDate(timeIntervalSince1970: date)
let forecastTime = dateFormatter.stringFromDate(thisDate)
if (index==) {
self.time1.text = forecastTime
}
if (index==) {
self.time2.text = forecastTime
}
if (index==) {
self.time3.text = forecastTime
}
if (index==) {
self.time4.text = forecastTime
}
}
if let weather = (perTime["weather"]? as? NSArray) {
var condition = (weather[] as NSDictionary)["id"] as Int
var icon = (weather[] as NSDictionary)["icon"] as String
var nightTime = false
if icon.rangeOfString("n") != nil{
nightTime = true
}
self.updateWeatherIcon(condition, nightTime: nightTime, index: index)
if (index == ) {
return
}
}
}
}
}
}
self.loading.text = "Weather info is not available!"
}
然后,根据不同的解析结果,跟新当前的和后面几个小时的天气调用方法updateWeatherIcon、updatePictures更新天气图片(白天、晚上、天气)。示例工程中会有详细的实现。这里略去不提。
这个时候,运行APP之后已经可以看到天气预报的主界面了。
用Swift实现一款天气预报APP(二)的更多相关文章
- 用Swift实现一款天气预报APP(三)
这个系列的目录: 用Swift实现一款天气预报APP(一) 用Swift实现一款天气预报APP(二) 用Swift实现一款天气预报APP(三) 通过前面的学习,一个天气预报的APP已经基本可用了.至少 ...
- 用Swift实现一款天气预报APP(一)
这个系列的目录: 用Swift实现一款天气预报APP(一) 用Swift实现一款天气预报APP(二) 用Swift实现一款天气预报APP(三) Swift作为现在苹果极力推广的语言,发展的非常快.这个 ...
- 毕业设计--天气预报App
9月中旬,开始动手做我的毕业设计了,之前一直在纠结做啥,后来想想,既然是做毕业设计,那就大胆地做点自己没接触过的东西吧.然后网上查找资料得知做天气预报需要用到开放的API,而且要用那种现在还在维护的, ...
- android入门学习-天气预报app(一)
引言 学习<android第一行代码>根据书本开发的天气预报app,主要用于熟练操作android开发(android studio3.0平台). 今天主要分享一下从服务器上获取天气信息, ...
- 基于Android开发的天气预报app(源码下载)
原文:基于Android开发的天气预报app(源码下载) 基于AndroidStudio环境开发的天气app -系统总体介绍:本天气app使用AndroidStudio这个IDE工具在Windows1 ...
- 越折腾越好用的 3 款开源 APP
高中的时候我特别喜欢捣鼓手机,然后我一个哥们儿在我的强烈推荐下买了个 HTC Dream(G1) 手机. G1 作为谷歌的第一个亲儿子,它出厂搭载的是 Android 1.5 系统,但当时已经出到了 ...
- 个人开发者做一款Android App需要知道的事情
个人开发者做一款Android App需要知道的事情 在大学时, 自己是学计算机专业的,而且还和老师一起做过一年半的项目. 有时候是不是有这样的想法,做一个自己的网站.但一直未付诸行动.2012年时, ...
- 分享一下一款直播App开发的过程
听说有人声称开发一款直播App不仅耗时还非常昂贵,今天跟大家说道一下,开发一款直播App到底分几步走? 第一步:分解直播App的功能,我们以X客为例 视频直播功能,这是一款直播App最主要的功能,要能 ...
- 使用SSM框架 搭建属于自己的APP二维码合成、解析、下载
最近公司的app上线了,在推广APP的时候出现了一个问题,因为Android和IOS的下载地址不一样,那么在推广的时候就要推广两个二维码,这样比较麻烦,如何简化我们的推广,让IOS用户扫描二维码的时候 ...
随机推荐
- 【转】Ubuntu12.04安装YouCompleteMe插件
原文网址:http://m.blog.csdn.net/blog/unhappypeople/19160243 以前用的都是ctags+omnicomplete+acp的方式,这次换成clang自解析 ...
- windows下php7安装redis扩展
windows下php7安装redis扩展windows下开发用的wamp集成的环境,想装个php-redis扩展.php_redis.dll下载地址:https://pecl.php.net/pac ...
- 显示列表控件(引用SourceGrid)
using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; u ...
- 判断网络类(获取mac) InternetCheck
using System; using System.Collections.Generic; using System.Net.NetworkInformation; using System.Ru ...
- git推送报错: No path specified. See 'man git-pull' for valid url syntax或does not appear to be a git repository以及remote: error: insufficient permission for adding an object to repository databa
本地(windows)代码想推送到linux自己搭建的git服务端,第一步是建立本地与服务端的关联,第二步是本地推送到服务端. 第一步需要看你的本地工程是否从git上clone来的,如果是clone来 ...
- Java 获取字符串指定下标位置的值 charAt()
Java手册 charAt public char charAt(int index) 返回指定索引处的 char 值.索引范围为从 0 到 length() - 1.序列的第一个 char 值位于索 ...
- (2/24) 快速上手一个webpack的demo
写在前面:该部分的安装都是基于windows系统的,且此处的webpack的版本为:3.6.0. 1.安装webpack 1.1 安装方法: 用win+R打开运行对话框,输入cmd进入命令行模式.然后 ...
- 记录一些sql,怕忘了
SELECT business_line,count(*) FROM zc_db.t_bug group by business_line; 这个是展示的,显示某一项一共有多少个xxx,注意是grou ...
- mybatis相对于ibatis的优势
2010年,apache的Ibatis框架停止更新,并移交给了google团队,同时更名为MyBatis.从2010年后Ibatis在没更新过,彻底变成了一个孤儿框架.一个没人维护的框架注定被myba ...
- netbeans php环境搭建
jdk必须: sudo apt-get install openjdk-7-jdk