I wonder how to capture the location when the app is not running and save the database. Already followed several tutorials but none worked. Please, before scoring as duplicate, I ask you to read the complete question as it can not be.
I tried the following tutorials:
Getting Location Updates for iOS 7 and 8 when the App is Killed/Terminated/Suspended
Background Modes Tutorial: Getting Started
to run app continously in the background
Not exactly what I need, but still I tried
Assuming that I have no implementation for the capture location. I need every day at noon, capture the user's location and save the database then send to a web service.
This capture location must be made regardless of whether the app is active in the background or not running (two rings on the middle button and dragging).
I tried some tutorials found on the internet and even some suggested here by the community, but still rather not worked.
I have added background modes - Location.
I allowed to get location requestAlwaysAuthorization.
The language can be in objective-c or swift, Most importantly, I learn how to capture this location when app in all states.
OK i am going to make this as simple as possible..Enable Background Modes and tick background fetch like this
Follow this methods for every step
When app is TERMINATED
AppDelegate.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate,CLLocationManagerDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong,nonatomic) CLLocationManager *locationManager;
#end
AppDelegate.m
#define userDef [NSUserDefaults standardUserDefaults]
#define IS_OS_8_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
#import "AppDelegate.h"
#import <CoreLocation/CoreLocation.h>
#import "AFNetworking.h"
#import <GoogleMaps/GoogleMaps.h>
#implementation AppDelegate{
BOOL fromTerminated;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
fromTerminated = NO;
if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey]) {
fromTerminated = YES;
self.locationManager = [[CLLocationManager alloc]init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
self.locationManager.activityType = CLActivityTypeOtherNavigation;
[self.locationManager startUpdatingLocation];
}
return YES;
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
if(self.locationManager){
[self.locationManager stopMonitoringSignificantLocationChanges];
self.locationManager = nil;
self.locationManager.delegate = nil;
}
self.locationManager = [[CLLocationManager alloc]init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
self.locationManager.activityType = CLActivityTypeOtherNavigation;
if(IS_OS_8_OR_LATER) {
[self.locationManager requestAlwaysAuthorization];
}
[self.locationManager startMonitoringSignificantLocationChanges];
}
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations{
NSLog(#"locationManager didUpdateLocations: %#",locations);
if(fromTerminated){
CLLocation * newLocation = [locations lastObject];
CLLocationCoordinate2D theLocation = newLocation.coordinate;
CLLocationAccuracy theAccuracy = newLocation.horizontalAccuracy;
[userDef setObject:[NSString stringWithFormat:#"%f",theLocation.longitude] forKey:#"LONGITUDE"];
[userDef setObject:[NSString stringWithFormat:#"%f",theLocation.latitude] forKey:#"LATITUDE"];
self.myLocation = theLocation;
self.myLocationAccuracy = theAccuracy;
[self updateLocation];
}
}
-(void)updateLocation{
// call your webservice for updating
}
#end
This code will do the following-->
Background fetch will trigger a location change and will launch the application and didFInishLaunchingWithOptions will be called with a UIApplicationLaunchOptionsLocationKey in the option dictionary. If it finds this that means the application is TERMINATED and woke up for background fetch. Now you got 30 seconds or so to do your stuff. So you create a location manager object and start updating, which will trigger your didUpdateLocations delegate method and then you can call your method to trigger that changed location in your server or database.
In your normal VC create another Location Manager object just like you created in the didFinishiLaunchingWithOptions method and implement the delegate method didUpdateLocation this will run untill the application is in foreground or background. The app delegate method hack will trigger the application when its terminated.
Cheers :)
[EDIT]
When app is in foreground or Background
#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
#interface ViewController ()<CLLocationManagerDelegate>
#property (nonatomic,strong) CLLocationManager *locationManager;
#end
#implementation ViewController{
}
-(void)viewDidAppear:(BOOL)animated{
if(self.locationManager == nil){
_locationManager = [CLLocationManager new];
}
_locationManager.delegate = self;
_locationManager.distanceFilter = kCLDistanceFilterNone;
_locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0 && [CLLocationManager authorizationStatus] != kCLAuthorizationStatusAuthorizedWhenInUse) {
[_locationManager requestAlwaysAuthorization];
} else {
[_locationManager startUpdatingLocation];
}
}
- (void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
_locationManager = nil;
CLLocation *location = [locations lastObject];
theUser.latitude = [NSString stringWithFormat:#"%f",location.coordinate.latitude];
theUser.longitude = [NSString stringWithFormat:#"%f",location.coordinate.longitude];
}
}
- (void)locationManager:(CLLocationManager*)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
switch (status) {
case kCLAuthorizationStatusNotDetermined: {
} break;
case kCLAuthorizationStatusDenied: {
} break;
case kCLAuthorizationStatusAuthorizedWhenInUse:
case kCLAuthorizationStatusAuthorizedAlways: {
[_locationManager startUpdatingLocation]; //Will update location immediately
} break;
default:
break;
}
}
#end
[EDIT]
TO check that app is getting launched after terminated state do this change and hit the run button and change the location of the device from the storyboard
Do this change, and Run the project (Do this in debug mode of a device)
and change location by this and then stick a breakpoint in applicationDidFinishLaunchingWithOptions and you will see the breakpoint is hit, meaning the app was in terminated state but this location change has triggered the OS to launch the application.
Hope could make you understand
Related
in iOS8 when i m putting my app in back ground the CLLocationManager returns Null in location ,i have also unable Location updates in Background Modes and also do requestAlwaysAuthorization
CLLocationManager *lm = [[CLLocationManager alloc] init];
lm.delegate = self;
lm.desiredAccuracy = kCLLocationAccuracyBest;
lm.distanceFilter = kCLDistanceFilterNone;
[lm requestAlwaysAuthorization];
[lm startUpdatingLocation];
Please find below code for getting location on background mode -
Please enter text for NSLocationAlwaysUsageDescription in plist file .
Please select location updates on background mode from project settings -
AppDelegate.h
//
// AppDelegate.h
// LocationTest
//
// Created by Santu C on 28/05/15.
// Copyright (c) 2015 company. All rights reserved.
//
#import <UIKit/UIKit.h>
#import<CoreLocation/CoreLocation.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property(strong,nonatomic) CLLocationManager *locationManager;
#end
AppDelegate.m
//
// AppDelegate.m
// LocationTest
//
// Created by Santu C on 28/05/15.
// Copyright (c) 2015 company. All rights reserved.
//
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize locationManager;
#define CONST_TIME_INTERVAL 60.0
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self location];
// Override point for customization after application launch.
return YES;
}
-(void)location
{
NSLog(#"location called");
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.distanceFilter = kCLDistanceFilterNone;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
self.locationManager.pausesLocationUpdatesAutomatically = NO;
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
if ([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)])
{
[self.locationManager requestAlwaysAuthorization];
}
#endif
[self.locationManager startUpdatingLocation];
}
- (void)startUpdatingLocation
{
NSLog(#"startUpdatingLocation called");
self.locationManager.delegate = self;
[self.locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
NSLog(#"locationManager didUpdateLocations");
CLLocation *newLocation = (CLLocation *)[locations lastObject];
NSLog(#"dLongitude : %f", newLocation.coordinate.latitude);
NSLog(#"dLatitude : %f", newLocation.coordinate.longitude);
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// 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.
[self startUpdatingLocation];
}
#end
Hope this will help you
do put code in viewDidLoad about CLLocationManger
CLLocationManager *lm = [[CLLocationManager alloc] init];
lm.delegate = self;
lm.desiredAccuracy = kCLLocationAccuracyBest;
lm.distanceFilter = kCLDistanceFilterNone;
[lm requestAlwaysAuthorization];
[lm startUpdatingLocation];
Problem solved..
My customer asked if it is possible that when a customer walks by his store he receives an email with todays special prices, even if the app is not running.
My question is: Is it allowed by iOS to call a restservice if the app is wakened by the iBeacon event?
I tried to play a system sound to simulate the restservice call and this is not working. Only when the app is in foreground.
To give you an idea how I designed my Beaconhandler so far, here is my code. Perhaps someone has an idea to improve it:
#import "BeaconHandler.h"
#interface BeaconHandler ()
#property (strong, nonatomic) CLLocationManager *locationManager;
#property CLProximity lastProximity;
#end
#implementation BeaconHandler
-(void) startMonitoring{
if(![self monitoringIsAllowed]){
return;
}
[self initLocationManager];
[self startMonitoringBeacons:[self beaconIDsToMonitor]];
}
-(BOOL) monitoringIsAllowed{
//TODO configuration
return YES;
}
-(NSDictionary*) beaconIDsToMonitor{
//TODO: load beacons from server
return #{#"region1":[[NSUUID alloc] initWithUUIDString:#"B9407F30-F5F8-466E-AFF9-25556B57FE6D"],
#"region2":[[NSUUID alloc] initWithUUIDString:#"B9407F30-F5F8-466E-AFF9-25556B57FE6A"]
};
}
-(void) initLocationManager{
self.locationManager = [[CLLocationManager alloc] init];
if([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[self.locationManager requestAlwaysAuthorization];
}
self.locationManager.delegate = self;
self.locationManager.pausesLocationUpdatesAutomatically = NO;
}
-(void) startMonitoringBeacons:(NSDictionary*)beacons{
for (NSString* beaconIdentifier in beacons.allKeys) {
NSUUID *beaconUUID = beacons[beaconIdentifier];
CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:beaconUUID identifier:beaconIdentifier];
beaconRegion.notifyEntryStateOnDisplay = YES;
[self.locationManager startMonitoringForRegion:beaconRegion];
[self.locationManager startRangingBeaconsInRegion:beaconRegion];
}
[self.locationManager startUpdatingLocation];
}
//TODO replace by backend call
-(void)sendLocalNotificationWithMessage:(NSString*)message{
UILocalNotification *notification = [[UILocalNotification alloc] init];
notification.alertBody =message;
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
#pragma CLLocationDelegate
-(void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region {
for (CLBeacon *beacon in beacons) {
if(beacon.proximity == self.lastProximity ||
beacon.proximity == CLProximityUnknown) {
return;
}
self.lastProximity = beacon.proximity;
[self sendLocalNotificationWithMessage:[NSString stringWithFormat:#"You are inside region %#", region.identifier]];
}
}
Yes, this is possible. I can confirm I have done this successfully on iOS. A couple of tips to get it running in the background:
Get this working in the foreground first.
You only have 5 seconds of background running time after entering/exiting a region, so make sure your web service returns quickly.
Add NSLog statements to your callbacks to figure out what is and is not completing in the background.
If the above does not help, post your code in the callback.
I'm trying to use core location in iOS 8 simulator, for that I added in my view an object of type 'MapKit View', In tab Atributtes inspector is checked the option show user location, In my project I'm using ARC, below is the structure of my code:
ViewController.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#import <MapKit/MapKit.h>
#interface MeuPrimeiroViewController : UIViewController <MKMapViewDelegate, CLLocationManagerDelegate>{
IBOutlet MKMapView *mapView;
}
#property (nonatomic, strong) CLLocationManager *locationManager;
#end
ViewController.m
#synthesize locationManager;
- (void)viewDidLoad {
[super viewDidLoad];
if ([CLLocationManager locationServicesEnabled]) {
NSLog(#"CLLocationManager locationServicesEnabled == ON");
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
// Check for iOS 8 Vs earlier version like iOS7.Otherwise code will
// crash on ios 7
if ([locationManager respondsToSelector:#selector
(requestWhenInUseAuthorization)]) {
[locationManager requestAlwaysAuthorization];
}
[locationManager startUpdatingLocation];
}else{
NSLog(#"CLLocationManager locationServicesEnabled == OFF");
}
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
NSLog(#"It works this method is called");
}
-(void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error{
NSLog(#"Error: %#",[error description]);
}
In my info.plist file I add this key (NSLocationAlwaysUsageDescription) with this value (String).
If I go to attributes inspector and enable the checkbox (shows user location) I receive this error message, and core location methods are not called:
Trying to start MapKit location updates without prompting for location authorization. Must call -[CLLocationManager requestWhenInUseAuthorization] or -[CLLocationManager requestAlwaysAuthorization] first.
If I disable the checkbox, this message disappear, and core location methods not called again. I try to change the navigation to londom, try to change location to free run but nothing I tried worked, the methods do not continue to be called and never showed an empowering message to use core location. I believe I've already tried everything, anyone have any suggestions or a solution to this problem?
This is what i am doing for my app and working perfectly
- (void)startSignificantChangeUpdates {
if (nil == self.locationManager) {
self.locationManager = [[CLLocationManager alloc] init];
}
self.locationManager.delegate = self;
[self.locationManager startMonitoringSignificantLocationChanges];
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
CLLocation* location = [locations lastObject];
if (location) {
self.currentLocation = location;
NSString *latitude = [NSString stringWithFormat:#"%f", location.coordinate.latitude];
NSString *longitude = [NSString stringWithFormat:#"%f", location.coordinate.longitude];
self.latLong = [NSString stringWithFormat:#"%#,%#",latitude, longitude];
}
if (!self.geocoder)
self.geocoder = [[CLGeocoder alloc] init];
[self.geocoder reverseGeocodeLocation:location completionHandler:
^(NSArray* placemarks, NSError* error){
if ([placemarks count] > 0) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
if (placemark.postalCode) {
self.currentZipCode = placemark.postalCode;
}
[[NSNotificationCenter defaultCenter] postNotificationName:#"zipCodeFoundNotification" object:self.currentZipCode userInfo:nil];
} else {
[[NSNotificationCenter defaultCenter] postNotificationName:#"zipCodeFoundNotification" object:nil userInfo:nil];
}
}];
}
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
if (status != kCLAuthorizationStatusAuthorized && status != kCLAuthorizationStatusNotDetermined) {
if (status == kCLAuthorizationStatusDenied){
self.currentZipCode = #"kCLAuthorizationStatusDenied";
} else if (status == kCLAuthorizationStatusRestricted) {
self.currentZipCode = #"kCLAuthorizationStatusRestricted";
}
}
}
I'm creating an app that uses core location and this is my code:
the .h :
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface ViewController : UIViewController
<CLLocationManagerDelegate>
#property(strong,nonatomic) CLLocationManager *manager;
#end
the .m :
#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>
#interface ViewController ()
#end
#implementation ViewController
#synthesize manager;
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidLoad
{
[super viewDidLoad];
if (!self.manager)
{
self.manager=[CLLocationManager new];
}
self.manager.delegate = self;
self.manager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
[self.manager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation * currentLocation = (CLLocation *)[locations lastObject];
NSLog(#"Location: %#", currentLocation);
if (currentLocation != nil)
{
NSLog([NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude]);
NSLog([NSString stringWithFormat:#"%.8f", currentLocation.coordinate.longitude]);
}
}
#end
the problem is didUpdateToLocation is not being called.
I tried putting a breakpoint inside of it and nothing happened.
For iOS location tracking to work correctly, here are the prerequisites, and the order is of importance, too:
Define your class as CLLocationManagerDelegate.
Instantiate CLLocationManager instance.
Set its delegate to self.
Set its various properties (accuracy etc)
Call [CLLocationManagerDelegate startUpdatingLocation] when you intend to.
Listen to location updates within your delegate method didUpdateLocations, implemented within the class designated as CLLocationManagerDelegate.
With your code, here are the things you need to correct:
-(IBAction)submit
{
[self.manager startUpdatingLocation];
}
- (void)viewDidLoad
{
[super viewDidLoad];
if (!self.manager)
{
self.manager=[CLLocationManager new];
}
self.manager.delegate = self;
self.manager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
}
In short, before telling it to start updating location, you need to instantiate it and set its properties right. Currently you are doing it the reverse way.
UPDATE
Also, your delegate method didUpdateToLocation is deprecated since iOS 6. You must replace it with newer method, like this:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation * currentLocation = (CLLocation *)[locations lastObject];
NSLog(#"Location: %#", currentLocation);
if (currentLocation != nil)
{
self.latitude.text = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.latitude];
self.longitude.text = [NSString stringWithFormat:#"%.8f", currentLocation.coordinate.longitude];
}
}
You need to add these keys to your Info.plist:
NSLocationWhenInUseUsageDescription
NSLocationAlwaysUsageDescription
And also request for the type you want to authorise if iOS8:
if ([self.locationManager respondsToSelector:#selector(requestWhenInUseAuthorization)])
{
[self.locationManager requestWhenInUseAuthorization];
}
[self.locationManager startUpdatingLocation];
So the system will display the alert "Allow "app" to access your location...? Allowing will start your location manager and didUpdateLocations will be called.
I've heard time and time again that there is always a better pattern than the singleton, but I can't understand how else my application could access the device location without waiting for the GPS to return data (I'm assuming that the location system is only running when explicitly called for, correct me if wrong).
So is there a better pattern for accessing CLLocation data from multiple (unrelated) controllers? Or can I expect the device location to be updating in the background even if I am not accessing it through a CLLocationManager?
Declare a single class . Like the following .
MyLocation.h
#protocol MyCLControllerDelegate <NSObject>
- (void)locationUpdate:(CLLocation *)location;
- (void)locationError:(NSError *)error;
#end
#interface MyLocation : NSObject <CLLocationManagerDelegate> {
CLLocationManager *locationManager;
id delegate;
}
#property (nonatomic, strong) CLLocationManager *locationManager;
#property (nonatomic, strong) id <MyCLControllerDelegate> delegate;
MyLocation.m
#import "MyLocation.h"
#implementation MyLocation
#synthesize locationManager;
#synthesize delegate;
- (id) init {
self = [super init];
if (self != nil) {
if([CLLocationManager locationServicesEnabled]) {
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusRestricted )
{
[self showAlertWithTitle:#"Warning" andWithMessage:#"Determining your current location cannot be performed at this time because location services are enabled but restricted" forTargetView:self];
NSlog(#"Determining your current location cannot be performed at this time because location services are enabled but restricted");
}
else
{
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self; // send loc updates to myself
[self.locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[self.locationManager setDistanceFilter:kThresholdDistance];
[self.locationManager startUpdatingLocation];
NSLog(#"Location sharing set ON!");
}
} else {
[MobileYakHelper showAlertWithTitle:#"Error" andWithMessage:#"Determining your current location cannot be performed at this time because location services are not enabled." forTargetView:self];
NSLog(#"Location sharing set OFF!");
}
}
return self;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
NSDictionary *dictValue = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithDouble:newLocation.coordinate.latitude], #"latitude",
[NSNumber numberWithDouble:newLocation.coordinate.longitude], #"longitude",
nil];
[[NSUserDefaults standardUserDefaults] setValue:dictValue forKey:#"MY_LOCATION"];
CLLocationDistance meters = [newLocation distanceFromLocation:oldLocation];
if (meters >= kThresholdDistance ) {
[self.delegate locationUpdate:newLocation];
}
}
- (void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error
{
[self.delegate locationError:error];
}
#end
To use it in few controller .Adapt delegate in it's .h file and use like as follows :
- (void) initializeLocations
{
MyLocation _myLocation = [[MyLocation alloc] init];
_myLocation.delegate = self;
_myLocation.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
CLLocation * currentLocation = _myLocation.locationManager.location;
// Updating user's current location to server
[self sendUserCurrentCoordinate:currentLocation.coordinate];
// start updating current location
_myLocation.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[_myLocation.locationManager startUpdatingLocation];
[_myLocation.locationManager startUpdatingLocation];
}
- (void)locationUpdate:(CLLocation *)location {
NSLog(#"location %#", location);
}
- (void)locationError:(NSError *)error {
NSLog(#"locationdescription %#", [error description]);
}