CLLocationManager delegates not working after initialization
Asked Answered
R

3

5

I am trying to get the compass of the iphone to work using the rhomobile framework. I already did the rhomobile part, created a working wrapper that calls native methods on the iphone, but I cant manage to get the events to work.

Locationmanager.h

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>

@interface locationController : NSObject <CLLocationManagerDelegate> 
{
    CLLocationManager *locationManager;
}
@property (strong, nonatomic) CLLocationManager *locationManager;

- (id)init;
- (void)dealloc;

@end

Locationmanager.m

#import "Locationmanager.h"

#include "ruby/ext/rho/rhoruby.h"

//store the values
double gx, gy, gz, gth;

//init location
locationController *lc;
@implementation locationController

@synthesize locationManager;

- (id)init {
if (self = [super init]) {
    self.locationManager = [[CLLocationManager alloc] init];

    NSLog(@"%@", [CLLocationManager headingAvailable]? @"\n\nHeading available!\n" : @"\n\nNo heading..\n");
    NSLog(@"%@", [CLLocationManager locationServicesEnabled]? @"\n\nLocation available!\n" : @"\n\nNo location..\n");

    // check if the hardware has a compass
    if ([CLLocationManager headingAvailable] == NO) {
        // No compass is available. This application cannot function without a compass, 
        // so a dialog will be displayed and no magnetic data will be measured.
        locationManager = nil;
        UIAlertView *noCompassAlert = [[UIAlertView alloc] initWithTitle:@"No Compass!" message:@"This device does not have the ability to measure magnetic fields." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [noCompassAlert show];
        [noCompassAlert release];
        NSLog(@"\n***** ERROR *****\n No compass found !!!");
    } else {
        // setup delegate callbacks
        locationManager.delegate = self;

        // heading service configuration
        locationManager.headingFilter = kCLHeadingFilterNone;

        // location service configuration
        locationManager.distanceFilter = kCLDistanceFilterNone;
        locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;

        //start location services
        [locationManager startUpdatingLocation];

        // start the compass
        [locationManager startUpdatingHeading];

    }
return self;
}
}
- (void)dealloc {    
    [super dealloc];
    // Stop the compass
    [locationManager stopUpdatingHeading];
    [locationManager release];
}

// This delegate method is invoked when the location manager has heading data.
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)heading {
    NSLog(@"\n\n***** New magnetic heading *****\n %f\n", heading.magneticHeading);
    NSLog(@"\n\n***** New true heading *****\n %f\n", heading.trueHeading);
    gx = heading.x;
    gy = heading.y;
    gz = heading.z;
    gth = heading.trueHeading;
}

// This delegate method is invoked when the location managed encounters an error condition.
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
    if ([error code] == kCLErrorDenied) {
        // This error indicates that the user has denied the application's request to use location services.
        NSLog(@"\n ***** ERROR *****\n Location not allowed!");
        [locationManager stopUpdatingHeading];
    } else if ([error code] == kCLErrorHeadingFailure) {
        NSLog(@"\n ***** ERROR *****\n Magnetic interference or something!");
    }
}

@end

//ruby wrappers
void locationmanager_init(void) {
   // make sure we can only start this method once
   static bool started = false;
   if(!started) {
       // Initialize the Objective C accelerometer class.
       lc = [[locationController alloc] init];
       started = true;
   }

}

void locationmanager_get_heading(double *x, double *y, double *z, double *th) {
    NSLog(@"\n ***** DEBUGGER *****\n Getting heading  x: %f, y: %f, z: %f, heading: %f", gx, gy, gz, gth);
    *x = gx;
    *y = gy;
    *z = gz;
    *th = gth;
}

I'm running the code on an iphone 4 with iOS 5.1, in the console I can see the debug messages of init, but I never see a debug message of the didUpdateHeading delegate. Anyone got a clue what I missed here?

UPDATE

I think I need to run my code in a background thread to get it working. Currently the locationmanager_init initializes + leaves the code, therefor its not active and the events are not fired. Anyone got a simple solution initializing this in the background to keep it active?

UPDATE 2

Returned the id, used self = [super init] and still no fix :(

GitHub code Initializes with locationmanager_init, retrieves data with locationmanager_get_heading

Reveille answered 31/8, 2012 at 15:11 Comment(6)
Have a look at developer.apple.com/library/mac/#documentation/CoreLocation/…Pathognomy
I tried it with and without defining the delegates in the .h file. Also used UIViewController instead of NSObject, as its done in Apple's Teslameter example, still no progress :(Reveille
I put the extension code on GithubReveille
In the Locationmanager.m file you can see the locationmanager_init method that gets called when I enter the view, and the locationmanager_getheading that gets called if someone wants to retrieve a heading.Reveille
are you sure your delegate isn't reaching dealloc prematurely?Fathomless
I commented out dealloc, still no events fired. From the documentation on ios dev I found that the events require an active thread to be fired, I think it might have to do with the object being put in sleep mode after initializing, even before firing the initial event.Reveille
U
16

You have to init the CLLocationManager on the main thread, check this SO here, or run it from a thread with an active run loop, check this SO here, From Apple documentation:

Configuration of your location manager object must always occur on a thread with 
an active run loop, such as your application’s main thread.

Make sure your locationController and the CCLocationManager inside it are alive past initialization, check here. I may be wrong here, but from your Github code, it seems that the *lc variable is getting released in the autorelease pool. Try giving it an extra retain.

lc = [[[locationController alloc] init] retain];

I guess this is the cause of your problem. If the object is released you wont get any updates.

Not related to the question but:

You should call [super dealloc] last but not first, check this SO here

Put the return statement in your init method before the last parenthesis, and not before the second last.

        ...
        // start the compass
        [locationManager startUpdatingHeading];

        }
    }
    return self;
}
Uterus answered 12/9, 2012 at 19:43 Comment(3)
I tried it and updated the code on github. In the console in xcode I dont see any headings appearing, however I got some error I havent seen before: Sep 13 13:00:44 unknown CommCenter[56] <Error>: kDataAttachStatusNotification sent, wasAttached: 1 isAttached: 1Reveille
after combining your answer with performSelectorOnMainThread it worked!! Thanks for your help, I've been stuck on this for about 7 weeks now :) You can imagine how happy I am I can continue my development now. Working code updated @ githubReveille
I had the problem of it being on another thread too. I dread to think how long it would have taken me to figure out without your answer, so thank you very much!Anasarca
P
0
- (void)locationManager:(CLLocationManager *)manager 
didUpdateHeading:(CLHeading *)heading;
- (void)locationManager:(CLLocationManager *)manager 
didFailWithError:(NSError *)error;

These are your instance methods not delegate methods.

check https://developer.apple.com/library/mac/#documentation/CoreLocation/Reference/CLLocationManagerDelegate_Protocol/CLLocationManagerDelegate/CLLocationManagerDelegate.html

Pathognomy answered 31/8, 2012 at 15:27 Comment(1)
The question is for iOS (which has a didUpdateHeading delegate method). Your link points to OS X documentation.Lactobacillus
D
0

Possibly this is not the solution to the overall problem, but your init method is missing a return statement:

- (id)init {
  if (self = [super init]) {
    // do your setup here
  }

  return self;
}
Despairing answered 10/9, 2012 at 9:24 Comment(3)
I've been playing around with many forms of initializing and I forgot to put it back :P Although it doesnt solve anything, it should be there, thanks for the report!Reveille
I also think the problem is with your [init]. from your code fragments, you need to explicitly set self = [super init], as self is not ready yet, unless you do it as tilo suggested, if( self= [super init]) {...}. good luck!Berryberryhill
Tried this and no fix, too bad :(Reveille

© 2022 - 2024 — McMap. All rights reserved.