用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-1] as CLLocation

        if (location.horizontalAccuracy > 0) {
            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":0]
    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)[0]["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) + 32)
                    }
                    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(25)
                    self.location.text = name
                }
            }

            if let weatherArray = (jsonResult["list"]? as? NSArray) {
                for index in 0...4 {
                    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) + 32)
                            }
                            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 == 1) {
                                self.temp1.text = "\(temperature)°"
                            }
                            if (index == 2) {
                                self.temp2.text = "\(temperature)°"
                            }
                            if (index == 3) {
                                self.temp3.text = "\(temperature)°"
                            }
                            if (index == 4) {
                                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==1) {
                                self.time1.text = forecastTime
                            }
                            if (index==2) {
                                self.time2.text = forecastTime
                            }
                            if (index==3) {
                                self.time3.text = forecastTime
                            }
                            if (index==4) {
                                self.time4.text = forecastTime
                            }
                        }
                        if let weather = (perTime["weather"]? as? NSArray) {
                            var condition = (weather[0] as NSDictionary)["id"] as Int
                            var icon = (weather[0] as NSDictionary)["icon"] as String
                            var nightTime = false
                            if icon.rangeOfString("n") != nil{
                                nightTime = true
                            }
                            self.updateWeatherIcon(condition, nightTime: nightTime, index: index)
                            if (index == 4) {
                                return
                            }
                        }
                    }
                }
            }
        }
        self.loading.text = "Weather info is not available!"
    }

然后,根据不同的解析结果,跟新当前的和后面几个小时的天气调用方法updateWeatherIconupdatePictures更新天气图片(白天、晚上、天气)。示例工程中会有详细的实现。这里略去不提。

这个时候,运行APP之后已经可以看到天气预报的主界面了。

 

欢迎加群互相学习,共同进步。QQ群:iOS: 58099570 | Android: 330987132 | Go:217696290 | Python:336880185 | 做人要厚道,转载请注明出处!http://www.cnblogs.com/sunshine-anycall/p/4149052.html

时间: 2024-09-10 04:50:37

用Swift实现一款天气预报APP(二)的相关文章

用Swift实现一款天气预报APP(一)

Swift作为现在苹果极力推广的语言,发展的非常快.这个语言就和她的名字一样,比OC减少了很多的文件和代码量.头文件,bye bye啦,再不用查个代码上下的头文件源文件切换了.而且语言本身也增加了很多的安全性的考虑,比如类的初始化个阶段的检查等.不按照规定的写就不能编译通过!本文假定你有一定的编程基础,和一定的Swift基础.如果木有的话,请看这里迅速补起. 本文就用Swif写一个APP,让各位一起来体会一下Swift到底是好在了哪里.为了简单,教程会使用Storyboard,而不是手动的添加C

我打算开发一款手机app,这个app能够同时显示一些国外的城市的时钟

问题描述 我打算开发一款手机app,这个app能够同时显示一些国外的城市的时钟 我打算开发一款手机app,这个app能够同时显示一些国外的城市的时钟,也就是显示不同时区 当前的时间.谁能告诉我具体的实现的思路.最好把安卓的实现思路和IOS的实现思路都 讲解一下. 解决方案 Android和Ios建议你采用Html5来实现,比较方便,在两种系统上都可以运行 你可以先尝试做一个时钟的app,然后就可以扩展到你的需求上 解决方案二: 功能上,应该比较简单,找到城市对应的时区,按时区来显示城市的当前时间

Android天气预报app改进版_Android

最近总是有人来和我说我以前写的一个小app无法正常获取数据~Android简易版天气预报app 今天就又运行了下来查找问题,发现或许是接口有限制吧,不能在多台手机使用同个apikey 然后,发现了我写的代码实在乱七八糟,界面也实在不好看,就又重写了一遍,小小地修改了一遍,开发环境改为了Android Studio 最终效果图如下   工程图如下 一.获取地区信息 做这么一个天气预报app,首先就要获取到国内地区列表 (在我的另一篇博客有介绍:向任意网址发起数据请求) 中国天气网开放有天气预报接口

Android天气预报app改进版

最近总是有人来和我说我以前写的一个小app无法正常获取数据~Android简易版天气预报app 今天就又运行了下来查找问题,发现或许是接口有限制吧,不能在多台手机使用同个apikey 然后,发现了我写的代码实在乱七八糟,界面也实在不好看,就又重写了一遍,小小地修改了一遍,开发环境改为了Android Studio 最终效果图如下 工程图如下 一.获取地区信息 做这么一个天气预报app,首先就要获取到国内地区列表 (在我的另一篇博客有介绍:向任意网址发起数据请求) 中国天气网开放有天气预报接口,访

寻找下一款Prisma APP:深度学习在图像处理中的应用探讨

在9月23日到9月24日的MDCC 2016年中国移动者开发大会"人工智能与机器人"专场中,阿里云技术专家周昌进行了题为<寻找下一款Prisma APP:深度学习在图像处理中的应用探讨>的演讲.演讲中,他主要介绍深度学习在图像处理领域中的应用,主要内容包括:传统的图像处理:如超分辨.灰度图彩色化.2D/3D转换等:图像/视频风格化:图像生成. 以下是现场分享整理. 图像处理类过程主要分为三步,包括图像增强.图像变换.图像生成.图像增强是指从图像到图像:图像变换是指从图像到另

警惕!近千款安卓APP被植入恶意程序

近日,有安全研究人员在扫描Android(安卓)平台时,发现近千款手机APP中被植入了广告木马程序Xavier,关键的是这些应用已被数百万人次下载,并用于窃取用户的个人信息. 近千款安卓APP被植入恶意程序 攻击者利用Xavier收集和泄漏用户的设备数据,硬件层面包括制造商.SIM卡制造商.产品名称.设备ID等等,个人设定则包括设备名称.使用语言.操作系统版本.已安装应用程序.Google Play帐号,以及最常使用的电子邮件地址等. 除了盗取数据外,攻击已进化到让骇客可以远端锁定移动设备并安装

使用MVVM-Sidekick开发Universal App(二)

原文:使用MVVM-Sidekick开发Universal App(二) 上一篇文章已经建立了基本的实体类,并且搞定了多语言的问题,以后在app里用字符串的时候就可以从资源文件中取了.现在继续进行. 一.添加一个页面 CurrencyExchanger首页是一个货币兑换的列表,这个列表比较复杂,我们先不管,先从简单的页面做起.首先要有一个添加货币的页面,显示所有可添加的货币列表.在WP项目中右键添加一个页面,命名为AddCurrencyPage.xaml. 然后MVVM-Sidekick自动添加

azure-公司想做一款社交app,用户为国内外用户

问题描述 公司想做一款社交app,用户为国内外用户 公司想做一款社交app,用户为国内外用户,如果用香港那边的Azure服务器,会不会有政策上的问题?貌似国家对社交这块管的比较严 解决方案 香港没有azure机房,只有新加坡.日本.澳大利亚.美国.巴西.欧洲有.如果放在境外,政府不会管,也管不着.但是如果你的用户透过它做了不好的事,政府会偷偷摸摸屏蔽境内用户的访问.

把iPad2变成PC!5款实用APP体验云计算

第1页:不支持Flash又如何!iPad2控制PC 泡泡网CPU频道8月5日 iOS系统不支持flash的特性经常被人诟病,在平板机能否取代电脑的辩论中,人们也经常拿Windows平台上的各种日常应用来证明,iPad不可能取代PC.尽管iPad和PC的市场定位并不一样,但是得益于全触控和超便携的属性,iPad凭借优秀的用户体验还是给PC市场带来了强烈冲击,今年一季度PC出货量同比下降20%就是拜iPad等平板机所赐.iPad和PC在使用上各有利弊,那么有没有办法将二者的优势整合呢?云计算就是这样