Retrieving the current location of the user/ retrieving the values of longitude and latitude watchkit/ios swift - ios

I want to obtain the current location (longitude and latitude) of the user, but I just get 0 for both of the variables. I do not know where the problem lies in. I am following this tutorial and have updated the infop.list of the app folder and the watch extension folder (NSLocationWhenInUseUsageDescription and NSLocationAlwaysUsageDescription), inserted the needed Watchconnector to both of the folders with cocoapods, and I also have added the Corelocation framework via link binary with libraries.
I have little knowledge about swift and watch os, I hope anyone can help.
Here is the project file: https://ufile.io/l5elkpsw
Thank you very much.
And here are my Appdelegate and Interfacecontroller file:
import UIKit
import CoreLocation
import MapKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
let locationManager:CLLocationManager = CLLocationManager()
var currentLocation = CLLocation()
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
WatchConnector.shared.activateSession()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestLocation()
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
return true
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if locations.count == 0
{
return
}
self.currentLocation = locations.first!
let message = ["lat":self.currentLocation.coordinate.latitude,"long":self.currentLocation.coordinate.longitude]
WatchConnector.shared.sendMessage(message, withIdentifier: "sendCurrentLocation") { (error) in
print("error in send message to watch\(error.localizedDescription)")
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Fail to load location")
print(error.localizedDescription)
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
import WatchKit
import Foundation
import CoreLocation
class InterfaceController: WKInterfaceController{
private let locationAccessUnauthorizedMessage = "Locations Disabled\n\nEnable locations for this app via the Settings in your iPhone to see meetups near you!"
private let pendingAccessMessage = "Grant location access to GPS dummy"
#IBOutlet weak var map: WKInterfaceMap!
#IBOutlet weak var button: WKInterfaceButton!
#IBOutlet weak var latitudeL: WKInterfaceLabel!
#IBOutlet weak var longitudeL: WKInterfaceLabel!
#IBOutlet weak var authorizeL: WKInterfaceLabel!
var currentLocation = CLLocation()
let locationManager = CLLocationManager()
var lat: Double = 0.0
var long: Double = 0.0
override func awake(withContext context: Any?) {
super.awake(withContext: context)
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
// Configure interface objects here.
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
WatchConnector.shared.listenToMessageBlock({ (message) in
self.lat = message["lat"] as! Double
self.long = message["long"] as! Double
print(self.lat)
print(self.long)
self.currentLocation = CLLocation(latitude: self.lat as! CLLocationDegrees, longitude: self.long as! CLLocationDegrees)
let mylocation : CLLocationCoordinate2D = CLLocationCoordinate2DMake(self.currentLocation.coordinate.latitude, self.currentLocation.coordinate.longitude)
let span = MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
let region = MKCoordinateRegion(center: mylocation, span: span)
self.map.setRegion(region)
self.map.addAnnotation(mylocation, with: .red)
}, withIdentifier: "sendCurrentLocation")
let authorizationStatus = CLLocationManager.authorizationStatus()
handleLocationServicesAuthorizationStatus(authorizationStatus)
}
func handleLocationServicesAuthorizationStatus(_ status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
print("handleLocationServicesAuthorizationStatus: .undetermined")
handleLocationServicesStateNotDetermined()
case .restricted, .denied:
print("handleLocationServicesAuthorizationStatus: .restricted, .denied")
handleLocationServicesStateUnavailable()
case .authorizedAlways, .authorizedWhenInUse:
print("handleLocationServicesAuthorizationStatus: .authorizedAlways, .authorizedWhenInUse")
handleLocationServicesStateAvailable(status)
}
}
func handleLocationServicesStateNotDetermined() {
authorizeL.setText(pendingAccessMessage)
locationManager.requestWhenInUseAuthorization()
}
func handleLocationServicesStateUnavailable() {
authorizeL.setText(locationAccessUnauthorizedMessage)
}
func handleLocationServicesStateAvailable(_ status: CLAuthorizationStatus) {
switch status {
case .authorizedAlways:
locationManager.startUpdatingLocation()
case .authorizedWhenInUse:
locationManager.requestLocation()
default:
break
}
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
#IBAction func btnPressed() {
self.latitudeL.setText("\(self.lat)")
self.longitudeL.setText("\(self.long)")
print("\(locationManager.requestLocation())")
print("\(self.lat)")
print("\(self.long)")
}
}
extension InterfaceController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("error:: \(error.localizedDescription)")
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
locationManager.requestLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if locations.first != nil {
print("location:: (location)")
}
}
}

Are you testing on a simulator? If so, you might need to set the location to something in the Debug/Location menu:

Related

Location Service as a Singleton in Swift, get stuck on "When In Use"

I'm programming an app that needs "Always location" and I decided to use a Singleton to keep tracking active since I need most of the time the location services even in the background.
When I run the application on my iPhone, the console says that the location service is in "When In Use" mode and my protocol don't get the location updates from the LocationManager.
What's wrong with my Singleton (I'm a Swift newbie please be clear in your answers.
Is it a good idea to use a Singleton for Location Services ?
LocationService.swift (UPDATED)
import Foundation
import CoreLocation
protocol LocationServiceDelegate {
func onLocationUpdate(location: CLLocation)
func onLocationDidFailWithError(error: Error)
}
class LocationService: NSObject, CLLocationManagerDelegate {
public static let shared = LocationService()
var delegate: LocationServiceDelegate?
var locationManager: CLLocationManager!
var currentLocation: CLLocation!
private override init() {
super.init()
self.initializeLocationServices()
}
func initializeLocationServices() {
self.locationManager = CLLocationManager()
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestAlwaysAuthorization()
self.locationManager.pausesLocationUpdatesAutomatically = false
self.locationManager.delegate = self
self.locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .restricted:
print("Location access was restricted.")
case .denied:
print("User denied access to location.")
case .notDetermined:
self.locationManager.requestAlwaysAuthorization()
case .authorizedAlways: fallthrough
case .authorizedWhenInUse:
print("User choosed locatiom when app is in use.")
default:
print("Unhandled error occured.")
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
self.currentLocation = locations.last!
locationChanged(location: currentLocation)
}
private func locationChanged(location: CLLocation) {
guard let delegate = self.delegate else {
return
}
delegate.onLocationUpdate(location: location)
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
self.locationManager.stopUpdatingLocation()
locationFailed(error: error)
}
private func locationFailed(error: Error) {
guard let delegate = self.delegate else {
return
}
delegate.onLocationDidFailWithError(error: error)
}
}
Then I initialize the singleton :
AppDelegate.swift
let locationService = LocationService.shared
Then my View conforms to my protocol :
ViewController.swift
extension ViewController: LocationServiceDelegate {
func onLocationUpdate(location: CLLocation) {
print("Current Location : \(location)")
}
func onLocationDidFailWithError(error: Error) {
print("Error while trying to update device location : \(error)")
}
}
Yes, You can use singleton for your purpose. Few things you can check with your implementation:
locationManager.pausesLocationUpdatesAutomatically = false.
enable background modes for location updates.
Switch to significant location updates when the app moves to background.
Is it a better way to send notifications to all viewControllers to pass the CLLocation object or its better to conform to my protocol in every controllers ?

Send Location of User in Background Swift

I am building an app where the user clicks a button and for 60mins (or any amount of time) we keep track of them by uploading their location to a server. Currently we are using 'Did Update Locations' function to send the users location to firebase in real-time.
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
}
This system works but it spams the server sending the location of the user to the server once every second.
This is too much data and we would only need to send the users location to the server once every 10-30 seconds.
What can we do send the users location once every 10-30 seconds?
class ViewController: UIViewController, CLLocationManagerDelegate {
private var locman = CLLocationManager()
private var startTime: Date? //An instance variable, will be used as a previous location time.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let loc = locations.last else { return }
let time = loc.timestamp
guard var startTime = startTime else {
self.startTime = time // Saving time of first location, so we could use it to compare later with second location time.
return //Returning from this function, as at this moment we don't have second location.
}
let elapsed = time.timeIntervalSince(startTime) // Calculating time interval between first and second (previously saved) locations timestamps.
if elapsed > 30 { //If time interval is more than 30 seconds
print("Upload updated location to server")
updateUser(location: loc) //user function which uploads user location or coordinate to server.
startTime = time //Changing our timestamp of previous location to timestamp of location we already uploaded.
}
}
import UIKit
import CoreLocation
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate,CLLocationManagerDelegate {
var window: UIWindow?
var locationManager = CLLocationManager()
var backgroundUpdateTask: UIBackgroundTaskIdentifier!
var bgtimer = Timer()
var latitude: Double = 0.0
var longitude: Double = 0.0
var current_time = NSDate().timeIntervalSince1970
var timer = Timer()
var f = 0
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
self.doBackgroundTask()
return true
}
func applicationWillResignActive(_ application: UIApplication) {
}
func applicationWillEnterForeground(_ application: UIApplication) {
print("Entering foreBackground")
}
func applicationDidBecomeActive(_ application: UIApplication) {
}
func applicationWillTerminate(_ application: UIApplication) {
}
func applicationDidEnterBackground(_ application: UIApplication) {
print("Entering Background")
// self.doBackgroundTask()
}
func doBackgroundTask() {
DispatchQueue.main.async {
self.beginBackgroundUpdateTask()
self.StartupdateLocation()
self.bgtimer = Timer.scheduledTimer(timeInterval:-1, target: self, selector: #selector(AppDelegate.bgtimer(_:)), userInfo: nil, repeats: true)
RunLoop.current.add(self.bgtimer, forMode: RunLoopMode.defaultRunLoopMode)
RunLoop.current.run()
self.endBackgroundUpdateTask()
}
}
func beginBackgroundUpdateTask() {
self.backgroundUpdateTask = UIApplication.shared.beginBackgroundTask(expirationHandler: {
self.endBackgroundUpdateTask()
})
}
func endBackgroundUpdateTask() {
UIApplication.shared.endBackgroundTask(self.backgroundUpdateTask)
self.backgroundUpdateTask = UIBackgroundTaskInvalid
}
func StartupdateLocation() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.requestAlwaysAuthorization()
locationManager.allowsBackgroundLocationUpdates = true
locationManager.pausesLocationUpdatesAutomatically = false
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Error while requesting new coordinates")
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let locValue:CLLocationCoordinate2D = manager.location!.coordinate
self.latitude = locValue.latitude
self.longitude = locValue.longitude
f+=1
print("New Coordinates: \(f) ")
print(self.latitude)
print(self.longitude)
}
#objc func bgtimer(_ timer:Timer!){
sleep(2)
/* if UIApplication.shared.applicationState == .active {
timer.invalidate()
}*/
self.updateLocation()
}
func updateLocation() {
self.locationManager.startUpdatingLocation()
self.locationManager.stopUpdatingLocation()
}}
I added the sleep function to delay of calling the location and send the information to server
Since this is running in both the app is active and goes to background. If you want only background process, remove or comment the function self.doBackgroundTask() from didFinishLaunchingWithOptions and remove the comment for self.doBackgroundTask() in the applicationdidEnterBackground. And then remove the comment in the function bgtimer(), since the background process has to stop once the app comes to active state.
Apps normally get suspended (no longer get CPU time) a moment after being moved to the background. You can ask for extra background time, but the system only gives you 3 minutes.
Only a very limited class of apps are allowed to run in the background for longer than that. Mapping/GPS applications are one of those categories. However, your app is not a mapping/GPS application, so I doubt if Apple would approve it.
Bottom line: I think you might be out of luck running your location queries for more than 3 minutes.
EDIT:
As Paulw11 points out, you can use the significant location change service to get location updates when the device moves by large distances.

didUpdateLocations never called

I'm trying to get the user's location. To do so I have set the following property in the info.plist :
I have also added the following code in my viewDidLoad method as well as the function below. The problem is that the locationManager(manager, didUpdate....) function never gets called, I also never get prompted for permission to access location, even though I have removed and installed the app again. I am testing this on my iPad, not on the simulator. The didFailWithError function never gets called either.
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("UPDATING")
let locValue:CLLocationCoordinate2D = manager.location!.coordinate
let latitude = locValue.latitude
let longitude = locValue.longitude
latitudeText = "\(latitude)"
longitudeText = "\(longitude)"
if let a = latitudeText, b = longitudeText {
print(a)
print(b)
self.locationManager.stopUpdatingLocation()
if (userAlreadyExist()) {
dispatch_async(dispatch_get_main_queue(), {
//self.performSegueWithIdentifier("segueWhenLoggedIn", sender: self)
self.performSegueWithIdentifier("showCamera", sender: self)
// self.performSegueWithIdentifier("showTabBarController", sender: self)
})
}
else {
dispatch_async(dispatch_get_main_queue(), {
self.performSegueWithIdentifier("segueWhenLoggedOut", sender: self)
})
}
}
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print(error.localizedDescription)
}
EDIT :
I have added the following snippet of code :
if CLLocationManager.locationServicesEnabled() {
print("yes")
}
else {
print("no")
}
it returns yes. I have also checked on my device, locationServices are enabled, the app is listed there, however all the other apps have "While Using", "Never" or "Always" written next to them, mine doesn't have anything written.
where do you start the location update ? for example:
//location manager
lazy var locationManager: CLLocationManager = {
var _locationManager = CLLocationManager()
_locationManager.delegate = self
_locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
_locationManager.activityType = . automotiveNavigation
_locationManager.distanceFilter = 10.0 // Movement threshold for new events
_locationManager.allowsBackgroundLocationUpdates = true // allow in background
return _locationManager
}()
if CLLocationManager.locationServicesEnabled() {
locationManager.startUpdatingLocation() // start location manager
}
here is a working conroller code:
also important to to set up Custom iOS Target Properties.
Add these two lines to the Info.plist:
NSLocationWhenInUseUsageDescription
NSLocationAlwaysUsageDescription
//
// ViewController.swift
// LocationTest2
import UIKit
import CoreLocation
class ViewController: UIViewController {
//location manager
lazy var locationManager: CLLocationManager = {
var _locationManager = CLLocationManager()
_locationManager.delegate = self
_locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
_locationManager.activityType = . automotiveNavigation
_locationManager.distanceFilter = 10.0 // Movement threshold for new events
// _locationManager.allowsBackgroundLocationUpdates = true // allow in background
return _locationManager
}()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
//allow location use
locationManager.requestAlwaysAuthorization()
print("did load")
print(locationManager)
//get current user location for startup
// if CLLocationManager.locationServicesEnabled() {
locationManager.startUpdatingLocation()
// }
}
}
// MARK: - CLLocationManagerDelegate
extension ViewController: CLLocationManagerDelegate {
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
for location in locations {
print("**********************")
print("Long \(location.coordinate.longitude)")
print("Lati \(location.coordinate.latitude)")
print("Alt \(location.altitude)")
print("Sped \(location.speed)")
print("Accu \(location.horizontalAccuracy)")
print("**********************")
}
}
}
for me worked:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("locationManager update")
}
instead of this
private func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("locationManager update")
}
Also make sure you have set the custom location to simulator as by default it will be None ...
In the simulator go to Debug -> Location-> .
It should also be noted that locationManager:didFailWithError: will run if the location is not set in the simulator, as you'd expect.
A very subtle cause for this bug in Swift code. Don't define the delegate call didUpdateLocations as private or fileprivate. The location manager won't be able to find or call it if you do.
Good:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
}
Bad:
fileprivate func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
}
I looked for answers high and low. This is the only thing that worked for me:
Change the func for didUpdateToLocation to:
func locationManager(_: CLLocationManager, didUpdateToLocation newLocation: CLLocation!,fromLocation oldLocation: CLLocation!) {
}
Note the subtle change of "as _: CLLocationManager", instead of "manager: CLLocationManager".
You should call startUpdatingLocation() inside the didDetermineState delegate method
if CLLocationManager.authorizationStatus() != .authorizedWhenInUse {
locationManager.requestWhenInUseAuthorization()
}else{
locationManager.startUpdatingLocation()
}
//later
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case .authorizedWhenInUse:
manager.startUpdatingLocation()
break
case .authorizedAlways:
manager.startUpdatingLocation()
break
case .denied:
//handle denied
break
case .notDetermined:
manager.requestWhenInUseAuthorization()
break
default:
break
}
}
I was using AppDelegate as CLLocationManagerDelegate instead of ViewController like in most examples, this was the code I had
// AppDelegate.swift
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var locationManager: CLLocationManager?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
...
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = rootViewController
self.window?.makeKeyAndVisible()
self.locationManager = CLLocationManager()
self.locationManager?.delegate = self
return true
}
}
and I was trying to get updates when exit event was called
// AppDelegate.swift
extension AppDelegate: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
if !CLLocationManager.locationServicesEnabled() {
return
}
let locationManager = CLLocationManager()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = kCLDistanceFilterNone
locationManager.allowsBackgroundLocationUpdates = true
locationManager.startUpdatingLocation()
return
}
}
But this didn't work. Instead I had to move the configuration of locationManager to AppDelegate class, and only start the updates in AppDelegate extension.
Like this
// AppDelegate.swift
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var locationManager: CLLocationManager?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
...
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = rootViewController
self.window?.makeKeyAndVisible()
self.locationManager = CLLocationManager()
self.locationManager?.delegate = self
/* This was the catch, it needs to be here instead of inside extension */
self.locationManager?.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager?.distanceFilter = kCLDistanceFilterNone
self.locationManager?.allowsBackgroundLocationUpdates = true
/* */
return true
}
}
extension AppDelegate: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
if !CLLocationManager.locationServicesEnabled() {
return
}
/* Only do this here */
manager.startUpdatingLocation()
return
}
}
Then it started working, and sending continuous location updates. Also I tested on simulator.
Make sure the startUpdatingLocation method gets called on the main thread, like so:
DispatchQueue.main.async { [weak self] in
self?.locationManager.startUpdatingLocation()
}

mapView.showsUserLocation = true affects the correctness of gps in my swift app. Why?

In my app I want to know user's real location. I want to show it in couple places as a plain text (as a string of longitude/latitude) and also show it on the map. I have several ui view controllers and one of them contains - now, for testing - a print to the console with current longitude and latitude, it also contains a map view.
I'm fetching the current gps position from my app delegate and put it on the screen.
In my AppDelegate.swift class I have:
var locationManager: CLLocationManager! = nil
var location: CLLocation! = nil
var longitude : Double = 0
var latitude : Double = 0
var location_fixed = false
func application(application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
initLocationManager()
}
func initLocationManager() {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
if CLLocationManager.authorizationStatus() == .AuthorizedAlways {
locationManager!.startUpdatingLocation()
} else {
locationManager!.requestAlwaysAuthorization()
}
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("Failed to initialize GPS: ", error.description)
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
location = locations.first!
print("Managed to get a fix on location")
let coord = location.coordinate
longitude = coord.longitude
latitude = coord.latitude
location_fixed = true;
locationManager.stopUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case .NotDetermined:
print("NotDetermined")
case .Restricted:
print("Restricted")
case .Denied:
print("Denied")
case .AuthorizedAlways:
print("AuthorizedAlways")
locationManager!.startUpdatingLocation()
case .AuthorizedWhenInUse:
print("AuthorizedWhenInUse")
locationManager!.startUpdatingLocation()
}
}
func stopGPS() -> Void
{
print("STOP GPS")
location_fixed = false;
locationManager.stopUpdatingLocation()
}
func startGPS() -> Void
{
locationManager.startUpdatingLocation()
}
func isLocationFixed() -> Bool
{
return self.location_fixed
}
func getLongitude() -> Double
{
return self.longitude
}
func getLatitude() -> Double
{
return self.latitude
}
func getLocation() -> CLLocation
{
return self.location
}
Now in my UIViewController in viewWillAppear method I added asynchronous call to the app delegate methods for checking current gps position:
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0)) {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.startGPS()
while (!appDelegate.isLocationFixed())
{
sleep(1)
}
dispatch_async(dispatch_get_main_queue()) {
self.fetchRequests(self.radius, lat: appDelegate.getLatitude(), lon: appDelegate.getLongitude())
print("gps here: \(appDelegate.getLatitude()), \(appDelegate.getLongitude())") }
}
and now when I enter this panel and waits for fetching the gps - I see the correct data. So far so good. Now, when I change the gps position in a simulator, then go to some other panel and come back to this one - I see the new data, which is fine.
And now comes the real weird problem. In the same panel I have a map view.
It's embedded in a UIViewController that I put in a container, so I cannot access it from my async call. It has a viewDidLoad() method and when I put there:
mapView.showsUserLocation = true
and run the app, I see the blue dot exactly when the simulator's gps points out, but now my data in the previous async call is not updating correctly.
When I comment out that one particular line of code, I'm getting the correct data shown as gps here:. When I leave it as a part of the code, then this line gps here: shows the same data all the time, even though I'm changing the gps position in a simulator, leaving and entering the panel several times. What's up with that?

Getting CLBeacon's information from AppDelegate to View Controller in Swift

I'm new to swift and have gone around and around trying to figure it out. I know I have it over complicated if I had to guess but need some help.
I'm trying to use iBeacon to read the UUID, Major and Minor values off of a beacon then use that to drive an image in the view controller.
In the AppDelegate.swift file I am able to get the information and use println to get it out. The AppDelegate file is the following:
import UIKit
import CoreLocation
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var locationManager: CLLocationManager?
var lastProximity: CLProximity?
var lastUUID: NSUUID!
var lastBeacanIdentifier:String = ""
var lastMajorValue: NSNumber = 0.0
var lastMinorValue: NSNumber = 0.0
func application(application: UIApplication,
didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
let uuidString = "99C2E498-7606-4575-A353-5F710834E75B"
let beaconIdentifier = "co.Company"
let beaconUUID:NSUUID = NSUUID(UUIDString: uuidString)
let beaconRegion:CLBeaconRegion = CLBeaconRegion(proximityUUID: beaconUUID, identifier: beaconIdentifier)
locationManager = CLLocationManager()
if(locationManager!.respondsToSelector("requestAlwaysAuthorization")) {
locationManager!.requestAlwaysAuthorization()
}
locationManager!.delegate = self
locationManager!.pausesLocationUpdatesAutomatically = false
locationManager!.startMonitoringForRegion(beaconRegion)
locationManager!.startRangingBeaconsInRegion(beaconRegion)
locationManager!.startUpdatingLocation()
if(application.respondsToSelector("registerUserNotificationSettings:")) {
application.registerUserNotificationSettings(
UIUserNotificationSettings(
forTypes: UIUserNotificationType.Alert | UIUserNotificationType.Sound,
categories: nil
)
)
}
return true
}
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
extension AppDelegate: CLLocationManagerDelegate {
func sendLocalNotificationWithMessage(message: String!) {
let notification:UILocalNotification = UILocalNotification()
notification.alertBody = message
UIApplication.sharedApplication().scheduleLocalNotification(notification)
}
func locationManager(manager: CLLocationManager!,
didRangeBeacons beacons: [AnyObject]!,
inRegion region: CLBeaconRegion!) {
NSLog("didRangeBeacons");
var message:String = ""
if(beacons.count > 0) {
let nearestBeacon:CLBeacon = beacons[0] as CLBeacon
if(nearestBeacon.proximity == lastProximity ||
nearestBeacon.proximity == CLProximity.Unknown) {
return;
}
lastProximity = nearestBeacon.proximity;
lastMajorValue = nearestBeacon.major;
lastMinorValue = nearestBeacon.minor;
lastUUID = nearestBeacon.proximityUUID;
switch nearestBeacon.proximity {
case CLProximity.Far:
message = "You are far away from the beacon";
println(lastMajorValue)
println(lastMinorValue)
println(lastUUID)
case CLProximity.Near:
message = "You are near the beacon";
println(lastMajorValue)
println(lastMinorValue)
println(lastUUID)
case CLProximity.Immediate:
message = "You are in the immediate proximity of the beacon";
println(lastMajorValue)
println(lastMinorValue)
println(lastUUID)
case CLProximity.Unknown:
return
}
} else {
message = "No beacons are nearby"
}
NSLog("%#", message)
sendLocalNotificationWithMessage(message)
}
func locationManager(manager: CLLocationManager!,
didEnterRegion region: CLRegion!) {
manager.startRangingBeaconsInRegion(region as CLBeaconRegion)
manager.startUpdatingLocation()
NSLog("You entered the region")
sendLocalNotificationWithMessage("You entered the region")
}
func locationManager(manager: CLLocationManager!,
didExitRegion region: CLRegion!) {
manager.stopRangingBeaconsInRegion(region as CLBeaconRegion)
manager.stopUpdatingLocation()
NSLog("You exited the region")
sendLocalNotificationWithMessage("You exited the region")
}
}
The View Controller file is the following:
import Foundation
import UIKit
import CoreLocation
class ViewController: UIViewController{
#IBOutlet weak var advertismentImageArea: UIImageView!
#IBAction func closeAdvertisementButton(sender: UIButton) {
advertismentImageArea.hidden = true
}
var beaconInformation: AppDelegate!
override func viewDidLoad() {
super.viewDidLoad()
var closestBeacon = beaconInformation
var majorNumber = closestBeacon.lastMajorValue
if majorNumber == 6303 {
advertismentImageArea.image = UIImage(named: "AdOne")
} else if majorNumber == 21456 {
advertismentImageArea.image = UIImage(named: "AdTwo")
} else {
return advertismentImageArea.hidden = true;
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I would really appreciate any help you all can provide.
One possible solution would be to use the iOS Delegation pattern. It's not the only way to solve this problem.
What you would do would be to create a Protocol in your AppDelegate class
#protocol BeaconLocationDelegate
{
func majorBeaconChanged(majorValue:NSNumber)
}
You would then also add the following variable to your AppDelegate class
weak var locationDelegate: BeaconLocationDelegate?
Inside your ViewController class you would then declare that your class implements the BeaconLocationDelegate protocol
class ViewController: UIViewController, BeaconLocationDelegate
and then somewhere in viewDidload add the following line of code
UIApplication.sharedApplication().delegate?.locationDelegate = self
and then implement the protocol method to update the UI however you want for your app
func majorBeaconChanged(majorValue:NSNumber)
And lastly, inside your AppDelegate class whenever you detect the change in the beacon you would then invoke code like:
locationDelegate?.majorBeaconChanged(newMajorValue)

Resources