Search code examples
iosxcodeios6nsthreaduiactivityindicatorview

XCode: Stop Animating Activity Indicator after Executing a Method From Another File


Ok, this seems like it should be very simple - All I want to do is call my ServerConnect.m (NSObject), NSURL Connection Request Method, from my SignIn.m (ViewController) and stop the UIActivityIndicatorView after the NSURL Request has completed. Of course, if I do it all on the main thread:

- (IBAction)forgotPassword:(id)sender {    
    [activityIndicator startAnimating];
    connection = [[ServerConnect alloc] init];
    [connection sendUserPassword:email withSecurity:securityID];
    [activityIndicator stopAnimating];
}

Then, everything will then execute concurrently, and the activity indicator will start and stop before the connection method finishes...

Thus, I attempted to place the connection request on a secondary thread:

- (IBAction)forgotPassword:(id)sender {
    [NSThread detachNewThreadSelector: @selector(requestNewPassword:) toTarget:self withObject:userEmail.text];
}

- (void) requestNewPassword:(NSString *)email
{
    [self->thinkingIndicator performSelectorOnMainThread:@selector(startAnimating) withObject:nil waitUntilDone:NO];

    //Make NSURL Connection to server on secondary thread
    NSString *securityID = [[NSString alloc] init];
    securityID = @"security";
    connection = [[ServerConnect alloc] init];
    [connection sendUserPassword:email withSecurity:securityID];

    [self->thinkingIndicator performSelectorOnMainThread:@selector(stopAnimating) withObject:nil waitUntilDone:NO];
}

But, I don't see the activity indicators here either, which may be due the NSURL Request not functioning properly on the secondary thread (i.e. for some reason, it does not gather an xml string as it does when requested on the main thread).

What is the proper way to architecture my code to make this work? I am surprised at how much work has been involved in trying to figure out how to get my activity indicator to simply stop after a method from another file has finished executing. Is there a way to run the code in series (one after another) and not concurrently? Any help would be appreciated.

Updated to Show: sendUserPassword:(NSString *)withSecurity:(NSString *)

- (void)sendUserPassword:(NSString *)emailString
            withSecurity:(NSString *)passCode;
{
    NSLog(@"Making request for user's password");
    newUser = NO;
    fbUser = NO;
    forgotPassword = YES;
    NSString *post = [NSString stringWithFormat: @"email=%@&s=%@", emailString, passCode];
    NSData *postData = [post dataUsingEncoding:NSUTF8StringEncoding];
    //Construct the web service URL
    NSURL *url = [NSURL URLWithString:@"http://www.someurl.php"];
    //Create a request object with that URL
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
                                                           cachePolicy:NSURLRequestReloadIgnoringCacheData
                                                       timeoutInterval:90];
    [request setURL:url];
    [request setHTTPMethod:@"POST"];
    [request setHTTPBody:postData];
    //Clear out the existing connection if there is one
    if(connectionInProgress) {
        [connectionInProgress cancel];
    }
    //Instantiate the object to hold all incoming data
    xmlData = [[NSMutableData alloc] init];

    //Create and initiate the conection - non-blocking
    connectionInProgress = [[NSURLConnection alloc] initWithRequest: request
                                                           delegate:self
                                                   startImmediately:YES];
}

Solution

  • I ended up incorporating the NSNotification system (see Multithreading for iOS) to solve my problem. Any reason why this would be frowned upon:

    "One easy way to send updates from one part of your code to another is Apple’s built-in NSNotification system.

    It’s quite simple. You get the NSNotificationCenter singleton (via [NSNotificationCenter defaultCenter]) and:

    1.) If you have an update you want to send, you call postNotificationName. You just give it a unique string you make up (such as “com.razeware.imagegrabber.imageupdated”) and an object (such as the ImageInfo that just finished downloading its image).

    2.) If you want to find out when this update happens, you call addObserver:selector:name:object. In our case the ImageListViewController will want to know when this happens so it can reload the appropriate table view cell. A good spot to put this is in viewDidLoad.

    3.) Don’t forget to call removeObserver:name:object when the view gets unloaded. Otherwise, the notification system might try to call a method on an unloaded view (or worse an unallocated object), which would be a bad thing!"