Overview
Say we have a class called SPFPriceFetcher that uses JCDHTTPConnection.
SPFPriceFetcher
|
|—JCDHTTPConnection
JCDHTTPConnection uses NSURLConnection connection where any data, finish loading, or response received would have to give feedback BACK to SPFPriceFetcher. We give feedback by retaining and using block definitions passed in from SPFPriceFetcher.
Passing block definitions from parent (SPFPriceFetcher) to child (JCDHTTPConnection)
Basically what happens is that SPFPriceFetcher provides block definitions for JCDHTTPConnection to retain and use:
SPFPriceFetcher — OnSuccess block definition —-> JCDHTTPConnection
SPFPriceFetcher — OnFailure block definition —-> JCDHTTPConnection
SPFPriceFetcher — OnDidSendData block definition —-> JCDHTTPConnection
where the block interface is defined in JCDHTTPConnection.h as:
1 2 3 4 5 |
typedef void (^OnSuccess) (NSHTTPURLResponse *response, NSString *bodyString); typedef void (^OnFailure) (NSHTTPURLResponse *response, NSString *bodyString, NSError *error); typedef void (^OnDidSendData) (NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite); |
Using blocks
1) When you declare a method interface that takes such block definitions, you need to have the block interface as defined by the typedefs above.
JCDHTTPConnection.h
1 2 3 4 5 6 7 8 9 10 11 12 |
@interface JCDHTTPConnection : NSObject @property (copy, nonatomic) OnSuccess onSuccess; @property (copy, nonatomic) OnFailure onFailure; @property (copy, nonatomic) OnDidSendData onDidSendData; - (BOOL)executeRequestOnSuccess:(OnSuccess)onSuccessBlock failure:(OnFailure)onFailureBlock didSendData:(OnDidSendData)onDidSendDataBlock; @end |
Then in your block definition, you would use the block definitions as a variable. In our definition, we retain them:
JCDHTTPConnection.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
- (BOOL)executeRequestOnSuccess:(OnSuccess)onSuccessBlock failure:(OnFailure)onFailureBlock didSendData:(OnDidSendData)onDidSendDataBlock { //gives 3 block definitions to be retained and thus, be used later. NSLog(@"assign the callbacks...so later, we can trigger the callbacks"); self.onSuccess = onSuccessBlock; self.onFailure = onFailureBlock; self.onDidSendData = onDidSendDataBlock; [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; NSLog(@"NSURLConnection, initWithRequest...RUN IT!"); NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:self.request delegate:self]; return connection != nil; } |
Now when the NSURLConnection runs to connectionDidFinishLoading, we use our retained block definitions and call them:
1 2 3 4 5 6 7 8 9 10 11 12 |
- (void)connectionDidFinishLoading:(NSURLConnection *)connection { NSLog(@"JCDHTTPConnection - connectionDidFinishLoading"); [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; if (self.onSuccess) { NSLog(@"JCDHTTPConnection - if there is a callback available via onSuccess"); NSLog(@"JCDHTTPConnection - call onSuccess with self.response --> NSHTTPURLResponse *response, self.body --> NSString *body"); self.onSuccess(self.response, self.body); } } |
For example, self.onSuccess will call the block definitions you retained that was passed in from SPFPriceFetcher earlier. It will pass the needed parmaters NSHTTPURLResponse (self.response) and NSString * (self.body) in for it to process.
In SPFPriceFetcher.m, we pass in the block definition like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
- (void)requestQuoteForSymbol:(NSString *)symbol withCallback:(SPFQuoteRequestCompleteBlock)callback { //blah blah blah [connection executeRequestOnSuccess: //define the OnSuccess block definition to be passed in ^(NSHTTPURLResponse *response, NSString *bodyString) { NSLog(@"SPFPriceFetcher - executeRequestOnSuccess"); if (response.statusCode == 200) { NSDecimalNumber *price = [NSDecimalNumber decimalNumberWithString:bodyString]; callback(YES, price); } else { callback(NO, nil); } } //define the OnFailure block definition to be passed in failure:^(NSHTTPURLResponse *response, NSString *bodyString, NSError *error) { callback(NO, nil); } didSendData:nil]; } |
As you can see, when we provide block definitions, we simply use “^” to denote that its a block, and then use the block interface and continue with the code implementation. We just have to make sure to match the block interface.
For example, in our case we know that OnSuccess block takes is defined as:
OnSuccess block definition
1 |
typedef void (^OnSuccess) (NSHTTPURLResponse *response, NSString *bodyString); |
and thus, we first denote “^” as a block, then match the block interface by having the parameters be NSHTTPURLResponse and NSString. We then write the method implementation.
OnSuccess block definition we pass in:
1 2 3 4 5 6 7 8 9 10 |
^(NSHTTPURLResponse *response, NSString *bodyString) { NSLog(@"SPFPriceFetcher - executeRequestOnSuccess"); if (response.statusCode == 200) { NSDecimalNumber *price = [NSDecimalNumber decimalNumberWithString:bodyString]; callback(YES, price); } else { callback(NO, nil); } } |
OnFailure block definition
Same thing with the OnFailure block definition.
We first note “^” to denote that its a block definition. Then we provide the interface needed by the OnFailure block definition as shown:
1 |
typedef void (^OnFailure) (NSHTTPURLResponse *response, NSString *bodyString, NSError *error); |
OnFailure block definition we pass in:
1 2 3 4 5 |
//define the OnFailure block definition to be passed in failure:^(NSHTTPURLResponse *response, NSString *bodyString, NSError *error) { callback(NO, nil); } |