Search code examples
iosobjective-cxcodeafnetworkingblocking

IOS block ui thread until JSON parse function in singleton class finishes


I'm making an IOS application which should gather data on startup from a json feed. Basicaly the initial view controller is a loading screen. In the background, during that loading screen, it should instantiate my singleton class, and call a json feed to fill an array with objects. When this process is finished, it should automatically segue to my main menu.

Here's the weird thing. I've set this up and it works just fine, but only 4 out of 5 times. Sometimes it'll just go straight to the menu and load the feed in the background, which isn't what i want.

Can anyone find the culprit?

Here's my view controller for the loading screen:

#import "loadingViewController.h"
#import "Singleton.h"
#import "MBProgressHUD.h"

@interface loadingViewController ()

@end

@implementation loadingViewController

- (void)viewDidLoad {
    [super viewDidLoad];



    //[self performSegueWithIdentifier:@"launchSegue" sender:self];

    // Do any additional setup after loading the view.

    }

- (void)viewWillAppear:(BOOL)animated {

    Singleton *singletonManager = [Singleton singletonManager];

    [singletonManager loadJsonData];


}

- (void)viewDidAppear:(BOOL)animated {

    [self performSegueWithIdentifier:@"launchSegue" sender:self];

}

- (void)doSegue
{

}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskAll;
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/

@end

And this is my implementation in the singleton class:

implementation Singleton

@synthesize acts;

#pragma mark Singleton Methods

+ (id)singletonManager {
    static Singleton *singletonMyManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        singletonMyManager = [[self alloc] init];
    });
    return singletonMyManager;
}

- (id)init {
    if (self = [super init]) {
        //someProperty = [[NSString alloc] initWithString:@"Default Property Value"];
        self.acts = [[NSMutableArray alloc] init];
    }
    return self;
}

- (BOOL) loadJsonData
{



    NSString     *urlString  = @"http://wwww.paaspop.nl/json/gettimetable";
    NSString     *callback   = @"api";
    NSDictionary *parameters = @{@"callback": callback};

    AFHTTPRequestOperationManager* manager = [AFHTTPRequestOperationManager manager];
    manager.securityPolicy.allowInvalidCertificates = YES;
    manager.responseSerializer = [CDLJSONPResponseSerializer serializerWithCallback:callback];
    __block NSDictionary* response = nil;
    AFHTTPRequestOperation* operation = [manager
                                         GET: urlString
                                         parameters: parameters
                                         success:^(AFHTTPRequestOperation* operation, id responseObject){
                                             response = responseObject;
                                             [self parseJSONData:response];
                                         }
                                         failure:^(AFHTTPRequestOperation* operation, NSError* error){
                                             NSLog(@"Error: %@", error);}
                                         ];
    [operation waitUntilFinished];
    return true;  



}

-(void) parseJSONData:(id) JSON
{
    NSArray *allActs = [JSON objectForKey:@"acts"];

    for (NSDictionary* dict in allActs) {
        //Create a pirate object where the json data can be stored
        Act *act = [[Act alloc] init];
        NSString *url;
        //Get the JSON data from the dictionary and store it at the Pirate object
        act.title = [dict objectForKey:@"title"];
        act.subtitle = [dict objectForKey:@"add"];
        act.jsontitle = [dict objectForKey:@"url_title"];
        act.favorite = FALSE;

        url = [dict objectForKey:@"photo"];

        NSURL *imageURL = [NSURL URLWithString:[dict objectForKey:@"photo"]];
        NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
        act.photo = [self imageWithImage:[UIImage imageWithData:imageData] scaledToWidth:320];

        NSLog(@"%f, %f",act.photo.size.width,act.photo.size.height);


        //Add the pirates to the array


        [self.acts addObject:act];


    }

}

-(UIImage*)imageWithImage: (UIImage*) sourceImage scaledToWidth: (float) i_width
{
    float oldWidth = sourceImage.size.width;
    float scaleFactor = i_width / oldWidth;

    float newHeight = sourceImage.size.height * scaleFactor;
    float newWidth = oldWidth * scaleFactor;

    UIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight));
    [sourceImage drawInRect:CGRectMake(0, 0, newWidth, newHeight)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}

- (NSMutableArray *) getActs {
    return self.acts;
}


- (void)dealloc {
    // Should never be called, but just here for clarity really.
}

Solution

  • There is nothing here to prevent it from going right to the new screen. Really it works 1 in 5 times and the other 4 are the oddities.

    You have included MBProgressHud in your file but you are not doing anything with that. It would be better to leave the UI active then block the user from doing things and having the app appear un-responsive.

    In the download class you are relying on the waitUntilFinished. This will wait for the network operation to complete and then return. This is not waiting for the json parsing and image manipulation to occur.

    The AFHTTPRequest library is very outdated. It is a nice body of work but unless you have some very specific lower level needs it is far to involved. NSUrlSession is a newer tool from apple that very powerful and extremely simple to use.