Apple Docs suggest not to store your CLLocationManager in a local variable. So I created a global constant outside the scope of my ViewController class just after the import statements. Trying to access the constant inside the class, however, throws compiler errors:
A similarly declared globalDictionaryconstant of type NSMutableDictionary seems to be accessible inside the class.
What is it that I am doing wrong here? Why does the above not work?
Code:
//
// ViewController.swift
// CoreLocationExample
//
//
import UIKit
import CoreLocation
import Foundation
let locationManager = CLLocationManager()
let globalDictionary = NSMutableDictionary()
class ViewController: UIViewController, CLLocationManagerDelegate {
// let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
let dict = [
"name":"John"
]
globalDictionary.addEntries(from: dict)
print(globalDictionary)
}
}
Using Swift 3 on Xcode Version 8.3.1
Apple Docs suggest not to store your CLLocationManager in a local variable.
It means do not create local instance inside a method / function.
please declare like this.
class AppDelegate: UIResponder, UIApplicationDelegate {
//CLLocation Manager
let locationManager = CLLocationManager()
var locValue = CLLocationCoordinate2D()
}
CLLocation Manager or globalDictionary Inside the class.
use singleton class for this
import UIKit
import CoreLocation
public class LocationManager: NSObject,CLLocationManagerDelegate {
public static let sharedInstance = LocationManager()
public lazy var locationManager: CLLocationManager = CLLocationManager()
var globalDictionary = NSMutableDictionary()
public override init() {
super.init()
self.locationManager.requestAlwaysAuthorization()
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.distanceFilter = kCLDistanceFilterNone;
self.locationManager.startUpdatingLocation()
self.locationManager.delegate = self
let dict = [
"name":"John"
]
self.globalDictionary.addEntries(from: dict)
print(self.globalDictionary)
}
// MARK: - Location Manager Delegate
public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error)
}
public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
let newLocation: CLLocation? = locations.last
let locationAge: TimeInterval? = -(newLocation?.timestamp.timeIntervalSinceNow)!
if Double(locationAge!) > 5.0 {
return
}
if Double((newLocation?.horizontalAccuracy)!) < 0 {
return
}
}
public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
}
}
You can do it in the following way -
class LocationTracker {
static var locationManager: CLLocationManager? = LocationTracker.sharedLocationManager()
class func sharedLocationManager() -> CLLocationManager {
let lockQueue = DispatchQueue(label: "self")
lockQueue.sync {
if _locationManager == nil {
_locationManager = CLLocationManager()
_locationManager?.desiredAccuracy = kCLLocationAccuracyBestForNavigation
_locationManager?.allowsBackgroundLocationUpdates = true
_locationManager?.pausesLocationUpdatesAutomatically = false
}
}
return _locationManager!
}
}
And when you need to call it in your code, you can do the below -
var locationManager: CLLocationManager? = LocationTracker.sharedLocationManager()
you can then add other Location related methods in this class as well like for startt tracking, update location, stoptracking etc.
Related
I need to get the zipCode and the city in multiple viewControllers.
Here is how I'm currently doing it...
import CoreLocation
let locationManager = CLLocationManager()
class MyViewController: UIViewController, CLLocationManagerDelegate{
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: {(placemarks, error)-> Void in
if error != nil {
//AlertView to show the ERROR message
}
if placemarks!.count > 0 {
let placemark = placemarks![0]
self.locationManager.stopUpdatingLocation()
let zipCode = placemark.postalCode ?? ""
let city:String = placemark.locality ?? ""
// Do something with zipCode
// Do something with city
}else{
print("No placemarks found.")
}
})
}
func someFunction() {
locationManager.startUpdatingLocation()
}
Everything works fine but as you can see doing it this way in multiple viewController leads to a lot of code repetition (of course, I'm not showing the whole code).
What would be the most common way to retrieve the zipCode and city from CLLocationManager() in a more practical way from multiple viewControllers?
What I'm thinking is something like...
MyLocationManager.zipCode() // returns zipCode as a string
MyLocationManager.city() // returns city as a string
The usual thing is to have just one location manager in one persistent place that you can always get to from anywhere, like the app delegate or the root view controller.
I tried to implement a singleton CLLocationManager class, I think you can modify the following class to implement some additional methods.
import Foundation
class LocationSingleton: NSObject, CLLocationManagerDelegate {
private let locationManager = CLLocationManager()
private var latitude = 0.0
private var longitude = 0.0
static let shared = LocationSingleton()
private override init() {
super.init()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLLocationAccuracyHundredMeters
locationManager.requestAlwaysAuthorization() // you might replace this with whenInuse
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
latitude = location.coordinate.latitude
longitude = location.coordinate.longitude
}
}
private func getLatitude() -> CLLocationDegrees {
return latitude
}
private func getLongitude() -> CLLocationDegrees {
return longitude
}
private func zipCode() {
// I think you can figure way out to implemet this method
}
private func city() {
// I think you can figure way out to implemet this method
}
}
I tried to use CoreLocation with Swift to track the user's location:
(Below you can find the code of may ViewController.swift file.)
But the code doesn't seems working as I've expected, because I'm still getting the same error every-time I'm launching the application:
I'm sure this is the problem why I'm not able to get a result from the locationManager() function that should print out the current location.
It says "Cannot assign value of type 'ViewController' to type 'CLLocationManagerDelegate?'"
import UIKit
import CoreLocation
class ViewController: UIViewController, WKUIDelegate {
override func viewDidLoad() {
super.viewDidLoad()
if CLLocationManager.locationServicesEnabled() {
print("CLLocationManager is available")
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
locationManager.delegate = self
locationManager.startUpdatingLocation()
}
}
let locationManager = CLLocationManager()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
print(location.coordinate)
}
}
}
Does anyone now how to fix this problem? - Any help would be very appreciated, thanks a million in advance.
You simply need to declare conformance to CLLocationManagerDelegate. You can either do this directly in the class declaration just as you did with WKUIDelegate or in an extension of ViewController.
class ViewController: UIViewController, WKUIDelegate, CLLocationManagerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
if CLLocationManager.locationServicesEnabled() {
print("CLLocationManager is available")
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
locationManager.delegate = self
locationManager.startUpdatingLocation()
}
}
let locationManager = CLLocationManager()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
print(location.coordinate)
}
}
}
With extension:
class ViewController: UIViewController, WKUIDelegate {
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
if CLLocationManager.locationServicesEnabled() {
print("CLLocationManager is available")
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
locationManager.delegate = self
locationManager.startUpdatingLocation()
}
}
}
extension ViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
print(location.coordinate)
}
}
}
I use CLLocationManager to request the user's location. However, if they are outside of New York City, I want to default to certain coordinates. Is there a way to check if they are in a certain city?
import UIKit
import CoreLocation
import GoogleMaps
private let kDefaultLatitude: Double = 40.713
private let kDefaultLongitude: Double = -74.000
private let kDefaultZoomLevel: Float = 16.0
class RootMapViewController: UIViewController {
#IBOutlet weak var mapView: GMSMapView!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
fetchLocation()
}
private func fetchLocation() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}
}
// MARK: CLLocationManagerDelegate
extension RootMapViewController: CLLocationManagerDelegate {
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locationManager.stopUpdatingLocation()
let userCoordinates = locations[0].coordinate
// How do I check if the user is in NYC?
// if user is in nyc
centerMapOn(userCoordinates)
mapView.myLocationEnabled = true
mapView.settings.myLocationButton = true
// else default to Times Square
}
}
You can use reverse geocoding. For example you can place:
geocoder:CLGeocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(locations[0],completionHandler{
if error == nil && placemarks.count > 0 {
let location = placemarks[0] as CLPlacemark
print(location.locality)
})
in func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
I was trying to create simple shared instance of CLLocationManager for my app.
There was no problem creating shared instance:
import UIKit
import CoreLocation
protocol LocationHandlerDelegate: class {
func locationHandlerDidUpdateLocation(location: CLLocation?)
func locationHandlerDidFailWithError(error: NSError)
}
class LocationHandler: NSObject, CLLocationManagerDelegate {
var locationManager: CLLocationManager!
var location: CLLocation?
weak var delegate: LocationHandlerDelegate?
static let sharedInstance = LocationHandler()
override init() {
super.init()
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
if CLLocationManager.authorizationStatus() == .NotDetermined {
locationManager.requestWhenInUseAuthorization()
}
}
// MARK: -
// MARK: - CLLocationManagerDelegate functions
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
delegate?.locationHandlerDidUpdateLocation(locations.last)
self.location = locations.last
locationManager.stopUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
delegate?.locationHandlerDidFailWithError(error)
locationManager.stopUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if status == .AuthorizedAlways || status == .AuthorizedWhenInUse {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer
}
}
}
But for some reason when I call my shared instance from another class:
class TodayViewController: UIViewController, CLLocationManagerDelegate, LocationHandlerDelegate {
super.viewDidLoad() {
LocationHandler.sharedInstance.delegate = self
if #available(iOS 9.0, *) {
LocationHandler.sharedInstance.locationManager.requestLocation()
} else {
LocationHandler.sharedInstance.locationManager.startUpdatingLocation()
}
}
...
// MARK: - LocationHandlerDelegate function
func locationHandlerDidUpdateLocation(location: CLLocation?) {
if let location = location {
print("Current lcoation: \(location)")
}
else {
// ...
}
}
func locationHandlerDidFailWithError(error: NSError) {
print("Error finding lcoation: \(error.localizedDescription)")
}
}
None of CLLocationManagerDelegate functions in my LocationHandler are called.
But if in TodayViewController I write
LocationHandler.sharedInstance.locationManager.delegate = self
instead of
LocationHandler.sharedInstance.delegate = self
and I implement CLLocationManagerDelegate functions in my TodayViewController instead of LocationHandler then those delegate functions are called. So I guess there may be some problem with instancing? Or am I missing something else?
I am creating an iOS Framework and i want to use Core Location to interact with Beacons. For testing reasons i am trying to get user location.
This is the class i created in the framework.
import Foundation
import CoreLocation
public class BeaconManager:NSObject,CLLocationManagerDelegate{
var locationManager:CLLocationManager = CLLocationManager()
public override init() {
super.init()
locationManager.requestAlwaysAuthorization()
locationManager.delegate = self
locationManager.startUpdatingLocation()
}
public func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
if let location = locations.first as? CLLocation {
println(location)
}
}
}
And i am calling it from a test app that has the framework like this
import UIKit
import OtravitaSDK
import CoreLocation
class ViewController: UIViewController {
var bm = BeaconManager()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
But is not working , is not printing the location. I have set the NSLocationAlwaysUsageDescription both in framework's info.plist and the app's info.plist
you can add your decription in NSLocationAlwaysUsageDescription & NSLocationWhenInUseUsageDescription in plist
This code put into AppDelegate file
var locationManager:CLLocationManager?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
//You can give this permission for fetch current location
var type = UIUserNotificationType.Badge | UIUserNotificationType.Alert | UIUserNotificationType.Sound;
var setting = UIUserNotificationSettings(forTypes: type, categories: nil);
UIApplication.sharedApplication().registerUserNotificationSettings(setting);
UIApplication.sharedApplication().registerForRemoteNotifications();
locationManager = CLLocationManager()
locationManager?.requestAlwaysAuthorization()
locationManager?.delegate = self
locationManager?.startUpdatingLocation()
// Override point for customization after application launch.
return true
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
if let location = locations.first as? CLLocation {
println(location)
}
}
What you need to do as of iOS 8 is configure your Info.plist file to cater for 2 kinds of location behaviour. You need to supply a default message that appears with a popup by default, asking the user for consent to use their location.
NSLocationWhenInUseUsageDescription and NSLocationAlwaysUsageDescription
See this article for a full walkthrough and another SO post which discusses this topic. Hope this helps!
You can create at first the reporter class (with shared instance) which will implement the CLLocationManagerDelegate, so you could implement your logic inside delegate methods
import Foundation
import CoreLocation
class LocationReporter: NSObject, CLLocationManagerDelegate {
static let sharedInstance = LocationReporter()
func startUpdating(locationManager: CLLocationManager) {
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
func stopUpdating(locationManager: CLLocationManager) {
locationManager.stopUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
print("latitude: ", location.coordinate.latitude)
print("longitude: ", location.coordinate.longitude)
}
}
//implement other locationManger delegate methods
}
Next you can create a Client class
import Foundation
import CoreLocation
class LocationDetectionClient {
private let locationManager = CLLocationManager()
func start() {
LocationReporter.sharedInstance.startUpdating(locationManager: locationManager)
}
func stop() {
LocationReporter.sharedInstance.stopUpdating(locationManager: locationManager)
}
}
And finally call the Client methods where you need
let locationDetectionClient = LocationDetectionClient()
public func startLocationDetection() {
locationDetectionClient.start()
}
public func stopLocationDetection() {
locationDetectionClient.stop()
}
Hope this would help