Coordinates return nil, I have entered requestAlwaysAuthorization and requestWhenInUseAuthorization in the plist file, but yet nothing happens.
Here is my code.
what is going in this code? I can't find where the error is. It's just returning nil for cordinates
import UIKit
import CoreLocation
class ViewController: UIViewController,CLLocationManagerDelegate {
//Location
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 "Display Objects"
#IBOutlet weak var userLocationLabel: UILabel!
#IBOutlet weak var visibilityLabel: UILabel!
#IBOutlet weak var temperatureLabel: UILabel!
#IBOutlet weak var iconView: UIImageView!
#IBOutlet weak var currentTimeLabel: UILabel!
#IBOutlet weak var humidityLabel: UILabel!
#IBOutlet weak var precipitationLabel: UILabel!
#IBOutlet weak var summaryLabel: UILabel!
#IBOutlet weak var windSpeedLabel: UILabel!
#IBOutlet weak var refreshButton: UIButton!
#IBOutlet weak var refreshActivityIndicator: UIActivityIndicatorView!
//API KEY
private let apiKey = "09ca8e3e75fafbadaf4b8594dabe860e"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
refreshActivityIndicator.hidden = true
getCurrentWeatherData()
}
//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]!) {
CLGeocoder().reverseGeocodeLocation(manager.location, completionHandler: {(placemarks, error)->Void in
let pm = placemarks[0] as! CLPlacemark
self.displayLocationInfo(pm)
})
if (locationFixAchieved == false) {
locationFixAchieved = true
var locationArray = locations as NSArray
var locationObj = locationArray.lastObject as! CLLocation
var coord = locationObj.coordinate
self.userLatitude = coord.latitude
self.userLongitude = coord.longitude
getCurrentWeatherData()
}
}
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)
self.userLocationLabel.text = "\(locality), \(administrativeArea)"
}
}
func locationManager(manager: CLLocationManager!,
didChangeAuthorizationStatus status: CLAuthorizationStatus) {
var shouldIAllow = false
switch status {
case CLAuthorizationStatus.Restricted:
locationStatus = "Restricted Access to location"
case CLAuthorizationStatus.Denied:
locationStatus = "User denied access to location"
case CLAuthorizationStatus.NotDetermined:
locationStatus = "Status not determined"
default:
locationStatus = "Allowed to location Access"
shouldIAllow = true
}
NSNotificationCenter.defaultCenter().postNotificationName("LabelHasbeenUpdated", object: nil)
if (shouldIAllow == true) {
NSLog("Location to Allowed")
// Start location services
locationManager.startUpdatingLocation()
} else {
NSLog("Denied access: \(locationStatus)")
}
}
func getCurrentWeatherData() -> Void {
userLocation = "\(userLatitude),\(userLongitude)"
// DC cord. = "44.029002,-92.855343"
//URL
let baseURL = NSURL(string: "https://api.forecast.io/forecast/\(apiKey)/")
let forecastURL = NSURL(string:"\(userLocation)", relativeToURL: baseURL)
// creates data object; to get and save data from online
let sharedSession = NSURLSession.sharedSession()
let downloadTask: NSURLSessionDownloadTask = sharedSession.downloadTaskWithURL(forecastURL!, completionHandler: { (location:NSURL!, response:NSURLResponse!, error: NSError!) -> Void in
// if everything goes alright (no errors then run code)
if (error == nil) {
//turn JSON dictionary to NSDictionary so it's easier to work with
let dataObject = NSData(contentsOfURL: location)
let weatherDictionary: NSDictionary = NSJSONSerialization.JSONObjectWithData(dataObject!, options: nil, error: nil) as! NSDictionary
//bring weather dictionary from the CurrentWeather file
let currentWeather = Current(weatherDictionary: weatherDictionary)
println(weatherDictionary)
//update view with current weather information
dispatch_sync(dispatch_get_main_queue(), { () -> Void in
self.temperatureLabel.text = "\(currentWeather.temperature)"
self.iconView.image = currentWeather.icon!
self.currentTimeLabel.text = "\(currentWeather.currentTime!)"
self.humidityLabel.text = "\(currentWeather.humidity * 100)%"
self.precipitationLabel.text = "\(currentWeather.precipProbability * 100)%"
self.summaryLabel.text = "\(currentWeather.summary)"
self.windSpeedLabel.text = "\(currentWeather.windSpeed) MPH"
self.visibilityLabel.text = "\(currentWeather.visibility) Mi"
//stop refresh animation
self.refreshActivityIndicator.stopAnimating()
self.refreshActivityIndicator.hidden = true
self.refreshButton.hidden = false
})
}
else {
let networkIssueController = UIAlertController(title: "Error", message: "Network Connectivity Error! Can't load data", preferredStyle: .Alert)
println(error)
//information for ok button in alertView
let okButton = UIAlertAction(title: "OK", style: .Default, handler: nil)
networkIssueController.addAction(okButton)
//information for cancel button in alertView
let cancelButton = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
networkIssueController.addAction(cancelButton)
//if an error occurs show alert
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()
}
//refresh method
#IBAction func refresh() {
getCurrentWeatherData()
refreshButton.hidden = true
refreshActivityIndicator.hidden = false
refreshActivityIndicator.startAnimating()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Perhaps you already handled all these situations in code that is not shown, but here is a list of things that I think are missing in your code:
I don't think you call initLocationManager in your code.
getCurrentWeatherData() in your viewDidLoad() won't work, you need to call it from locationManager(didUpdateLocations).
Adding requestAlwaysAuthorization or requestWhenInUseAuthorization alone to the application options is not enough. You also need to provide NSLocationAlwaysUsageDescription with text explaining why you need access.
You need to check the authorization status. If it is undetermined, you need to request access and implement a callback to wait for user response. If you call requestAuthorization with any other status than undetermined, I believe the function does not do anything and does not provide any feedback.
For further troubleshooting I would suggest that you (1) get the status of the authorization and (2) put a print statement or a breakpoint to the locationManager(didUpdateLocations) to see if it gets called at all.
I struggled with this step as well. And MirekE has pointed out all great points. I have built on what MirekE stated and showed yo some code to maybe help explain it in more detail.
Step 1) Make sure your info.plist have the following.Do not forget to give them a value like - Need to use your location to get the weather for your current location. Or some thing along those lines.
NSLocationAlwaysUsageDescription
Privacy - Location Usage Description
NSLocationWhenInUseUsageDescription
Step 2) I added this code to my project and combined with step 1 then I would get the alert views to allow location services.
//this is part of my viewDidLoad()
if ( ios8() ) {
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
}
locationManager.startUpdatingLocation()
/*
iOS 8 Utility
*/
func ios8() -> Bool {
if ( NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1 ) {
return false
} else {
return true
}
}
Step 3) I used defaults to store them for another purpose.
// MARK: CLLocationManagerDelegate
public func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
var location:CLLocation = locations[locations.count-1]as! CLLocation
if (location.horizontalAccuracy > 0) {
self.locationManager.stopUpdatingLocation()
self.defaults.setDouble(location.coordinate.latitude, forKey: "lat")
self.defaults.setDouble(location.coordinate.longitude, forKey: "lon")
println("lat \(location.coordinate.latitude) lon \(location.coordinate.longitude)")
defaults.synchronize()
updateLocationInfo() // This is where you would call your getCurrentWeatherData()
}
}
// MARK: locationManager
public func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
println(error)
self.loading.text = "Can't get your location!"
}
Related
I created a singleton class to handle location authorization because I needed it for several views in my app. So I created the below Location.swift class.
NOTE: I have added correctly into Info.plist, and have looked at several other posts but none seem to address this (at least none I found)
protocol LocationServiceDelegate {
func tracingLocation(currentLocation: CLLocation)
func tracingLocationDidFailWithError(error: NSError)
}
class Location: NSObject,CLLocationManagerDelegate {
var latitude: Double!
var longitude: Double!
var currentLocation : CLLocation!
var locationManager: CLLocationManager?
var lastLocation: CLLocation?
var delegate: LocationServiceDelegate?
static let sharedInstance:Location = {
let instance = Location()
return instance
}()
override init() {
super.init()
self.locationManager = CLLocationManager()
self.locationManager?.delegate = self
guard let locationManagers = self.locationManager else {
return
}
if CLLocationManager.authorizationStatus() == .notDetermined {
locationManagers.requestWhenInUseAuthorization()
}
locationManagers.desiredAccuracy = kCLLocationAccuracyBest
locationManagers.pausesLocationUpdatesAutomatically = false
locationManagers.distanceFilter = 0.1
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else {
return
}
self.lastLocation = location
updateLocation(currentLocation: location)
}
#nonobjc func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
locationManager?.requestWhenInUseAuthorization()
break
case .authorizedWhenInUse:
locationManager?.startUpdatingLocation()
break
case .authorizedAlways:
locationManager?.startUpdatingLocation()
break
case .restricted:
// restricted by e.g. parental controls. User can't enable Location Services
break
case .denied:
// user denied your app access to Location Services, but can grant access from Settings.app
break
}
}
// Private function
private func updateLocation(currentLocation: CLLocation){
guard let delegate = self.delegate else {
return
}
delegate.tracingLocation(currentLocation: currentLocation)
}
private func updateLocationDidFailWithError(error: NSError) {
guard let delegate = self.delegate else {
return
}
delegate.tracingLocationDidFailWithError(error: error)
}
func startUpdatingLocation() {
print("Starting Location Updates")
self.locationManager?.startUpdatingLocation()
currentLocation = locationManager?.location
Location.sharedInstance.latitude = currentLocation.coordinate.latitude
Location.sharedInstance.longitude = currentLocation.coordinate.longitude
print(Location.sharedInstance.latitude, Location.sharedInstance.longitude)
// self.locationManager?.startMonitoringSignificantLocationChanges()
}
func stopUpdatingLocation() {
print("Stop Location Updates")
self.locationManager?.stopUpdatingLocation()
}
}
My app is crashing, and I think its because the location authorization is not set in the beginning. The funny thing is that the request alert which prompts the user to allow location services doesn't show up until you leave the app.
Once you close the app and accept the location services, the app works fine. So my question is, why isn't the alert showing up?
it is also interesting to note that this is only occurring through an actual device. In the simulator the alert pops up as expected when the initial view is loading.
my first view that is supposed to load and show data is as follows:
import UIKit
import Alamofire
class CurrentWeatherVC: UIViewController {
#IBOutlet weak var locationLabel: UILabel!
#IBOutlet weak var weatherIcon: UIImageView!
#IBOutlet weak var currentTempLabel: UILabel!
#IBOutlet weak var weatherTypeLabel: UILabel!
var currentWeather : CurrentWeather!
override func viewDidLoad() {
super.viewDidLoad()
Location.sharedInstance.locationManager(manager: Location.sharedInstance.locationManager, didChangeAuthorizationStatus: .authorizedWhenInUse)
currentWeather = CurrentWeather()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
Location.sharedInstance.startUpdatingLocation()
currentWeather.downloadWeatherDetails {
self.updateMainUI()
}
}
func updateMainUI() {
//Double value convterted to string for current temp.
//Added the degree symbol here
//For forecast it gets added in before saved into list so be aware of that.
currentTempLabel.text = "\(currentWeather.currentTemp)°"
weatherTypeLabel.text = currentWeather.weatherType
locationLabel.text = currentWeather.cityName
weatherIcon.image = UIImage(named: currentWeather.weatherType)
}
}
I suspect downloadWeatherDetailss implementation uses a dataTask or one of the other NSURLSession methods that run in background.
Make sure to call UI stuff only on the mainQueue:
// ...
DispatchQueue.main.async {
self.updateMainUI()
}
// ...
I have an issue with Core Location, I've followed the setup from
https://stackoverflow.com/a/24696878/6140339 this answer, and placed all my code in AppDelegate, but I can't figure out how to call it, so I created another function that does the same thing as didUpdateLocations, and called it inside my findMyLocation button, and it is only returning nil.
I have tried setting a custom location in Simulator, still nil, I tried using the debugger and setting a location, I even tried testing it on my iphone to see if i could get a location, and still nothing.
is there a way to call didUpdateLocations from my button?
Or am I just doing something else wrong that im missing.
here is my code in my viewController:
import UIKit
import Social
import CoreLocation
class FirstViewController: UIViewController{
//let social = socialFunctions()
let locationManager = CLLocationManager()
let location = locationFunctions()
var locationFixAchieved = false
var currentLocation = CLLocation()
#IBOutlet weak var locationLabel: UILabel!
#IBAction func findMyLocation(sender: AnyObject) {
updateLocation()
print("Location: \(locationManager.location)")
}
#IBAction func postToFacebookButton(sender: UIButton) {
postToFacebook()
}
#IBAction func postTweetButton(sender: UIButton) {
postToTwitter()
}
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return .LightContent
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
//MARK: - SOCIAL FUNCTIONS
func postToFacebook(){
if(SLComposeViewController.isAvailableForServiceType(SLServiceTypeFacebook)){
let socialController = SLComposeViewController(forServiceType: SLServiceTypeFacebook)
//creates post with pre-desired text
socialController.setInitialText("")
presentViewController(socialController, animated: true, completion: nil)
}
}
func postToTwitter(){
if(SLComposeViewController.isAvailableForServiceType(SLServiceTypeTwitter)){
let socialController = SLComposeViewController(forServiceType: SLServiceTypeTwitter)
//creates post with pre-desired text
socialController.setInitialText("")
presentViewController(socialController, animated: true, completion: nil)
}
}
//MARK: - LOCATION FUNCTIONS
func updateLocation() {
let locations = [CLLocation]()
if (locationFixAchieved == false) {
locationFixAchieved = true
let locationArray = locations as NSArray
let locationObj = locationArray.lastObject as? CLLocation
let coord = locationObj?.coordinate
if coord?.latitude != nil {
locationLabel.text = ("Location \r\n Latitude: \(coord?.latitude) \r\n Longitude: \(coord?.longitude)")
print("Latitude: \(coord?.latitude)")
print("Longitude: \(coord?.longitude)")
} else {
locationLabel.text = ("Could not find location")
print("LAT & LONG are nil")
}
}
}
}
Here is the code i added to my appDelegate
import UIKit
import CoreLocation
let fvc = FirstViewController()
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var window: UIWindow?
var locationManager: CLLocationManager!
var seenError : Bool = false
var locationFixAchieved: Bool = false
var locationStatus : NSString = "Not Started"
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
initLocationManager();
return true
}
func initLocationManager() {
seenError = false
locationFixAchieved = false
locationManager = CLLocationManager()
locationManager.delegate = self
CLLocationManager.locationServicesEnabled()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
locationManager.stopUpdatingLocation()
if (error == true) {
if (seenError == false) {
seenError = true
print(error)
}
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let locations = [CLLocation]()
if (locationFixAchieved == false) {
locationFixAchieved = true
let locationArray = locations as NSArray
let locationObj = locationArray.lastObject as? CLLocation
let coord = locationObj?.coordinate
print("Latitude: \(coord?.latitude)")
print("Longitude: \(coord?.longitude)")
//fvc.locationLabel.text = ("Location \r\n Latitude: \(coord?.latitude) \r\n Longitude: \(coord?.longitude)")
}
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
var shouldIAllow = false
switch status {
case CLAuthorizationStatus.Restricted:
locationStatus = "Restricted Access to location"
case CLAuthorizationStatus.Denied:
locationStatus = "User denied access to location"
case CLAuthorizationStatus.NotDetermined:
locationStatus = "Status not determined"
default:
locationStatus = "Allowed location Access"
shouldIAllow = true
}
NSNotificationCenter.defaultCenter().postNotificationName("LabelHasBeenUpdated", object: nil)
if (shouldIAllow == true) {
NSLog("Location Allowed")
//Start location services
locationManager.startUpdatingLocation()
} else {
NSLog("Denied access: \(locationStatus)")
}
}
Following dan's comment(thank you) all I had to do was delete the first line, and it shows the coordinates, i have yet to figure out how to call my function to change the label text, but i will post that when i figure it out. EDIT: posted solution below
I changed
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let locations = [CLLocation]()
if (locationFixAchieved == false) {
locationFixAchieved = true
let locationArray = locations as NSArray
let locationObj = locationArray.lastObject as? CLLocation
let coord = locationObj?.coordinate
print("Latitude: \(coord?.latitude)")
print("Longitude: \(coord?.longitude)")
//fvc.locationLabel.text = ("Location \r\n Latitude: \(coord?.latitude) \r\n Longitude: \(coord?.longitude)")
}
}
deleting let locations = [CLLocation]()
This is how i called it when i press the button.
func updateLocation() {
let manager = CLLocationManager()
let locValue : CLLocationCoordinate2D = (manager.location?.coordinate)!;
let long = locValue.longitude
let lat = locValue.latitude
print(long)
print(lat)
locationLabel.text = ("Location \r\nLatitude: \(lat) \r\nLongitude: \(long)")
}
I am building a CoreLocation based app which which show user their location based on 6 parameters like Latitude, Longitude, Horizontal Accuracy, Altitude, Vertical Accuracy, Distance Traveled.
It suppose to ask user's permission to allow access of location at first time but I have tried resetting all simulator's settings too.
Gray part will be filled with map later.
This is how my View.Controller.swift look like:
// Created by 16246 on 6/7/16.
// Copyright © 2016 16246. All rights reserved.
//
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
private let LocationManager = CLLocationManager()
private var previousPoint:CLLocation?
private var totalMovementDistance:CLLocationDistance = 0
#IBOutlet var latitudeLabel: UILabel!
#IBOutlet var longitudeLabel: UILabel!
#IBOutlet var horizontalAccuracy: UILabel!
#IBOutlet var altitudeLabel: UILabel!
#IBOutlet var verticalAccuracyLabel: UILabel!
#IBOutlet var distanceTraveledLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
LocationManager.delegate = self
LocationManager.desiredAccuracy = kCLLocationAccuracyBest
LocationManager.requestAlwaysAuthorization()
// Do any additional setup after loading the view, typically from a nib.
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
print("Authorization Status Changed to \(status.rawValue)")
switch status {
case .Authorized, .AuthorizedWhenInUse:
LocationManager.startUpdatingLocation()
default:
LocationManager.stopUpdatingLocation()
}
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
let errorType = error.code == CLError.Denied.rawValue ? "Access Denied": "Error \(error.code)"
let alertController = UIAlertController(title: "Location Manager Error", message: errorType, preferredStyle: .Alert)
let okAction = UIAlertAction(title: "OK", style: .Cancel, handler: {action in})
alertController.addAction(okAction)
presentViewController(alertController, animated: true, completion: nil)
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let newLocation = (locations as [CLLocation]) [locations.count-1]
let latitudeString = String(format: "%g\u{00B0}", newLocation.coordinate.latitude)
latitudeLabel.text = latitudeString
let longitudeString = String(format: "%g\u{00B0}", newLocation.coordinate.longitude)
longitudeLabel.text = longitudeString
let horizontalAccuracyString = String(format: "%g\u{00B0}", newLocation.horizontalAccuracy)
horizontalAccuracy.text = horizontalAccuracyString
let altitudeString = String(format: "%g\u{00B0}", newLocation.altitude)
altitudeLabel.text = altitudeString
let verticalAccuracyString = String(format: "%g\u{00B0}", newLocation.verticalAccuracy)
verticalAccuracyLabel.text = verticalAccuracyString
if newLocation.horizontalAccuracy < 0 {
return
}
if newLocation.horizontalAccuracy > 100 ||
newLocation.verticalAccuracy > 50 {
return
}
if previousPoint == nil {
totalMovementDistance = 0
} else {
totalMovementDistance += newLocation.distanceFromLocation(previousPoint!)
}
previousPoint = newLocation
let distanceString = String(format: "%gm", totalMovementDistance)
distanceTraveledLabel.text = distanceString
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
This is my Info.plist file:
This is my Simulator:
I want output like this:
I have been stuck on this since past 2 days. Pint of beer for a geek who help me to get along with this.
Move LocationManager.requestAlwaysAuthorization() to the viewDidAppear method.
EDIT:
Ok, you are asking requestAlwaysAuthorization but in info.plist you set the When in usage... key entry, so change the requestAlwaysAuthorization to the requestWhenInUseAuthorization
I have 8 fake users, including me, with different locations on Parse. If user presses on an annotation on my map, I'd like to get an array with their user.username to open a direct chat with the choosen one among them sending the user.username to my next NewChatVC receiver var via prepareForSegue. In order to achieve this, I'm try'n to create an array closeUsersArray with first, for say, ten people selected among closer ones. Distance filter in km seems to be fine, but when I try to fill my array, in the console I get many repetitions instead only 8 names with:
self.closeUsersArray.append(user.username!) //MARK: Test
or a group/array or filled with repetitions of those 8 names this happens with:
println("this is the array of users * \(self.closeUsersArray) *") //MARK: Test
update
I discovered that in locationManager code with println("evaluates") evaluates multiple times, calling displayLocationInfo with calls createAnnotations multiple times. I think that I should try to clear conditions, maybe are too many
below my file, thanks in advance
import UIKit
import MapKit
import CoreLocation
import Parse
class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate, UIActionSheetDelegate {
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var segmentedControl: UISegmentedControl!
let locationManager = CLLocationManager()
let kDefaultKmForUserOnMap = 50.0 //default value
var limitNumberForQueryResults = 10
var closeUsersArray:[String] = [] //MARK: Test
let defaults = NSUserDefaults.standardUserDefaults()
var withinKms : Double!
override func viewDidLoad()
{
super.viewDidLoad()
//MARK: checks if the variable is nil, if yes, attributes a value
if defaults.doubleForKey("withinKms") <= 0 {
defaults.setDouble(kDefaultKmForUserOnMap, forKey: "withinKms")
defaults.synchronize()
withinKms = defaults.doubleForKey("withinKms")
println("MapViewController - viewDidLoad - var kKmRadiusForUsersOnMap was never set before, so now is set to \(withinKms) ")
} else {
withinKms = defaults.doubleForKey("withinKms")
println("MapViewController - viewDidLoad - else occurred and var test is \(withinKms)")
}
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
self.mapView.showsUserLocation = true
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidAppear(animated: Bool)
{
super.viewDidAppear(animated)
self.segmentedControl.selectedSegmentIndex = 0
withinKms = self.defaults.doubleForKey("withinKms")
println("MapViewController - viewDidAppear - radius shown on map is * \(withinKms) * ")
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "fromMapToNewChats" {
//MARK: Hint - this is the standard way to pass data to a NOT embedded VC
var nextVC : NewChatsFromHomeVC = segue.destinationViewController as! NewChatsFromHomeVC
nextVC.calledFromVC = "MapViewController"
nextVC.receivedReceiver = "Specific User"
// // nextVC.filterToParse = self.channleKeywordReceived
}
}
//************************************************
//MARK: send message by touching an annotation
func mapView(mapView: MKMapView!, didSelectAnnotationView view: MKAnnotationView!) {
println("anotation pressed: \(view.annotation.title)")
self.performSegueWithIdentifier("fromMapToNewChats", sender: self)
}
//************************************************
// MARK: - Location Delegate Methods
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!)
{
let point = PFGeoPoint(latitude:manager.location.coordinate.latitude, longitude:manager.location.coordinate.longitude)
let location = locations.last as! CLLocation
let center = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1))
self.mapView.setRegion(region, animated: true)
CLGeocoder().reverseGeocodeLocation(manager.location, completionHandler: {(placemarks, error)->Void in
if (error != nil)
{
println("Error: " + error.localizedDescription)
return
}
if placemarks.count > 0
{
let pm = placemarks[0] as! CLPlacemark
self.displayLocationInfo(pm, point: point)
println("evaluates 3")
}
else
{
println("Error with the data.")
}
})
}
func displayLocationInfo(placemark: CLPlacemark, point: PFGeoPoint)
{
self.locationManager.stopUpdatingLocation()
self.createAnnotations(point, address: "\(placemark.locality) \(placemark.administrativeArea) \(placemark.postalCode) \(placemark.country)")
}
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!)
{
println("Error: " + error.localizedDescription)
}
// timelineMessageDataArray.removeAll(keepCapacity: true) //erase previus contents
// println("timelineData cleared")
// MARK: - Create Annotation
func createAnnotations(point: PFGeoPoint, address: String)
{
var query = PFUser.query()
query?.whereKey("location", nearGeoPoint: point, withinKilometers: withinKms)
query?.orderByAscending("location") //MARK: Put list in order
query?.limit = self.limitNumberForQueryResults
query?.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if error == nil
{
for(var i = 0; i < objects!.count; i++)
{
let user = objects![i] as! PFUser
var myHomePin = MKPointAnnotation()
let userPoint = user["location"] as! PFGeoPoint
myHomePin.coordinate = CLLocationCoordinate2DMake(userPoint.latitude, userPoint.longitude)
myHomePin.title = user.username
myHomePin.subtitle = address
self.mapView.addAnnotation(myHomePin)
// self.closeUsersArray.append(user.username!) //MARK: Test
}
// println("this is the array of users * \(self.closeUsersArray) *") //MARK: Test
}
else
{
println("Error: " + error!.localizedDescription)
}
})
}
// MARK: - Action Delegate
func actionSheet(actionSheet: UIActionSheet, clickedButtonAtIndex buttonIndex: Int)
{
switch(buttonIndex)
{
case 0: //Destructive button
break
case 1: // 25.0 Km
// NSUserDefaults.standardUserDefaults().setDouble(25.0, forKey: "withinKms")
self.defaults.setDouble(50.0, forKey: "withinKms")
self.defaults.synchronize()
self.locationManager.startUpdatingLocation()
break;
case 2: // 50.0 Km
self.defaults.setDouble(100.0, forKey: "withinKms")
self.defaults.synchronize()
self.locationManager.startUpdatingLocation()
break;
case 3: // 100.0 Km
self.defaults.setDouble(200.0, forKey: "withinKms")
self.defaults.synchronize()
self.locationManager.startUpdatingLocation()
break;
case 4: // 200.0 Km
self.defaults.setDouble(300.0, forKey: "withinKms")
self.defaults.synchronize()
self.locationManager.startUpdatingLocation()
break;
default:
break;
}
}
// MARK: - Actions
#IBAction func homeAction(sender: AnyObject)
{
self.dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func indexChanged(sender: UISegmentedControl)
{
switch segmentedControl.selectedSegmentIndex
{
case 0:
println("map clicked") //MARK: this one never evaluates!
case 1:
self.performSegueWithIdentifier("showListView", sender: self)
println("showListView clicked")
default:
break;
}
}
#IBAction func radiusAction(sender: UIButton)
{
// UIActionSheet(title: nil, delegate: self, cancelButtonTitle: "cancel", destructiveButtonTitle: nil, otherButtonTitles: "25.0 Miles", "50.0 Miles", "100.0 Miles", "200.0 Miles").showInView(self.view)
UIActionSheet(title: nil, delegate: self, cancelButtonTitle: "cancel", destructiveButtonTitle: nil, otherButtonTitles: "50 Km", "100 Km", "200 Km", "300 Km").showInView(self.view)
}
#IBAction func profileButton(sender: UIBarButtonItem) {
// let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("ProfileNavControllerID") as? UIViewController
//
// self.presentViewController(vc!, animated: true, completion: nil)
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("ProfileNavControllerID") as! UIViewController
self.presentViewController(vc, animated: true, completion: nil)
}
}
Not sure if this would solve the problem but maybe changing your for loop might help. Try this:
for nearbyUser in objects {
let user = nearbyUser.objectForKey("username")
var myHomePin = MKPointAnnotation()
let userPoint = nearbyUser.objectForKey("location") as! PFGeoPoint
myHomePin.coordinate = CLLocationCoordinate2DMake(userPoint.latitude, userPoint.longitude)
myHomePin.title = ("\(user)")
myHomePin.subtitle = address
self.mapView.addAnnotation(myHomePin)
self.closeUsersArray.append(user) //MARK: Test
}
Try something like that maybe
I am in the process of learning iOS 8 app development with Swift. I have followed a tutorial on Treehouse that walks you through building a weather app in Swift and iOS 8.
As an improvement to the app, the author/tutor suggests using CLLocationManager to get the location of the device to feed into the weather API instead of the hard coded latitude and longitude values.
So having read various tutorial online, I have gone ahead and attempted to implement this suggested improvement.
I have placed the code responsible for getting the location coordinates inside the AppDelegate.swift file.
AppDelegate.swift Code
import UIKit
import CoreLocation
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var window: UIWindow?
var locationManager: CLLocationManager!
var errorOccured: Bool = false
var foundLocation: Bool = false
var locationStatus: NSString = "Not Started"
var location: CLLocationCoordinate2D?
var locationName: String?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
application.setStatusBarHidden(true, withAnimation: .None)
initializeLocationManager()
return true
}
func initializeLocationManager() {
self.locationManager = CLLocationManager()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
self.locationManager.requestAlwaysAuthorization()
self.locationManager.startUpdatingLocation()
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
println("didUpdateLocations running")
if (foundLocation == false) {
self.locationManager.stopUpdatingLocation()
foundLocation = true
var locationArray = locations as NSArray
var locationObj = locationArray.lastObject as CLLocation
var geoCoder = CLGeocoder()
geoCoder.reverseGeocodeLocation(locationObj, completionHandler: { (placemarks, error) -> Void in
var p = placemarks as NSArray
var placemark: CLPlacemark? = p.lastObject as? CLPlacemark
self.locationName = placemark?.name
})
self.location = locationObj.coordinate
}
}
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
locationManager.stopUpdatingLocation()
if ((error) != nil) {
if (errorOccured == false) {
errorOccured = true
print(error)
}
}
}
// authorization status
func locationManager(manager: CLLocationManager!,
didChangeAuthorizationStatus status: CLAuthorizationStatus) {
var shouldIAllow = false
switch status {
case CLAuthorizationStatus.Restricted:
locationStatus = "Restricted Access to location"
case CLAuthorizationStatus.Denied:
locationStatus = "User denied access to location"
case CLAuthorizationStatus.NotDetermined:
locationStatus = "Status not determined"
default:
locationStatus = "Allowed to location Access"
shouldIAllow = true
}
NSNotificationCenter.defaultCenter().postNotificationName("LabelHasbeenUpdated", object: nil)
if (shouldIAllow == true) {
NSLog("Location to Allowed")
// Start location services
locationManager.startUpdatingLocation()
} else {
NSLog("Denied access: \(locationStatus)")
}
}
}
And then in my ViewController.swift file I want to obtain the location coordinates. Here is the code:
ViewController.swift Code
func getCurrentWeatherData() -> Void {
let baseURL = NSURL(string: "https://api.forecast.io/forecast/\(apiKey)/")
var forecastURL: NSURL
var locName = "London"
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
appDelegate.foundLocation = false
if let loc = appDelegate.location {
println("Got Location!") // for debug purposes
var currentLat = loc.latitude
var currentLng = loc.longitude
forecastURL = NSURL(string: "\(currentLat),\(currentLng)", relativeToURL: baseURL)
locName = appDelegate.locationName!
} else {
println("No Location :(") // for debug purposes
var currentLat = "51.513445"
var currentLng = "-0.157828"
forecastURL = NSURL(string: "\(currentLat),\(currentLng)", relativeToURL: baseURL)
}
let sharedSession = NSURLSession.sharedSession()
let downloadTask: NSURLSessionDownloadTask = sharedSession.downloadTaskWithURL(forecastURL, completionHandler: { (location: NSURL!, response: NSURLResponse!, error: NSError!) -> Void in
var urlContents = NSString.stringWithContentsOfURL(location, encoding: NSUTF8StringEncoding, error: nil)
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)
dispatch_async(dispatch_get_main_queue(), {
() -> Void in
self.locationNameLabel.text = "\(locName)"
self.temperatureLabel.text = "\(currentWeather.temperature)"
self.iconView.image = currentWeather.icon!
self.currentTimeLabel.text = "At \(currentWeather.currentTime!) it is"
self.humidityLabel.text = "\(currentWeather.humidity)"
self.percipitationLabel.text = "\(currentWeather.percipProbability)"
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
self.refreshActivityIndicator.stopAnimating()
self.refreshActivityIndicator.hidden = true
self.refreshButton.hidden = false
})
}
})
downloadTask.resume()
}
The above is not working. My didUpdateLocations delegate never gets called. And in the debug console/output I always get No Location :( printed out, suggesting a failure in getting the location, more specifically suggesting that the location property on my AppDelegate is nil.
Things I have done to remedy this:
In the info.plist I have added the two keys NSLocationWhenInUseUsageDescription and NSLocationAlwaysUsageDescription
Ensured that I am connected via WiFi and not Ethernet
And countless other code tweaks, and still nothing.
A couple of observations:
As you point out, if you're going to call requestAlwaysAuthorization, then you must set NSLocationAlwaysUsageDescription. If you called requestWhenInUseAuthorization, you'd need NSLocationWhenInUseUsageDescription. (The fact that you see the confirmation dialog means that you've done this correctly. I assume you are seeing whatever description you supplied in the confirmation alert.)
On your simulator, you may not see location updates like on a device. Test this on an actual device.
When I used your code, I see didUpdateLocations when I called this from a device, but not from the simulator.
Once you solve the issue of not seeing didUpdateLocations being called, there is another issue:
You are posting a notification when the authorization status changes, but not when a location is received asynchronously (i.e. later). Frankly, the latter is the more critical event from the view controller's perspective, so I would have thought that (a) you should post a notification when the location is received; and (b) the view controller should observe this notification. Right now, even if you succeed in getting didUpdateLocations to be called, the view controller won't be notified of such.
Also, your didUpdateLocations is initiating yet another asynchronous process, the geocode of the coordinate. If your view controller needs that, too, you should post a notification inside the completion block of the geocoder.
Frankly, you haven't even shown us the view controller code that adds an observer for whatever notifications that this CLLocationManagerDelegate code will invoke, but I assume you have done that.
Just for the record: I first put the two keys (NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription) into the test-plist instead of the application-plist..Took me some time to realize.....