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

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)

Related

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

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:

Updating the label with current heart rate (swift and watch kit)

When you click on the button the current heart rate should be captured and the label should be updated. But, I do not understand why the label won't get updated with the heart rate value. I do not know what is wrong here.
I am still a very beginner in terms of swift and watch kit. I hope anyone can help me out.
Thank you very much.
Below you can see the code. I have also added the authorization part into the AppDelegate.swift file.
import WatchKit
import Foundation
import HealthKit
class InterfaceController: WKInterfaceController {
#IBOutlet weak var label: WKInterfaceLabel!
#IBOutlet weak var button: WKInterfaceButton!
var isRecording = false
//For workout session
let healthStore = HKHealthStore()
var session: HKWorkoutSession?
var currentQuery: HKQuery?
override func awake(withContext context: Any?) {
super.awake(withContext: context)
// Configure interface objects here.
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
//Check HealthStore
guard HKHealthStore.isHealthDataAvailable() == true else {
print("Health Data Not Avaliable")
return
}
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
#IBAction func btnPressed() {
if(!isRecording){
let stopTitle = NSMutableAttributedString(string: "Stop Recording")
stopTitle.setAttributes([NSAttributedString.Key.foregroundColor: UIColor.red], range: NSMakeRange(0, stopTitle.length))
button.setAttributedTitle(stopTitle)
isRecording = true
startWorkout() //Start workout session/healthkit streaming
}else{
let exitTitle = NSMutableAttributedString(string: "Start Recording")
exitTitle.setAttributes([NSAttributedString.Key.foregroundColor: UIColor.green], range: NSMakeRange(0, exitTitle.length))
button.setAttributedTitle(exitTitle)
isRecording = false
healthStore.end(session!)
label.setText("Heart Rate")
}
}
}
extension InterfaceController: HKWorkoutSessionDelegate{
func workoutSession(_ workoutSession: HKWorkoutSession, didChangeTo toState: HKWorkoutSessionState, from fromState: HKWorkoutSessionState, date: Date) {
switch toState {
case .running:
print(date)
if let query = heartRateQuery(date){
self.currentQuery = query
healthStore.execute(query)
}
//Execute Query
case .ended:
//Stop Query
healthStore.stop(self.currentQuery!)
session = nil
default:
print("Unexpected state: \(toState)")
}
}
func workoutSession(_ workoutSession: HKWorkoutSession, didFailWithError error: Error) {
//Do Nothing
}
func startWorkout(){
// If a workout has already been started, do nothing.
if (session != nil) {
return
}
// Configure the workout session.
let workoutConfiguration = HKWorkoutConfiguration()
workoutConfiguration.activityType = .running
workoutConfiguration.locationType = .outdoor
do {
session = try HKWorkoutSession(configuration: workoutConfiguration)
session?.delegate = self
} catch {
fatalError("Unable to create workout session")
}
healthStore.start(self.session!)
print("Start Workout Session")
}
func heartRateQuery(_ startDate: Date) -> HKQuery? {
let quantityType = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate)
let datePredicate = HKQuery.predicateForSamples(withStart: startDate, end: nil, options: .strictEndDate)
let predicate = NSCompoundPredicate(andPredicateWithSubpredicates:[datePredicate])
let heartRateQuery = HKAnchoredObjectQuery(type: quantityType!, predicate: predicate, anchor: nil, limit: Int(HKObjectQueryNoLimit)) { (query, sampleObjects, deletedObjects, newAnchor, error) -> Void in
//Do nothing
}
heartRateQuery.updateHandler = {(query, samples, deleteObjects, newAnchor, error) -> Void in
guard let samples = samples as? [HKQuantitySample] else {return}
DispatchQueue.main.async {
guard let sample = samples.first else { return }
let value = sample.quantity.doubleValue(for: HKUnit(from: "count/min"))
print("This line is executed!")
self.label.setText(String(UInt16(value))) //Update label
}
}
return heartRateQuery
}
}
import UIKit
import HealthKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> 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 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:.
}
let healthStore = HKHealthStore()
func applicationShouldRequestHealthAuthorization(_ application: UIApplication) {
healthStore.handleAuthorizationForExtension{
(success,error) in
}
}
}

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.

Location update even when app is killed/terminated

I am trying to get location updates even in all states, even when app is killed/terminated/suspended. I have enabled background fetch in xcode and implemented the following code(used reference "Capture location in all states app"). But when i terminate the app it's giving a red line on class AppDelegate. I do not understand what is the problem here.I have done this using the solution of the question "Getting location for an iOS app when it is in the background and even killed" here, But its not working in ios 9.Please help me out or tell me the other solution.
UPDATED CODE -
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var window: UIWindow?
var locationManager: CLLocationManager?
var significatLocationManager : CLLocationManager?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
if(UIApplication.sharedApplication().backgroundRefreshStatus == UIBackgroundRefreshStatus.Available){
print("yessssss")
}else{
print("noooooo")
}
if let launchOpt = launchOptions{
if (launchOpt[UIApplicationLaunchOptionsLocationKey] != nil) {
self.significatLocationManager = CLLocationManager()
self.significatLocationManager?.delegate = self
self.significatLocationManager?.requestAlwaysAuthorization()
if #available(iOS 9.0, *) {
self.significatLocationManager!.allowsBackgroundLocationUpdates = true
}
self.significatLocationManager?.startMonitoringSignificantLocationChanges()
}else{
self.locationManager = CLLocationManager()
self.locationManager?.delegate = self
self.locationManager?.requestAlwaysAuthorization()
if #available(iOS 9.0, *) {
self.locationManager!.allowsBackgroundLocationUpdates = true
}
self.locationManager?.startMonitoringSignificantLocationChanges()
}
}else{
self.locationManager = CLLocationManager()
self.locationManager?.delegate = self
self.locationManager?.requestAlwaysAuthorization()
if #available(iOS 9.0, *) {
self.locationManager!.allowsBackgroundLocationUpdates = true
}
self.locationManager?.startMonitoringSignificantLocationChanges()
}
return true
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
let locationArray = locations as NSArray
let locationObj = locationArray.lastObject as! CLLocation
let coord = locationObj.coordinate
}
func applicationDidEnterBackground(application: UIApplication) {
if self.significatLocationManager != nil {
self.significatLocationManager?.startMonitoringSignificantLocationChanges()
}else{
self.locationManager?.startMonitoringSignificantLocationChanges()
}
}
Check this tutorial:
http://mobileoop.com/getting-location-updates-for-ios-7-and-8-when-the-app-is-killedterminatedsuspended
And the source code here:
https://github.com/voyage11/GettingLocationWhenSuspended
Hope you will get your answer. It's working for me. Now I am trying to make a http request when 'didUpdateLocations' function being called, so that I can store user current location in server when the app is not running(Suspended/terminated/killed).
I don't think apple allowed to make any http request when the app is suspended/terminated. But if server received the request of location update, then I am good to go with that. Because I don't need response back from server. Need a lot of testing....

Swift: "mapView.showUserLocation = true" returns "fatal error: unexpectedly found nil while unwrapping an Optional value (lldb) "

So I'm trying to search for local businesses in Swift using keywords like "bar", or "pizza". I linked the search to a button action so that the locations will pop up on the map within a defined region. However, I can't even get the application to load with user location because I get a nil error.
Here's my AppDelegate:
import UIKit
import CoreLocation
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var locationManager: CLLocationManager?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
locationManager = CLLocationManager()
locationManager?.requestWhenInUseAuthorization()
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:.
}
}
And here's my ViewController.swift:
import Foundation
import UIKit
import MapKit
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
#IBAction func searchBars(sender: AnyObject) {
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = "Bar"
request.region = mapView.region
let search = MKLocalSearch(request: request)
search.startWithCompletionHandler({(response: MKLocalSearchResponse!, error: NSError!) in
if error != nil {
println("Error occurred in search: \(error.localizedDescription)")
} else if response.mapItems.count == 0 {
println("No matches found")
for item in response.mapItems as [MKMapItem] {
println("Name = \(item.name)")
println("Phone = \(item.phoneNumber)")
}
}
})
}
func mapView(mapView: MKMapView!, didUpdateUserLocation userLocation: MKUserLocation!) {
mapView.centerCoordinate = userLocation.location.coordinate
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
mapView.showsUserLocation = true
mapView.delegate = self
}
#IBAction func zoomIn(sender: AnyObject) {
let userLocation = mapView.userLocation
let region = MKCoordinateRegionMakeWithDistance(userLocation.location.coordinate, 2000, 2000)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The line that returns the nil error is in my ViewController.swift file under #IBAction func zoomIn with the line: let region = MKCoordinateRegionMakeWithDistance(userLocation.location.coordinate, 2000, 2000). Which gives a nil value for some reason.
What you're doing with this line is creating a mapView object which is not instantiated yet.
weak var mapView: MKMapView!
You get the error because you are trying to change showsUserLocation property to an object which doesn't exist yet, it's nil.
What you need to do, if you created the map in a storyboard, is to remove the weak var line and put a IBOutlet instead (Ctrl + Click and drag from the storyboard).
Many thanks to Skoua for his help. I figured out what was wrong on my own after he helped me with the IBOutlet.
Here's the corrected code.
#IBAction func searchBars(sender: AnyObject) {
matchingItems.removeAll()
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = "bar"
request.region = mapView.region
let search = MKLocalSearch(request: request)
search.startWithCompletionHandler({(response: MKLocalSearchResponse!, error: NSError!) in
if error != nil {
println("Error occurred in search: \(error.localizedDescription)")
} else if response.mapItems.count == 0 {
println("No matches found")
//This is where the problem occured
} else {
println("Matches found")
//I needed to insert an else statement for matches being found
for item in response.mapItems as [MKMapItem] {
//This prints the 'matches' into [MKMapItem]
println("Name = \(item.name)")
println("Phone = \(item.phoneNumber)")
self.matchingItems.append(item as MKMapItem)
println("Matching items = \(self.matchingItems.count)")
var annotation = MKPointAnnotation()
annotation.coordinate = item.placemark.coordinate
annotation.title = item.name
self.mapView.addAnnotation(annotation)
}
}
})
}

Resources