0
votes

I use this piece of code to create some NSOperations and add them to a queue:

HUD = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];
        //HUD.labelText = @"Downloading..";
        //HUD.dimBackground = YES;

        /* Operation Queue init  */
        NSOperationQueue *queue = [NSOperationQueue new];

        // Spawn request operations & add them to queue
        SoapRequestOperation *operation = [[SoapRequestOperation alloc] initWithRequest:@"<?xml version=\"1.0\" encoding=\"utf-8\"?> <soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"> <soap:Body> <Categories xmlns=\"http://tempuri.org/\"> <UID>string</UID> <Username>string</Username> <Password>string</Password> </Categories>      </soap:Body> </soap:Envelope>" andValue:@"Categories"];

        [queue addOperation:operation];

        operation = [[SoapRequestOperation alloc] initWithRequest:@"<?xml version=\"1.0\" encoding=\"utf-8\"?> <soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"> <soap:Body> <Currencies xmlns=\"http://tempuri.org/\"> <UID>string</UID> <Username>string</Username> <Password>string</Password> </Currencies>      </soap:Body> </soap:Envelope>" andValue:@"Currencies"];

        [queue addOperation:operation];

        operation = [[SoapRequestOperation alloc] initWithRequest:@"<?xml version=\"1.0\" encoding=\"utf-8\"?> <soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"> <soap:Body> <Nominals xmlns=\"http://tempuri.org/\">   <UID>string</UID> <Username>string</Username> <Password>string</Password> </Nominals> </soap:Body> </soap:Envelope>" andValue:@"Nominals"];

        [queue addOperation:operation];

        operation = [[SoapRequestOperation alloc] initWithRequest:@"<?xml version=\"1.0\" encoding=\"utf-8\"?> <soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"> <soap:Body> <Projects xmlns=\"http://tempuri.org/\">   <UID>string</UID> <Username>string</Username> <Password>string</Password> </Projects>        </soap:Body> </soap:Envelope>" andValue:@"Projects"];

        [queue addOperation:operation];

        operation = [[SoapRequestOperation alloc] initWithRequest:@"<?xml version=\"1.0\" encoding=\"utf-8\"?> <soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"> <soap:Body> <Register xmlns=\"http://tempuri.org/\">   <UID>string</UID> <Username>string</Username> <Password>string</Password> <OrganisationCode>string</OrganisationCode> </Register> </soap:Body> </soap:Envelope>" andValue:@"Register"];

        [queue addOperation:operation];

        [queue waitUntilAllOperationsAreFinished];

        [self reloadTableData];

        HUD.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"37x-Checkmark.png"]];
        HUD.mode = MBProgressHUDModeCustomView;
        [HUD hide:YES afterDelay:2];

This is the .m file for my NSOperation subclass:

#import "SoapRequestOperation.h"
#import "Currency.h"
#import "Category.h"
#import "Project.h"
#import "Constants.h"

@implementation SoapRequestOperation

@synthesize request, value;

-(id) initWithRequest:(NSString *)l_Request andValue:(NSString *)l_Value{
    if (self = [super init]) {
        /* do most of initialization */
        self.request = l_Request;
        self.value = l_Value;
        xmlBlock = 0;
        appDelegate = [[UIApplication sharedApplication] delegate];
    }
    return(self);
}

- (void) main {
    if ([value isEqualToString:@"Category"]){
        xmlBlock = CATEGORY;
    }
    else if ([value isEqualToString:@"Currency"]){
        xmlBlock = CURRENCY;
    }
    else if ([value isEqualToString:@"Nominal"]){
        xmlBlock = NOMINAL;
    }
    else if ([value isEqualToString:@"Project"]){
        xmlBlock = PROJECT;
    }
    else {
        xmlBlock = REGISTER;
    }

    NSString *soapMsg = request;
    //---print it to the Debugger Console for verification---
    NSLog(@"%@", soapMsg);
    NSURL *url = [NSURL URLWithString:
                  @"http://www.$^$%£%@%^£.co.uk/$$^$^£$^£.asmx"];
    NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
    //---set the headers---
    NSString *msgLength = [NSString stringWithFormat:@"%d",
                           [soapMsg length]];
    [req addValue:@"text/xml; charset=utf-8"
forHTTPHeaderField:@"Content-Type"];
    [req addValue:[NSString stringWithFormat:@"http://tempuri.org/%@", value]
forHTTPHeaderField:@"SOAPAction"];
    [req addValue:msgLength forHTTPHeaderField:@"Content-Length"];
    //---set the HTTP method and body---
    [req setHTTPMethod:@"POST"];
    [req setHTTPBody: [soapMsg dataUsingEncoding:NSUTF8StringEncoding]];
    //[activityIndicator startAnimating];
    conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
    if (conn) {
        webData = [NSMutableData data];
    }
}

-(void) connection:(NSURLConnection *) connection
didReceiveResponse:(NSURLResponse *) response {
    [webData setLength: 0];
}

-(void) connection:(NSURLConnection *) connection
    didReceiveData:(NSData *) data {
    [webData appendData:data];
}

-(void) connection:(NSURLConnection *) connection
  didFailWithError:(NSError *) error {

}

-(void) connectionDidFinishLoading:(NSURLConnection *) connection {
    NSLog(@"DONE. Received Bytes: %d", [webData length]);
    NSString *theXML = [[NSString alloc]
                        initWithBytes: [webData mutableBytes]
                        length:[webData length]
                        encoding:NSUTF8StringEncoding];
    //---shows the XML---
    NSLog(@"%@", theXML);

    xmlParser = [[NSXMLParser alloc] initWithData: webData];
    [xmlParser setDelegate: self];
    [xmlParser setShouldResolveExternalEntities:YES];
    [xmlParser parse];

}

//---when the start of an element is found---
-(void) parser:(NSXMLParser *) parser
didStartElement:(NSString *) elementName
  namespaceURI:(NSString *) namespaceURI
 qualifiedName:(NSString *) qName
    attributes:(NSDictionary *) attributeDict {
    currentElement = [elementName copy];

    //Category
    if( [currentElement isEqualToString:@"Item"])
    {
        category = [[NSMutableDictionary alloc] init];
        categoryName = [[NSMutableString alloc] init];
    }

    //Currency
    if ([currentElement isEqualToString: @"Currency"]) {
        currency = [[NSMutableDictionary alloc] init];
        countryName = [[NSMutableString alloc] init];
        currencyName = [[NSMutableString alloc] init];
        currencyUnit = [[NSMutableString alloc] init];
        shortName = [[NSMutableString alloc] init];
    }

    //Nominal
    if( [currentElement isEqualToString:@"Nominal"])
    {
        nominal = [[NSMutableDictionary alloc] init];
        name = [[NSMutableString alloc] init];
        description = [[NSMutableString alloc] init];
        paid = [[NSMutableString alloc] init];
    }

    //Project
    if( [currentElement isEqualToString:@"Project"])
    {
        project = [[NSMutableDictionary alloc] init];
        projectName = [[NSMutableString alloc] init];
        projectDescription = [[NSMutableString alloc] init];
    }

    //Register
    if( [currentElement isEqualToString:@"RegisterResult"])
    {
        elementFound = YES;
    }

}

-(void)parser:(NSXMLParser *) parser foundCharacters:(NSString *)string
{
    //Category
    if (xmlBlock == CATEGORY){
        if ([currentElement isEqualToString: @"Item"]){
            [categoryName appendString:string];
        }
    }

    //Currency
    if (xmlBlock == CURRENCY){
        if ([currentElement isEqualToString: @"CountryName"]){
            [countryName appendString:string];
        }
        else if ([currentElement isEqualToString: @"CurrencyName"]){
            [currencyName appendString:string];
        }
        else if ([currentElement isEqualToString: @"Unit"]){
            [currencyUnit appendString:string];
        }
        else if ([currentElement isEqualToString: @"ShortName"]){
            [shortName appendString:string];
        }
    }

    //Nominal
    if ([currentElement isEqualToString: @"Name"] && xmlBlock == NOMINAL){
        [name appendString:string];
    }
    else if ([currentElement isEqualToString: @"Description"] && xmlBlock == NOMINAL){
        [description appendString:string];
    }
    else if ([currentElement isEqualToString: @"Paid"]){
        [paid appendString:string];
    }

    //Project
    if (xmlBlock == PROJECT) {
        if ([currentElement isEqualToString: @"Name"]){
            [projectName appendString:string];
        }
        else if ([currentElement isEqualToString: @"Description"]){
            [projectDescription appendString:string];
        }
    }

    //Register
    if (xmlBlock == REGISTER && elementFound){
        registerVal = string;
    }
}

//---when the end of element is found---
-(void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
 namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{    
    if ([elementName isEqualToString:@"Item"])
    {
        // save values to an item, then store that item into the array...
        [category setObject:categoryName forKey:@"categoryName"];

        [categories addObject:[category copy]];

        Category *c = [[Category alloc] init];
        c.categoryName = categoryName;
        c.totalValue = @"0.00";
        [appDelegate.categories addObject:c];

        NSLog(@"%d", [appDelegate.categories count]);
    }

    //Currency
    if ([elementName isEqualToString: @"Currency"]) {
        // save values to an item, then store that item into the array...
        [currency setObject:countryName forKey:@"countryName"];
        [currency setObject:currencyName forKey:@"currencyName"];
        [currency setObject:currencyUnit forKey:@"unit"];
        [currency setObject:shortName forKey:@"shortName"];

        [currencies addObject:[currency copy]];

        Currency *c = [[Currency alloc] init];
        c.totalValue = @"0.00";
        c.countryName = countryName;
        c.currencyName = currencyName;
        double myDouble = [currencyUnit doubleValue];
        c.unit = myDouble;
        c.shortName = shortName;

        [appDelegate.currencies addObject:c];

        NSLog(@"%d", [appDelegate.currencies count]);

    }

    //Nominal
    if ([elementName isEqualToString:@"Nominal"])
    {
        // save values to an item, then store that item into the array...
        [nominal setObject:name forKey:@"name"];
        [nominal setObject:description forKey:@"description"];
        [nominal setObject:paid forKey:@"paid"];

        [nominals addObject:[nominal copy]];

        NSLog(@"%d", [nominals count]);

    }

    if ([elementName isEqualToString:@"Project"])
    {
        // save values to an item, then store that item into the array...
        [project setObject:projectName forKey:@"projectName"];
        [project setObject:projectDescription forKey:@"projectDescription"];

        [projects addObject:[project copy]];

        Project *p = [NSEntityDescription
                      insertNewObjectForEntityForName:@"Project"
                      inManagedObjectContext:appDelegate.managedObjectContext];
        [p setValue:projectName forKey:@"name"];
        [p setValue:@"0.00" forKey:@"totalValue"];
        NSError *error;
        if (![appDelegate.managedObjectContext save:&error]) {
            NSLog(@"Whoops, couldn't save: %@", [error localizedDescription]);
        }
        [appDelegate.projects addObject:p];

        NSLog(@"%d", [appDelegate.currencies count]);
    }

    if ([elementName isEqualToString:@"RegisterResult"])
    {
        if ([registerVal isEqualToString:@"true"]){
            boolRegister = YES;
        }
        else {
            boolRegister = NO;
        }

        NSLog(@"BOOL = %@", (boolRegister ? @"YES" : @"NO"));

        elementFound = FALSE;
    }
}

Unfortunately, the NSURLConnection and NSXMLParser delegate methods are not being called (everything is as it should be in the header file). And I'm not sure I fully understand the line:

[queue waitUntilAllOperationsAreFinished];

Should this not cause the calling thread to wait until all the operations are completed? I have a feeling this line isn't working due to the faults with the NSOperation subclass itself?

Can anyone help me out here?

Thanks a lot,

Jack

2
You have to do NSURLConnections on the main thread. NSOperations are not performed on the main thread this is why your connections aren't working and why none of the delegates are being called.shoughton123
@shoughton123 I see, so is the answer to this question: stackoverflow.com/questions/10654129/… provided by The Saad incorrect? Or have I misunderstood? Thanks.Jack Nutkins
You absolutely do not need to run NSURLConnections on the main thread.Joel

2 Answers

2
votes

The issue is how you have setup your operation. It is setup as a non-concurrent operation so it is executing the main method in a separate thread then finishing up. By the time the delegates are called, the thread is gone. Poof. Your issue is you are not keeping the thread around. See here: How do I do an Asychronous NSURLConnection inside an NSOperation?

0
votes

Joel is correct in the start method you should call the main method to be start on the main thread, or you need to keep alive the thread in wich the method id called.