1

I have an app that updates the interface depending on the result of a NSURL request. I have set it up so that the request is fired when my app comes into the foreground, and only if the current view controller is called "ProfileViewController".

My problem is that the interface locks up for a few seconds every time I bring the app back from the background. I am trying to fully understand main/background threads, but am not sure what I can do to make the app remain responsive while the NSURL check is being performed. Any assistance would be great! Thanks!

In my View Did Load Method:

-(void)viewDidLoad {

    [super viewDidLoad];

    [[NSNotificationCenter defaultCenter] addObserver:self
    selector:@selector(appReturnsActive) name:UIApplicationDidBecomeActiveNotification
    object:nil];

    //additional code
}

Then in my App Returns Active Method:

- (void)appReturnsActive {

    [myTimer invalidate];
    myTimer = nil;

    //ONLY WANT TO PERFORM THE UPDATE IF VIEWING THE PROFILE VIEW CONTROLLER
    UIViewController *currentVC = self.navigationController.visibleViewController;
    NSString * name = NSStringFromClass([currentVC class]);
    if ([name isEqualToString:@"ProfileViewController"]) { 

        [activityIndicator startAnimating];
        [activityIndicatorTwo startAnimating];
        locationManagerProfile.delegate = self;
        locationManagerProfile.desiredAccuracy = kCLLocationAccuracyBest;
        [locationManagerProfile startUpdatingLocation];    
    }
}

Finally, in my Did Update Location Method, I get the distance between the user and the location. If the result is equal to 1, then I update the Interface to show different buttons. This is where the interface freezes up:

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation 
*)newLocation fromLocation:(CLLocation *)oldLocation {

    CLLocation *currentLocation = newLocation;
    if (currentLocation != nil) {

        [self performSelectorOnMainThread:@selector(buttonUpdate:)
        withObject:NULL waitUntilDone:NO];

    }
}

New Method:

-(void)buttonUpdate {

NSString *userLongitude =[NSString stringWithFormat:@"%.8f", 
        currentLocation.coordinate.longitude];
        NSString *userLatitude = [NSString stringWithFormat:@"%.8f", 
        currentLocation.coordinate.latitude];
        [locationManagerProfile stopUpdatingLocation];
        NSString *placeLatitude = [[NSUserDefaults standardUserDefaults]
                                   stringForKey:@"savedLatitude"];
        NSString *placeLongitude = [[NSUserDefaults standardUserDefaults]
                                    stringForKey:@"savedLongitude"];
        NSString *distanceURL = [NSString 
        stringWithFormat:@"http://www.website.com/page.php?
        lat1=%@&lon1=%@&lat2=%@&lon2=%@",userLatitude, userLongitude, placeLatitude, 
        placeLongitude];

        NSData *distanceURLResult = [NSData dataWithContentsOfURL:[NSURL 
        URLWithString:distanceURL]];

        NSString *distanceInFeet = [[NSString alloc] initWithData:distanceURLResult 
        encoding:NSUTF8StringEncoding];

        if ([distanceInFeet isEqualToString:@"1"])
        {
            UIBarButtonItem *btnGo = [[UIBarButtonItem alloc] initWithTitle:@"Button 1" 
            style:UIBarButtonItemStyleBordered target:self 
            action:@selector(actionTwo)];
            self.navigationItem.rightBarButtonItem = btnGo;

            UIBarButtonItem *btnGoTwo = [[UIBarButtonItem alloc] initWithTitle:@"Button 
            Two" style:UIBarButtonItemStyleBordered target:self 
            action:@selector(actionOne)];
            self.navigationItem.rightBarButtonItem = btnGoTwo;

            self.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects:btnGo, 
            btnGoTwo, nil];
        }
}
user2492064
  • 591
  • 1
  • 8
  • 21

2 Answers2

0

Your CoreLocation delegate methods (e.g. "didUpdateToLocation", etc.) are all happening in the background, on secondary threads, while everything UI-related needs to happen on the main thread.

To fix your problem, you should modify your UI on the main thread. One of the handy foundation API's you can use is:

performSelectorOnMainThread:withObject:waitUntilDone:

To fix your problem, move your button-creating code into a separate method and call that new method via "performSelectorOnMainThread" from the old (being called on a separate thread via the CoreLocation delegate protocol) and you should be good to go.

Community
  • 1
  • 1
Michael Dautermann
  • 88,797
  • 17
  • 166
  • 215
  • Thanks for the response!!! Should i put everything from the didUpdateLocation method in the new method that I am about to create? Im assuming that I would put pretty much everything below the if (currentLocation != nil) line in there. – user2492064 Jun 21 '13 at 10:09
  • In my own code, if I need to modify the UI on the main thread from something happening in a background thread, I do as much as possible on the separate thread to keep the user interface (and the user's experience) as peppy as possible. – Michael Dautermann Jun 21 '13 at 10:11
  • Seems to be working!! One small related question. I get an error when I include the NSString that includes currentLocation.coordinate.longitude in the new method, can I fix that? – user2492064 Jun 21 '13 at 10:28
  • Of course you can fix it. One of the parameters is "`withObject`". Why not pass along that NSString object? – Michael Dautermann Jun 21 '13 at 10:30
  • wow. so obvious. you rock! I actually need to update my question and post the code that I have so far because whatever i did made my app crash. ok michael, just updated it. any ideas? thank you!! – user2492064 Jun 21 '13 at 10:41
0
NSData *distanceURLResult = [NSData dataWithContentsOfURL:[NSURL 
    URLWithString:distanceURL]];

is a synchronous call. It means your code will wait for the result of the network call to continue. You should make an Asynchronous request, with NSURLConnection (http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html) or AFNetworking.

Boris Charpentier
  • 3,515
  • 25
  • 28