CLLocationManager not calling any delegate methods - iphone

First off, the disclaimer is I'm targeting iOS 5, so that very well may be the source of my issues, but if not...
I'm trying to write a simple class that manages location updates through CoreLocation. However, I see some pretty strange behavior. I have a custom class which basically wraps CLLocationManager and the delegate methods. This is the interface:
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#protocol CLZipCodeFetcherDelegate
#optional - (void)didReceiveZipCode:(NSString *)zipCode;
#end
#interface CLZipCodeFetcher : NSObject <CLLocationManagerDelegate> {
id <CLZipCodeFetcherDelegate> delegate;
CLLocationManager *locationManager;
}
#property (strong, readwrite) id <CLZipCodeFetcherDelegate> delegate;
#property (strong, read write) CLLocationManager *locationManager;
- (void)getZipCode;
#end
and the implementation (ignore the zip code related stuff- that's what the class is eventually meant to do, but right now I'm just trying to get some coordinates back from CoreLocation):
#import "CLZipCodeFetcher.h"
#implementation CLZipCodeFetcher
#synthesize delegate, locationManager;
- (id) init {
self = [super init];
if (self != nil) {
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.distanceFilter = 10.0f; // we don't need to be any more accurate than 10m
self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
self.locationManager.purpose = #"Retrieve zip code";
}
return self;
}
- (void)getZipCode {
[self.locationManager startUpdatingLocation];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
NSLog(#"Received location");
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
NSLog(#"Location failed");
}
#end
And then I initialize an instance of the class in my view controller and call the method:
CLZipCodeFetcher *zipCodeFetcher = [[CLZipCodeFetcher alloc] init];
[zipCodeFetcher getZipCode];
Now, when I run this, the app takes a moment to load, and then pops up the alert dialog asking for permission to let the app use location services. However, as soon as it appears, it immediately disappears (too fast to let me hit the allow or deny button), and nothing happens (no delegate methods are called). If I then go into the Location Settings of the device, it indeed shows 'Off' for my app. When I turn it on manually from that screen and then go to try the app again, no alert dialog appears, but still nothing happens. Neither of my debug messages are logged. The behavior is the same on both the simulator and my iPhone 4.
So basically, anyone have any idea why? I've looked through all the similar threads, and many issues were related to memory management, but I don't think that'd be a problem here since ARC should be managing my CLLocationManager object, right?
P.S. Also, I did add the CoreLocation framework to my project, and I get no code errors or warnings, but is there anything else I need to do before using CoreLocation in my app?

ARC is deallocating your newly created object after the getZipCode method call.

I have not used ARC yet so I'm not sure of your syntax in the header (readwrite vs. read write).
But I would put an NSLog(#" my loc manager is %#", self.locationManager); inside of getZipCode just to make sure you've got one and it's not being released before it has a chance to do anything.

Related

Looking for a tutorial on moving Pre-Storyboard code (XCode4) to Storyboard code (XCode5) [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking us to recommend or find a tool, library or favorite off-site resource are off-topic for Stack Overflow as they tend to attract opinionated answers and spam. Instead, describe the problem and what has been done so far to solve it.
Closed 8 years ago.
Improve this question
I'm trying to get to grips with XCode5, but most code samples are pre-XCode5. and of course pre iOS7. The main issue is Storyboards. LOTS of people want to know how to build in SCode5 without Storyboards - but I wan't to know how to move pre-storyboard code to storyboard code.
For Example. The most excellent book, "Geolocation in iOS" Alasdair Allan, O'Reilly, 2012, is full of code written a few releases ago. Of course as I am coming into XCode at XCode5/iOS7 level, I have no idea what they are talking about in various parts.
I kind of have the beginning of the sample code working but it's throwing an erro now and I can't figure it out. I suspect because its trying to do it Code4 way, and I'm in XCode5.
Anyway - what would be nice is a tutorial that points out what one changes.
Let me give an example:
The code for the first example in the book goes like this.
In the Project Navigator image in the book, it shows
LocationAppDelegate.h
LocationAppDelegate.m
LocationViewController.h
LocationViewController.
LocationViewController.xib
In my display, I have all the same files, Except. Instead of the ".xib" file I have "Main.storyboard"
So far ok - I believe from what I have read, that the Main.storyboard is the new equivelant of the xib file.
But there are a LOT of differences in the automatically generated code within the .h and .m files. So having done my best, I at least have the location service displaying a dummy location in the debug window.
But - now I have this error. Well two errors actually.
The first, a Semantic warning
LocationViewController.m:15:17: Method 'tableView:cellForRowAtIndexPath:' in protocol not implemented
The second, an error with the red ! mark
LocationViewController.m:60:9: No visible #interface for 'UITableView' declares the selector 'dequeueReusableCellWithIndentifier:'
The code as it appears in the book is fairly straight forward, but this error has lost me.
The code from LocationViewController.m
//
// LocationViewController.h
// Location
//
// Created by Robert Chalmers on 08/10/2013.
// Copyright (c) 2013 Robert Chalmers. All rights reserved.
//
#import <UIKit/UIKit.h>
#interface LocationViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
#property (strong, nonatomic) IBOutlet UITableView *tableView;
#end
The code from LocationViewController.m
//
// LocationViewController.m
// Location
//
// Created by Robert Chalmers on 08/10/2013.
// Copyright (c) 2013 Robert Chalmers. All rights reserved.
//
#import "LocationViewController.h"
#interface LocationViewController ()
#end
#implementation LocationViewController
#synthesize tableView = _tableView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - View lifecycle
#pragma mark UITableViewDelegate Methods
- (void)tableView:(UITableView *)tv
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//add code here
}
#pragma mark UITableViewDataSource Methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tv {
return 1;
}
- (NSInteger) tableView:(UITableView *)tv numberOfRowsInSection:(NSInteger)section {
return 5;
}
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"cell";
UITableViewCell *cell =
//[tv dequeueReusableCellWithIndentifier:#"cell"];
[tv dequeueReusableCellWithIndentifier:#"cell"];
if (cell == nil ) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:identifier];
cell.accessoryType = UITableViewCellAccessoryNone;
return cell;
}
}
#end
and for what it's worth, the code from LocationAppDelegate.h and followed by .m
//
// LocationAppDelegate.h
// Location
//
// Created by Robert Chalmers on 08/10/2013.
// Copyright (c) 2013 Robert Chalmers. All rights reserved.
//
#import <UIKit/UIKit.h>
#class viewController;
#interface LocationAppDelegate : UIResponder <UIApplicationDelegate, CLLocationManagerDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) viewController *viewController;
#property (strong, nonatomic) CLLocationManager *locationManager;
#end
============
//
// LocationAppDelegate.m
// Location
//
// Created by Robert Chalmers on 08/10/2013.
// Copyright (c) 2013 Robert Chalmers. All rights reserved.
//
#import "LocationAppDelegate.h"
#import "LocationViewController.h"
#implementation LocationAppDelegate
#synthesize window = _window;
#synthesize viewController = _viewController;
#synthesize locationManager = _locationManager;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
if ([CLLocationManager locationServicesEnabled]) {
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.distanceFilter = 1000;
[self.locationManager startUpdatingLocation];
}
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
// 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.
}
- (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.
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// 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.
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// 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.
}
- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
NSLog(#"Location: %#", [newLocation description]);
}
- (void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error {
NSLog(#"Error: %#", [error description]);
}
#end
Mostly of course, I'd like to know what that error is, but also if there is some guidelines as to what now goes in what files?
Thanks.
I think you should have a look into this to fix xcode 5 storyboards errors.
Regarding the error, you should try:
[tv dequeueReusableCellWithIdentifier:#"cell" forIndexPath:indexPath];
Instead of:
[tv dequeueReusableCellWithIndentifier:#"cell"];
Here you can find your answer
iOS7 storyboard in Xcode 5 keep growing vertically
Commit a version of your code before Xcode changes its storyboard
Click on the storyboard. Xcode will ask if you want to upgrade.
Choose the always upgrade
At this state the storyboard is already messed up by Xcode. don't
worry. just close the project.
Do a "git stash" and go back to the version to committed in step 0
above
Open your project again.

Trying to pull properties from another class in iOS

I am completely new to iOS development so I may be doing this wrong but I have a class I am using to get coordinate gps data that I want to have as a generic class I can reuse in lots of apps. My problem is getting the data from the gps to properly display in other apps.
Here is my header file for the GPS class:
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#interface LocationAwareness : NSObject <CLLocationManagerDelegate> {
CLLocationManager *locationManager;
}
#property(copy) NSString *longitude;
#property(copy) NSString *latitude;
#end
And here is the implementation:
#import "LocationAwareness.h"
#implementation LocationAwareness
#synthesize longitude;
#synthesize latitude;
- (id)init {
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.distanceFilter = kCLDistanceFilterNone; // whenever we move
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters; // 100 m
[locationManager startUpdatingLocation];
return self;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
// Stops updating location if data has been updated within 10 minutes
if ( abs([newLocation.timestamp timeIntervalSinceDate: [NSDate date]]) < 600) {
[locationManager stopUpdatingLocation];
float latitudedata = newLocation.coordinate.latitude;
latitude = [NSString stringWithFormat:#"%f", latitudedata];
float logitudedata = newLocation.coordinate.longitude;
longitude = [NSString stringWithFormat:#"%f", logitudedata];
}
}
#end
Now I can't seem to find anywhere that tells me how to get the latitude or longitude properties in another project. I have the header imported and have tried to store LocationAwareness.latitude into a variable that I can use but everything I store it in ends up blank. When I start my main class and aloc init a locationawareness object the gps fires up so I think its working but I don't seem to know enough about how this works to get everything in order. I've been searching the internet for hours. Anyone have an idea what I am doing wrong?
Well, this may or may not be causing the problem (it's quite likely), but a major problem is your init method.
The beginning should be:
self = [super init];
if (self) {
// Do your initializing as you did above.
}
return self;
edit:
I added your code with my update to a project and it works well.
In order to use this, you should do something like the following:
LocationAwareness *loc = [[LocationAwareness alloc] init];
// Give it some time to start updating the current location and then
// in a different function:
NSLog(#"%#", loc.latitude);
EDIT 2
Wherever you are using this, you will want to declare a property which stores it so that you can create it once and reference it many times. To do that, use the following code:
In the header for the object where you want to use this object, add this with the other properties:
#property (nonatomic, assign) LocationAwareness *location;
Then, towards the top of your implementation file (.m file) you should see other #synthesize lines, add this one:
#synthesize location;
Then, create the actual location instance that you want to use as per the example above:
self.location = [[LocationAwareness alloc] init];
Now give it some time to figure out your location and start providing updates. Then you can print the location like this:
NSLog(#"%#", self.location.latitude);

How to load location before main view controller loads?

Let me just say I'm still an Objective-C/iOS noob, I haven't been learning it that long, so please excuse my ignorance :)
The content of my app changes based on the user's location. I would prefer the "ThisApp" would like to use your current location dialogue to appear before any content is loaded, to avoid the content having to reload.
I noticed that some apps seem to have that location popup appear while still on the launch image page (the page that shows Default.png). I did a lot of Googling but couldn't find anything (probably because I didn't know the correct terminology).
The way I implemented this was to have my app go to my LocationViewController first, this only contains default code, no changes, and in the storyboard it just has an image view with the Default.png in. Then I have a segue from the location controller (LocationViewControllerSegue) to the main view controller.
My AppDelegate.h looks like so:
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate, CLLocationManagerDelegate> {
CLLocationManager *locationManager;
}
#property (strong, nonatomic) UIWindow *window;
#end
And my AppDelegate.m:
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize window = _window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if (locationManager == nil)
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyKilometer;
locationManager.distanceFilter = 16000;
[locationManager startUpdatingLocation];
return YES;
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
[self.window.rootViewController performSegueWithIdentifier:#"LocationViewControllerSegue" sender:self.window.rootViewController];
}
... snip a bunch of default generated code ...
#end
Is there a better way to do this, than my illusionary location view controller method?
If this is a reasonable way to get what I want, is there somewhere better I could place the performSegueWithIdentifier than in the locationManager? It seems like a bad place to put it (and not sure if it will cause issues further along in the app?), but moving it either into the LocationViewController or into didFinishLaunchingWithOptions: stops it from working.
Any help pointing me in the right direction would be appreciated. Thanks :)
This seems like a fairly hacky method, but one that might work. About the only thing I'd recommend for this particular method would be to have locationManager stop updating the location once you've found what you need ([locationManager stopUpdatingLocation]). Otherwise, as sree charan noted, you'll get a bunch of calls to locationManager:didUpdateToLocation:fromLocation: that will then generate a ton of calls to performSegueWithIdentifier:sender:.
As an aside, I think I might rather do this location update in your LocationViewController rather than the app delegate, but it likely has more to do with style than substance.

CLLocation may not respond to setDistanceFilter

I'm a newbie to programming for iPhone, I'm following along this book.
I'm stuck at the example in Chapter 4, Delegation and Core Location.
Here's the code I've written so far:
WhereamiAppdelegate.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface WhereamiAppDelegate : NSObject <UIApplicationDelegate, CLLocationManagerDelegate> {
UIWindow *window;
CLLocation *locationManager;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#end
And here is the implementation file:
I have only included the changes that I've made.
The entire file is here.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Create location manager.
locationManager = [[CLLocation alloc] init];
[locationManager setDelegate:self];
[locationManager setDistanceFilter:kCLDistanceFilterNone];
[locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
[locationManager startUpdatingLocation];
[self.window makeKeyAndVisible];
return YES;
}
- (void)dealloc
{
[locationManager setDelegate:nil];
[_window release];
[super dealloc];
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
NSLog(#"%#",newLocation);
}
- (void)locationManager:(CLLocationManager *)manager
didFailWithError:(NSError *)error
{
NSLog(#"Couldn't find loaction %#",error);
}
XCode gives me a warning that says CLLocation may not respond to setDistanceFilter and other similar warnings.
I am clueless here, I have followed the book line to line.
I think that I haven't implemented a necessary protocol or something.
Can someone please tell me what I'm doing wrong and how I should proceed further.
The class CLLocation is not the same as CLLocationManager. The former represents one location, while the latter is the manager class that handles configuring location updates for your application.
Things like setDistanceFilter: are undocumented parts of system APIs. They may change without warning in future versions of iOS (any), and they also disqualify your app from being in AppStore, but if you're fine with that, no problem... if Apple really wanted to hide those things, they wouldn't be findable by just making a for-loop on a method-table.
BTW, the difference between CLLocation and CLLocationManager is irrelevant for your situation, because the code actually runs and does the right thing, so, you are sending the correct messages to the correct objects.

iPhone corelocation framework limitation?

fellas,
I am developing a application in which I need functionality to ask user for their location more than once, what happening is when user allowed it once to use his location and when he navigate to another section it's not asking him to get his location, it's taking it from already cached location.
is that possible to ask user multiple time for his approval to get his location?
Any help appreciated.
Cheers,
Amit
You don't need to get permission multiple times.
To begin getting updates, you call:
[locationManager startUpdatingLocation];
Until you call [locationManager stopUpdatingLocation] you will get continuous location updates.
So, you need to implement the delegate method to say what happens when you get a new location. This delegate method could do something as simple as save the location to a class variable for later use.
The delegate function you need to implement is:
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {}
I should caution you here. It is battery intensive to constantly run the GPS (you'll kill the device in about 2.5 hours if you are requesting very accurate reading as fast as possible). So, what you should probably do is get a fix when the user turns the app on, and then call stopUpdatingLocation.
Then, in your app, have a "Locate Me" button, which would turn on the LocationManager, get a fix, and then turn off the LocationManager again. You might want to keep polling for a location until you get a good location.horizontalAccuracy
I suggest you implement an NSObject subclass, which implements the LocationManagerDelegate protocol. Then, this object would be shared across multiple view controllers. Here is a simple implementation of a central gpsController.
So, this would be gpsController.h:
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#interface gpsController : NSObject <CLLocationManagerDelegate> {
CLLocationManager *locationManager;
CLLocation *lastReading;
}
- (id)init;
#property (nonatomic, retain) CLLocationManager *locationManager;
#property (nonatomic, retain) CLLocation *lastReading;
#end
And then the following is gpsController.m:
#import "gpsController.h"
#implementation gpsController
#synthesize locationManager, lastReading;
- (id)init {
if(self = [super init]) {
[[self locationManager] startUpdatingLocation];
self.lastReading = nil;
}
return self;
}
- (CLLocationManager *)locationManager {
if (locationManager) return locationManager;
locationManager = [[CLLocationManager alloc] init];
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
locationManager.delegate = self;
return locationManager;
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
self.lastReading = newLocation;
}
According to iPhone Programming: The Big Nerd Ranch Guide, CLLocationManagers return the cached, last found device location first. Their sample code bails out of locationManager:didUpdateToLocation:fromLocation if the didUpdateToLocation timestamp is more than 3 minutes old, assuming it to be from the cache. That seems a pretty long time between valid updates, but maybe only by doing some testing would one know.
Maybe you could persist the timestamp between messages to see if you're still being served location data from the cache, or increment a counter for each call to locationManager:didUpdateToLocation:fromLocation and only ever use odd numbered calls or some other increment...