0

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];
}
JRoss
  • 177
  • 10
  • 23
  • Can you show the implementation of `sendUserPassword:withSecurity:` method? – Alladinian Jun 14 '13 at 07:59
  • Have you tried using gcd rather than managing threads directly? Or even an asynchronous connection that stops the animation in the completion block? – Abizern Jun 14 '13 at 08:40
  • I haven't, @Abizern, can you provide an example or reference as to how this would be done? – JRoss Jun 15 '13 at 01:27
  • I have updated with the sendUserPassword:withSecurity: method. Does that help, @Alladinian? – JRoss Jun 15 '13 at 03:13
  • 2
    Well, since you're sending an async request, why don't you just stop the indicator after you receive the expected response in the appropriate delegate method? – Alladinian Jun 15 '13 at 23:30
  • What do I need to do to access the activity indicator outside of my View Controller file (i.e. within my "ServerConnect" object I created above to request a user's forgotten password)? Interface builder only allows me to link to the IBOutlet in the "File's Owner", @Alladinian... – JRoss Jun 15 '13 at 23:50
  • 1
    **Hint:** Notifications. **Alteratively** You could just do what the rest of us do - use a library like AFNetworking which handles all of this for you and abstracts away a lot of the work you need to do to support asynchronous networking and blocks. – Abizern Jun 16 '13 at 09:49

3 Answers3

0

One suggestion try like this:

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

- (void) requestNewPassword:(NSString *)email
{
    //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];
}
Midhun MP
  • 103,496
  • 31
  • 153
  • 200
0

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!"

JRoss
  • 177
  • 10
  • 23
0

You could try something this, it uses a block when it is finished. I had similar thing right here.

// Turn indicator on

// Setup the request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];
[request setTimeoutInterval: 90.0];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:postData];
request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;

[NSURLConnection sendAsynchronousRequest:request
              queue:[NSOperationQueue currentQueue]
              completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
              // Its has finished but sort out the result (test for data and HTTP 200 i.e. not 404)
              NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
              if (data != nil && error == nil && [httpResponse statusCode] == 200)
              {

                 // Connection finished a gooden
                 // Do whatever you like with data
                 // Stop indicator

              }
              else
              {
                 // There was an error, alert the user
                 // Do whatever you like with data
                 // Stop indicator

              }

          }];
Community
  • 1
  • 1
Recycled Steel
  • 2,272
  • 3
  • 30
  • 35