Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

iOS

Stormy with user location and updated city location label.

import UIKit
import CoreLocation
import MapKit

class ViewController: UIViewController, CLLocationManagerDelegate, UIAlertViewDelegate {

    private let apiKey = "Your api key here"



    // Location Manager
    var seenError : Bool = false
    var locationFixAchieved : Bool = false
    var locationStatus : NSString = "Not Started"
    var locationManager: CLLocationManager!
    var userLocation : String!
    var userLatitude : Double!
    var userLongitude : Double!

    // Current Weather outlets
    @IBOutlet weak var iconView: UIImageView!
    @IBOutlet weak var currentTimeLabel: UILabel!
    @IBOutlet weak var temperatureLabel: UILabel!
    @IBOutlet weak var humidityLabel: UILabel!
    @IBOutlet weak var precipitationLabel: UILabel!
    @IBOutlet weak var summaryLabel: UILabel!
    @IBOutlet weak var refreshButton: UIButton!
    @IBOutlet weak var refreshActivityIndicator: UIActivityIndicatorView!
    @IBOutlet weak var cityNameLabel: UILabel!


    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        refreshActivityIndicator.hidden = true

        initLocationManager()

    }

    //Location Code

    func initLocationManager() {
        seenError = false
        locationFixAchieved = false
        locationManager = CLLocationManager()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.requestAlwaysAuthorization()
        locationManager.startUpdatingLocation()



    }

    func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
        locationManager.stopUpdatingLocation()
        if ((error) != nil) {
            if (seenError == false) {
                seenError = true
                print(error)
            }
        }
    }

    func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
        if (locationFixAchieved == false) {
            locationFixAchieved = true
            var locationArray = locations as NSArray
            var locationObj = locationArray.lastObject as CLLocation
            var coord = locationObj.coordinate
            var latitude = coord.latitude
            var longitude = coord.longitude
            //println(coord.latitude)
            //println(coord.longitude)

            self.userLatitude = coord.latitude
            self.userLongitude = coord.longitude
            //userLocation = "\(userLatitude),\(userLongitude)"

            getCurrentWeatherData()

            CLGeocoder().reverseGeocodeLocation(manager.location, completionHandler: {(placemarks, error)->Void in

                if (error != nil) {
                    println("Reverse geocoder failed with error" + error.localizedDescription)
                    return
                }

                if placemarks.count > 0 {
                    let pm = placemarks[0] as CLPlacemark
                self.displayLocationInfo(pm)
                } else {
                    println("Problem with the data received from geocoder")
                }

            })


        }


    }




        func displayLocationInfo(placemark: CLPlacemark?) {
            if let containsPlacemark = placemark {
                //stop updating location to save battery life
                locationManager.stopUpdatingLocation()
                let locality = (containsPlacemark.locality != nil) ? containsPlacemark.locality : ""
                let postalCode = (containsPlacemark.postalCode != nil) ? containsPlacemark.postalCode : ""
                let administrativeArea = (containsPlacemark.administrativeArea != nil) ? containsPlacemark.administrativeArea : ""
                let country = (containsPlacemark.country != nil) ? containsPlacemark.country : ""
                println(locality)
                println(postalCode)
                println(administrativeArea)
                println(country)

                // cityNameLabel outlet
                self.cityNameLabel.text = placemark?.locality


                // println("locality is: \(placemark?.locality)")
                // println("user location: \(userLocation)")

            }

    }


    func locationManager(manager: CLLocationManager!,
        didChangeAuthorizationStatus status: CLAuthorizationStatus) {

            switch CLLocationManager.authorizationStatus() {
            case .NotDetermined:
                locationManager.requestAlwaysAuthorization()
            case .Authorized:
                locationManager.startUpdatingLocation()
            case .AuthorizedWhenInUse, .Restricted, .Denied:
                let alertController = UIAlertController(
                    title: "Background Location Access Disabled",
                    message: "In order to see local weather data, please open this app's settings and set location access to 'Always'.",
                    preferredStyle: .Alert)

                let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
                alertController.addAction(cancelAction)

                let openAction = UIAlertAction(title: "Open Settings", style: .Default) { (action) in
                    if let url = NSURL(string:UIApplicationOpenSettingsURLString) {
                        UIApplication.sharedApplication().openURL(url)
                    }
                }
                alertController.addAction(openAction)

                self.presentViewController(alertController, animated: true, completion: nil)



    }
    }


    //Weather


    func getCurrentWeatherData() -> Void {

        userLocation = "\(userLatitude),\(userLongitude)"
        let baseURL = NSURL(string: "https://api.forecast.io/forecast/\(apiKey)/")
        let forecastURL = NSURL(string: "\(userLocation)", relativeToURL: baseURL)
        let sharedSession = NSURLSession.sharedSession()
        let downloadTask: NSURLSessionDownloadTask = sharedSession.downloadTaskWithURL(forecastURL!, completionHandler: { (location: NSURL!, response: NSURLResponse!, error: NSError!) -> Void in

            if (error == nil){
                let dataObject = NSData(contentsOfURL: location)
                let weatherDictionary: NSDictionary = NSJSONSerialization.JSONObjectWithData(dataObject!, options: nil, error: nil) as NSDictionary

                let currentWeather = Current(weatherDictionary: weatherDictionary)

                //Test Connection and API with the following
                println("Temp is: \(currentWeather.temperature)")
                println("Time is: \(currentWeather.currentTime!)")
                //println(weatherDictionary)

                //Main dispatch 


                dispatch_async(dispatch_get_main_queue(), { () -> Void in
                    self.temperatureLabel.text = "\(currentWeather.temperature)"
                    self.iconView.image = currentWeather.icon!
                    self.currentTimeLabel.text = "At \(currentWeather.currentTime!) it is"
                    self.humidityLabel.text = "\(currentWeather.humidity)"
                    self.precipitationLabel.text = "\(currentWeather.precipProbability)"
                    self.summaryLabel.text = "\(currentWeather.summary)"




                    // Stop refresh animation
                    self.refreshActivityIndicator.stopAnimating()
                    self.refreshActivityIndicator.hidden = true
                    self.refreshButton.hidden = false
                })

           } else {

                let networkIssueController = UIAlertController(title: "Error", message: "Unable to load data. Connectivity error!", preferredStyle: .Alert)

                let okButton = UIAlertAction(title: "OK", style: .Default, handler: nil)
                networkIssueController.addAction(okButton)

                let cancelButton = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
                networkIssueController.addAction(cancelButton)

                self.presentViewController(networkIssueController, animated: true, completion: nil)

                dispatch_async(dispatch_get_main_queue(), { () -> Void in
                    // Stop refresh animation
                    self.refreshActivityIndicator.stopAnimating()
                    self.refreshActivityIndicator.hidden = true
                    self.refreshButton.hidden = false
                })

            }

        })
        downloadTask.resume()
    }




    @IBAction func refresh() {

        initLocationManager()
        refreshButton.hidden = true
        refreshActivityIndicator.hidden = false
        refreshActivityIndicator.startAnimating()


    }





    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

You must import any of these keys to your info.plist file. Open as source code and paste in which ever one you need.

<key>NSLocationAlwaysUsageDescription</key>
    <string>We use you location to give you weather data</string>
    <key>NSLocationUsageDescription</key>
    <string>We use your location to give you weather data</string>
    <key>NSLocationWhenInUsageDescription</key>

Thanks Bradley Maskell :-) I commented out the :

     //stop updating location to save battery life
     locationManager.stopUpdatingLocation()

because it didn't work with the refresh function. i placed it in the refresh function at the end and that works. Im not sure if that is correct, but i am learning b doing at the moment.

4 Answers

To initialize, check for location authorization, and find coordinates for the api, and reverse geocoder to get your city name.

Which info.plist do i add it to. My app doesn't work and I did exactly what you did.

Thank's Bradley. But why do You have 3 location manager functions ?

Bradley - I believe I have pasted your code exactly and pasted in the keys above to the info.plist. The build fails and I am getting the following error that I have never seen before.

error: couldn't parse contents of '/Users/..../Stormy/StormyTests/Info.plist': The data couldn’t be read because it isn’t in the correct format.

Any idea how to fix this?

How did you add the data to your plist file?

K Jo did you enter the entitlement keys in your StormyTest plist or your main app's plist?