0

I'm trying to develop an app to receive data from a web service to be displayed in some views. In the first version of the app I've only one view controller and one GET request to the web service (by means of NSURLconnection): all the methods for the NSURLconnection are in the Viewcontroller and everything is working well.

Now I would need to add other views and make some other GET requests so I was thinking that the best thing is to apply the MVC pattern: in particular I have created a class (FV_Data) where I put all the GET requests and manage the NSURLconnections, while in each ViewController I call the method (in the FV_Data class) for the needed GET request.

My problem is how to return the array with the data from the web service to the ViewController that asked for the data: in my first tests, the NSURLconnection is correctly started and the array (in the connectionDidFinishLoading method) is filled with the data from the web service but in the View Controller the array is empty.

I've read different posts but I can't understand what I'm doing wrong.

This is the code that I've written (I omit the code in methods that work).

Thanks, Corrado

FV_Data.h

#import <Foundation/Foundation.h>

@interface FV_Data: NSObject {

    NSMutableData *responseStatistic;
    NSMutableData *responseGetStatus;

    NSURLConnection *connectionStatistic;
    NSURLConnection *connectionGetStatus;
}

-(NSArray *)richiediGetStatistic;
-(NSArray *)richiediGetStatus;


@property (nonatomic, retain) NSArray *ArrayStatistic;
@property (nonatomic, retain) NSArray *ArrayGetStatus;


@end

FV_Data.m

#import "FV_Data.h"

@implementation FV_Data

-(id)init {
    self = [super init];
    return self;
}

-(void)richiediGetStatistic{
    ...
}

-(void)richiediGetStatus{
    ...
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
...
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
   ...
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {

    if(connection == connectionStatistic){
        NSString *responseStatisticString = [[NSString alloc] initWithData:responseStatistic encoding:NSUTF8StringEncoding];
        self.ArrayStatistic = [responseStatisticString componentsSeparatedByString:@","];
    }

    else if(connection == connectionGetStatus){
        NSString *responseGetStatusString = [[NSString alloc] initWithData:responseGetStatus encoding:NSUTF8StringEncoding];
        self.ArrayGetStatus = [responseGetStatusString componentsSeparatedByString:@","];
    }
}

@end

FV_Live_ViewController.h

#import <UIKit/UIKit.h>
#import "FV_Data.h"

@interface FV_Live_ViewController : UIViewController {
    IBOutlet UILabel *energia;
}

@property (nonatomic, retain) FV_Data *PVOutputData;

@end

FV_Live_ViewController.m

#import "FV_Live_ViewController.h"

@implementation FV_Live_ViewController

-(void)viewWillAppear:(BOOL)animated{

    self.PVOutputData = [[FV_Data alloc] init];

    [self.PVOutputData richiediGetStatistic];

    energia.text = [self.PVOutputData.ArrayStatistic objectAtIndex:0];
}

@end
rmaddy
  • 314,917
  • 42
  • 532
  • 579
Corrado
  • 505
  • 1
  • 5
  • 18
  • I'd suggest you consider adopting Apple's established [naming conventions](https://developer.apple.com/library/ios/documentation/cocoa/conceptual/ProgrammingWithObjectiveC/Conventions/Conventions.html), specifically using camelCase, lower case first letter for variables, only using leading uppercase letters for classes, not using underscores, etc. – Rob Sep 24 '13 at 19:57

3 Answers3

2

Oh man... network request needs time to complete, so you should setup you view (energia.text) AFTER connectionDidFinishLoading:
I am sorry, but it seems you are inventing a bicycle. If you know how do the blocks work in Objective-C i suggest to use AFNetworking framework.
If you don't know how blocks work - read Apple doc about blocks first - it is really necessary to successfully develop for iOS / OS X

Petro Korienev
  • 4,007
  • 6
  • 34
  • 43
  • AFNetworking (or some operation-based approach) is certainly a better architecture than the one the OP is suggesting. Good suggestion. – Rob Sep 24 '13 at 19:02
1

Your connectionDidFinishLoading is successfully completing a download, but you don't appear to be doing anything to tell the view controller to do anything to update the user interface when the data requests are done. You want to do that, because the loading process takes place asynchronously. For example, if you initiate this process in viewDidLoad, the loading will, invariably, not completely until well after viewDidLoad finished.

You need to provide some mechanism by which FV_Data can inform FV_Live_ViewController that the request is complete, at which point you will have your view controller update the UI with the new data. The two typical approaches are:

  • Implement your own delegate-protocol pattern, a fairly traditional solution to this sort of problem, by which your FV_Data can inform the view controller that the request is done. See Delegation Pattern in the Cocoa Core Competencies). This is analogous to the NSURLConnectionDataDelegate methods that you've implemented in FV_Data, but in this case, rather than being a mechanism for the NSURLConnection to inform FV_Data of information, it would be a mechanism for FV_Data to inform FV_Live_ViewController.

  • Use completion blocks, a more contemporary solution, which FV_Live_ViewController can inform FV_Data what to do when the request is done. This is analogous to what you see in AFNetworking, or Cocoa methods like sendAsynchronousRequest.

But before you dive too much into either approach, I must say that I'm not crazy about the architecture by which you have FV_Data doing two separate requests, resulting in two sets of class properties and duplication of code in the methods. A slightly different class structure could minimize this redundant code (the current code only doubles the number of opportunities for mistakes).

Furthermore, I'll go a step further and second Petro's suggestion that you consider using a proven, existing, third party framework to manage these requests, such as AFNetworking. A NSOperation-based approach like AFNetworking offers numerous advantages, but an elegant implementation can be complicated (hence the suggestion to use AFNetworking which enjoys these advantages while keeping you out of the weeds of the implementation). And unfortunately, NSURLConnection methods like sendAsynchronousRequest look convenient, but suffer from real limitations.

We're happy to help you either way, but just a suggestion.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Thanks for the detailed answer. I'm still a little bit confused (still a lot to learn about blocks, ...) but I will try to study and implement this solution. – Corrado Sep 24 '13 at 21:13
  • @Corrado If you use AFNetworking, it uses blocks, but also makes it much easier than implementing your own block-based solution from scratch. AFNetworking will be a good way to gain some comfort in using blocks. Once you're really familiar with this pattern, then you can then contemplate implementing your own block-based implementations. Good luck. If there's anything we can do to help, just let us know. – Rob Sep 24 '13 at 21:28
-1

It looks like for what you're trying to do using sendAsynchronousRequest:queue:completionHandler: May be a better fit than implementing a delegate.

quellish
  • 21,123
  • 4
  • 76
  • 83