ios blocks wait to finish - objective-c-blocks

How do I wait for a block execution to finish? I am interested about this in the context of HTTP requests where the success response is executed in a block. In particular this occur in AFNetworking.
I have found
dispatch_sync but not sure how to use it.

FSNetworking is the best HTTP library, I ever seen.
FSNConnection supports a block execution to finish.
Here is an example
NSURL *url = ...; // required
NSDictionary *headers = ...; // optional
NSDictionary *parameters = ...; // optional
FSNConnection *connection =
[FSNConnection withUrl:url
method:FSNRequestMethodGET
headers:headers
parameters:parameters
parseBlock:^id(FSNConnection *c, NSError **error) {
return [c.responseData dictionaryFromJSONWithError:error];
}
completionBlock:^(FSNConnection *c) {
NSLog(#"complete: %#\n error: %#\n parseResult: %#\n", c, c.error, c.parseResult);
}
progressBlock:^(FSNConnection *c) {
NSLog(#"progress: %#: %.2f/%.2f", c, c.uploadProgress, c.downloadProgress);
}];
[connection start];

Related

How can I get NSURLSession to process completionHandler when running as a command-line tool

I am somewhat new to Objective-C. I have some PERL scripts that download files from a site that requires client certificate authentication. I would like to port those scripts from PERL to Objective-C command line tools that I can then run from Terminal. Based on my research I have concluded that NSURLSession should work for what I need to do. However, I have been unable to get NSURLSession to work in my command line tool. It seems to build fine with no errors, but does not return anything from the completionHandler. Further, I have put this same code into a Mac OS X App tool and it seems to work fine.
Here is my main.m file:
#import <Foundation/Foundation.h>
#import "RBC_ConnectDelegate.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
// insert code here...
NSURL *myURL =[NSURL URLWithString:#"http://google.com"];
//[[[RBC_Connect alloc] init] connectGetURLSynch];
RBC_ConnectDelegate *myConnect = [[RBC_ConnectDelegate alloc] init];
[myConnect GetURL2: myURL];
}
return 0;
}
Here is my Implementation File:
#import <Foundation/Foundation.h>
#interface RBC_ConnectDelegate : NSObject
- (void)GetURL2:(NSURL *)myURL;
#property(nonatomic,assign) NSMutableData *receivedData;
//<==== note use assign, not retain
//do not release receivedData in a the dealloc method!
#end
And here is my implementation file:
#import "RBC_ConnectDelegate.h"
#implementation RBC_ConnectDelegate
- (void)GetURL2:(NSURL *)myURL2{
//create semaphore
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
// Create the request.
NSLog(#"Creating Request");
NSURLRequest *theRequest =
[NSURLRequest requestWithURL:myURL2
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:10.0];
NSLog(#"Creating Session");
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: nil delegateQueue: [NSOperationQueue mainQueue]];
NSLog(#"Initializing Data Task");
NSURLSessionDataTask * dataTask = [defaultSession dataTaskWithRequest:theRequest
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(#"CompletionHandler");
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSInteger myStatusCode = [(NSHTTPURLResponse *) response statusCode];
NSLog(#"Status Code: %ld", (long)myStatusCode);
}
if(error == nil)
{
NSString * text = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog(#"Data = %#",text);
}
else
{
NSLog(#"Error");
}
dispatch_semaphore_signal(semaphore);
}];
NSLog(#"Resuming Data Task");
[dataTask resume];
}
#end
As you can see, I am trying to get something very simple working here first, with the idea that I can then build on it. Everything I have looked at suggests this may be related to the fact that NSURLSession runs asynchronously but I have been unable to find a solution that speaks specifically to how to address this issue when building a command-line tool. Any direction anyone can provide would be appreciated.
cheers,
NSOperationQueue's reference says that for +mainQueue, the main thread's run loop controls the execution of operations, so you need a run loop. Add this to the end of your main:
while (1)
{
SInt32 res = 0;
#autoreleasepool
{
res = CFRunLoopRunInMode(kCFRunLoopDefaultMode, DBL_MAX, false);
}
if (kCFRunLoopRunStopped == res || kCFRunLoopRunFinished == res)
break;
}
To call -GetURL2:, use -[NSOperationQueue addOperation:] or dispatch_async. You can stop execution with CFRunLoopStop. I tested with GCD instead of NSOperations but this should work anyway.
Also, you need to call dispatch_semaphore_wait if you want to synchronize after the URL session is complete.

Data transfer between NSOperations

I would like to obtain the following: I have two NSOperations in a NSOperationQueue. The firs is a download from a website (gets some json data) the next is parsing that data. This are dependent operations.
I don't understand how to link them together. If they are both allocated and in the queue, how do I transfer the json string to the operation that parses it? Is it a problem if this queue is inside another NSOperationQueue that executes an NSOperation that consists of the two mentioned previously?
All I could find is transfers of data to a delegate on the main thread (performSelectorOnMainThread), but I need all this operations to execute in the background.
Thanks.
Code:
NSDownload : NSOperation
- (instancetype)initWithURLString:(NSString *)urlString andDelegate:(id<JSONDataDelegate>)delegate
{
self = [super init];
if (self) {
_urlStr = urlString;
_delegate = delegate; /// this needs to be a NSOPeration
_receivedData = [NSMutableData dataWithCapacity:256];
}
return self;
}
#pragma mark - OVERRIDE
- (void)main
{
#autoreleasepool {
if (self.isCancelled) {
return;
}
NSURL *url = [NSURL URLWithString:self.urlStr];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
self.urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
}
}
#pragma mark - NSURLConnectionDataDelegate
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
if (self.isCancelled) {
[connection cancel];
self.receivedData = nil;
return;
}
[self.receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (self.isCancelled) {
self.receivedData = nil;
return;
}
// return data to the delegate
NSDictionary *responseDict = #{JSON_REQUESTED_URL : self.urlStr,
JSON_RECEIVED_RESPONSE : self.receivedData};
[(NSObject *)self.delegate performSelectorOnMainThread:#selector(didReceiveJSONResponse:) withObject:responseDict waitUntilDone:NO]; // ok to uses performSelector as this data is not for use on the main thread ???
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// return error to the delegate
[(NSObject *)self.delegate performSelectorOnMainThread:#selector(didFailToReceiveDataWithError:) withObject:error waitUntilDone:NO];
}
#user1028028:
Use the following approach.
1) Maintain the operation queue reference that you are using to add DownloadOperation.
2) In connectionDidFinishLoading method, create ParseOperation instance, set the json data and add it to operation queue. Maintain ParseOperation strong reference variable in DownloadOperation and handling of cancelling of parsing operation through DownloadOperation interface.
3) After completed parsing call the UI functionality in main thread.
I hope this helps.
As lucianomarisi notes, it would usually be best to just have the first operation generate the second operation. This is usually simpler to manage. Operation dependencies aren't really that common in my experience.
That said, it's of course possible to pass data between operations. For instance, you could create a datasource property on the second operation. That would be the object to ask for its data; that object would be the first operation. This approach may require locking, though.
You can also create a nextOp property on the first operation. When it completes, it would call setData: on the second operation before exiting. You probably wouldn't need locking for this, but you might. In most cases it would be better for the first operation to just schedule the nextOp at this point (which again looks like lucianomarisi's answer).
The point is that an operation is just an object. It can have any methods and properties you want on it. And you can pass one operation to another.
Keep in mind that since an operation runs in the background, there's no reason you need to use the asynchronous interface to NSURLConnection. The synchronous API (sendSynchronousRequest:returningResponse:error: is fine for this, and much simpler to code. You could even use a trivial NSBlockOperation. Alternately, you can use the asynchronous NSURLConnection interface, but then you really don't need an NSOperation.
I also notice:
_receivedData = [NSMutableData dataWithCapacity:256];
Is it really such a small piece of JSON data? It's hard to believe that this complexity is worth it to move such a small parsing operation to the background.
(As a side note, unless you know precisely the size of the memory, there's not usually much benefit to specifying a capacity manually. Even then it's not always clear that it's a benefit. I believe NSURLConnection is using dispatch data under the covers now, so you're actually requesting a memory block that will never be used. Of course Cocoa also won't allocate it because it optimizes that out... the point is that you might as well just use [NSMutableData data]. Cocoa is quite smart about these kinds of things; you generally can only get in the way of its optimizations.)
As Rob said, unless you have any particular reason to use operations use the synchronized call. Then perform the selector on MainThread or on any other thread you need. Unless you want to separate the retrieval and parsing in separate operations or thread (explicitly).
Here is the code I was using for json retrieval and parsing:
-(BOOL) loadWithURL:(NSString*) url params: (NSDictionary*) params andOutElements:(NSDictionary*) jElements
{
NSError *reqError = nil;
NSString* urlStr = #"";//#"http://";
urlStr = [urlStr stringByAppendingString:url];
NSURL* nsURL = [NSURL URLWithString:urlStr];
//Private API to bypass certificate ERROR Use only for DEBUG
//[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[nsURL host]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: nsURL
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
[request setHTTPMethod:#"POST"];
NSString *postString = #"";
if(params!=nil) {
NSEnumerator* enumerator = params.keyEnumerator;
NSString* aKey = nil;
while ( (aKey = [enumerator nextObject]) != nil) {
NSString* value = [params objectForKey:aKey];
//Use our own encoded implementation instead of above Apple one due to failing to encode '&'
NSString* escapedUrlString =[value stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
//Required to Fix Apple bug with not encoding the '&' to %26
escapedUrlString = [escapedUrlString stringByReplacingOccurrencesOfString: #"&" withString:#"%26"];
//this is custom append method. Please implement it for you -> the result should be 'key=value' or '&keyNotFirst=value'
postString = [self appendCGIPairs:postString key:aKey value:escapedUrlString isFirst:false];
}
}
//************** Use custom enconding instead !!!! Error !!!!! **************
[request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&reqError];
if(reqError!=nil) {
NSLog(#"SP Error %#", reqError);
return NO;
}
NSString *json_string = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
//Handles Server Errors during execution of the web service that handles the call.
if([json_string hasPrefix:#"ERROR"] == YES){
NSLog(#"SP Error %#", lastError);
return NO;
}
//Very Careful!!!!!! Will stop for any reason.!!!!!!
//Handles errors from IIS Server that serves teh request.
NSRange range = [json_string rangeOfString:#"Runtime Error"];
if(range.location != NSNotFound) {
NSLog(#"SP Error %#", lastError);
return NO;
}
//Do the parsing
jElements = [[parser objectWithString:json_string error:nil] copy];
if([parser error] == nil) {
NSLog(#"Parsing completed");
} else {
jElements = nil;
NSLog(#"Json Parser error: %#", parser.error);
NSLog(#"Json string: %#", json_string);
return NO;
}
//Parsed JSON will be on jElements
return YES;
}

How to get array of images from network faster? (iOS)

Basically, I have an array of urls as strings, and as I loop through this array, if the element is a url for an image, I want to turn that url into a UIImage object and add it to another array. This is very slow though since I have to request the data for each URL. I've tried using dispatch_async as I show below but it doesn't seem to make any difference at all.
The key is that when I add these objects to my other array, whether they are images or something else they have to stay in order. Can anyone offer any guidance?
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i=0; i<[slides count]; i++){
__block NSString *mediaURLString = [primaryPhoto objectForKey:#"url"];
if ([self mediaIsVideo:mediaURLString]){
***some code***
}
else{ //if media is an image
dispatch_group_async(group, queue, ^{
mediaURLString = [mediaURLString stringByAppendingString:#"?w=1285&h=750&q=150"];
NSURL *url = [NSURL URLWithString:mediaURLString];
[mutableMedia addObject:url];
NSURL *url = ((NSURL *)self.mediaItem);
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response;
NSError *error;
NSData *urlData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
UIImage *image = [[UIImage alloc] initWithData:urlData];
[mutableMedia replaceObjectAtIndex:i withObject:image];
});
}
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
try this:
[self performSelectorInBackground:#selector(WebServiceCallMethod) withObject:nil];
and create one method like this
-(void)WebServiceCallMethod
{
mediaURLString = [mediaURLString stringByAppendingString:#"?w=1285&h=750&q=150"];
NSURL *url = [NSURL URLWithString:mediaURLString];
[mutableMedia addObject:url];
NSURL *url = ((NSURL *)self.mediaItem);
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response;
NSError *error;
NSData *urlData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
UIImage *image = [[UIImage alloc] initWithData:urlData];
[mutableMedia replaceObjectAtIndex:i withObject:image];
}
Hope it Helps!!
Do yourself a favor and don't use +sendSynchronousRequest:... Try something like this instead:
dispatch_group_t group = dispatch_group_create();
for (int i=0; i<[slides count]; i++)
{
__block NSString *mediaURLString = [primaryPhoto objectForKey:#"url"];
if ([self mediaIsVideo:mediaURLString]){
***some code***
}
else
{
//if media is an image
mediaURLString = [mediaURLString stringByAppendingString:#"?w=1285&h=750&q=150"];
NSURL *url = [NSURL URLWithString:mediaURLString];
[mutableMedia addObject:url];
NSURL *url = ((NSURL *)self.mediaItem);
NSURLRequest *request = [NSURLRequest requestWithURL:url];
dispatch_group_enter(group);
[NSURLConnection sendAsynchronousRequest: request queue: [NSOperationQueue mainQueue] completionHandler:
^(NSURLResponse *response, NSData *data, NSError *connectionError)
{
if (data.length && nil == connectionError)
{
UIImage *image = [[UIImage alloc] initWithData:data];
[mutableMedia replaceObjectAtIndex:i withObject:image];
}
dispatch_group_leave(group);
}];
}
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// Do stuff here that you want to have happen after all the images are loaded.
});
This will start asynchronous requests for all your URLs. When each request finishes, it will run its completion handler which will update your array, and when all requests have finished, the block in the dispatch_group_notify call will be executed.
This approach has the advantage that you can call it from the main thread, all individual completion blocks will be run on the main thread (thus ensuring thread-safety for the mutableMedia array (at least as far as this code goes)) and the final completion block will also be run on the main thread, so you can do whatever you need to update the UI directly.
There is a nifty solution using dispatch lib. The code below should stand for itself.
The basic idea is that an array contains "input" objects which each will be "transformed" via an asynchronous unary task - one after the other. The final result of the whole operation is an array of the transformed objects.
Everything here is asynchronous. Every eventual result will be passed in a completion handler which is a block where the result is passed as a parameter to the call-site:
typedef void (^completion_t)(id result);
The asynchronous transform function is a block which takes the input as a parameter and returns a new object - via a completion handler:
typedef void (^unary_async_t)(id input, completion_t completion);
Now, the function transformEach takes the input values as an NSArray parameter inArray, the transform block as parameter transform and the completion handler block as parameter completion:
static void transformEach(NSArray* inArray, unary_async_t transform, completion_t completion);
The implementation is a follows:
static void do_each(NSEnumerator* iter, unary_async_t transform,
NSMutableArray* outArray, completion_t completion)
{
id obj = [iter nextObject];
if (obj == nil) {
if (completion)
completion([outArray copy]);
return;
}
transform(obj, ^(id result){
[outArray addObject:result];
do_each(iter, transform, outArray, completion);
});
}
static void transformEach(NSArray* inArray, unary_async_t transform,
completion_t completion) {
NSMutableArray* outArray = [[NSMutableArray alloc] initWithCapacity:[inArray count]];
NSEnumerator* iter = [inArray objectEnumerator];
do_each(iter, transform, outArray, completion);
}
And build and run the following example
int main(int argc, const char * argv[])
{
#autoreleasepool {
// Example transform:
unary_async_t capitalize = ^(id input, completion_t completion) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
sleep(1);
if ([input respondsToSelector:#selector(capitalizedString)]) {
NSLog(#"processing: %#", input);
NSString* result = [input capitalizedString];
if (completion)
completion(result);
}
});
};
transformEach(#[#"a", #"b", #"c"], capitalize, ^(id result){
NSLog(#"Result: %#", result);
});
sleep(10);
}
return 0;
}
will print this to the console:
2013-07-31 15:52:49.786 Sample2[1651:1603] processing: a
2013-07-31 15:52:50.789 Sample2[1651:1603] processing: b
2013-07-31 15:52:51.792 Sample2[1651:1603] processing: c
2013-07-31 15:52:51.793 Sample2[1651:1603] Result: (
A,
B,
C
)
You can easily create a category for NSArray which implements, say a
-(void) asyncTransformEachWithTransform:(unary_async_t)transform
completion:(completion_t)completionHandler;
method.
Have fun! ;)
Edit:
IFF you ask how this applies to your problem:
The array of URLs is the input array. In order to create the transform block, simply wrap your asynchronous network request in an asynchronous method, say:
`-(void) fetchImageWithURL:(NSURL*)url completion:(completion_t)completionHandler;`
Then wrap method fetchImageWithURL:completion: into a appropriate transform block:
unary_async_t fetchImage = ^(id url, completion_t completion) {
[self fetchImageWithURL:url completion:^(id image){
if (completion)
completion(image); // return result of fetch request
}];
};
Then, somewhere in your code (possible a view controller) assuming you implemented the category for NSArray, and your array of urls is property urls:
// get the images
[self.urls asyncTransformEachWithTransform:fetchImage completion:^(id arrayOfImages) {
// do something with the array of images
}];

Why i get timeout erros when page is not available

I use NSString method initWithContentsOfURL:usedEncoding:error: to get content of some page. I notice, when page that i try to access is not exist this method is executed long time and then fail with timeout error. I tried to use NSURLRequest and NSURLConnection classes for the same purposes, but get the same result - execution long time and then timeout error.
When i try to open the same page in browser, i get response more quickly and it returns page is not available error.
It looks like cocoa methods don't do a dns resolution for page name, or they have longer timeout for that operation.
So my question, does cocoa method that i use do dns resolve? How to do that if they didn't?
Samples of code i use:
NSURL* url = [NSURL URLWithString:#"http://unexisting.domain.local"];
NSError* err = nil;
NSString* content = [NSString stringWithContentsOfURL:url usedEncoding:nil error:&err];
if (err) {
NSLog(#"error: %#", err);
} else {
NSLog(#"content: %#", content);
}
NSURL* url = [NSURL URLWithString:#"http://unexisting.domain.local"];
NSURLRequest* request = [NSURLRequest requestWithURL:url];
NSURLResponse* response = nil;
NSError* err = nil;
NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err];
if (err) {
NSLog(#"error: %#", err);
} else {
NSString* content = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"content: %#", content);
}
Thanks!
Gene M. answered how to do that with using of SCNetworkReachability. Here is sample code:
bool success = false;
const char *host_name = [#"stackoverflow.com"
cStringUsingEncoding:NSASCIIStringEncoding];
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL,
host_name);
SCNetworkReachabilityFlags flags;
success = SCNetworkReachabilityGetFlags(reachability, &flags);
bool isAvailable = success && (flags & kSCNetworkFlagsReachable) &&
!(flags & kSCNetworkFlagsConnectionRequired);
if (isAvailable) {
NSLog(#"Host is reachable: %d", flags);
}else{
NSLog(#"Host is unreachable");
}
It's definitely good practice to monitor the reachability (device connectivity) and react to it as noted above.
You can also implement the NSURLConnectionDelegate protocol and its methods such as
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(#"**ERROR** %#", error);
// Respond to error
}
If you're not connected you should get an NSURLConnection error code of 999 and NSURLErrorCancelled = -999 and/or a NSURLErrorNotConnectedToInternet = -1009 if you're not connected, etc. At least you'd have a report back of what's going on.
Docs:
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Constants/Reference/reference.html

HTTP server works in Cocoa application but not test case -- run loop issue?

I'm trying to add a GHUnit test case to this SimpleHTTPServer example. The example include a Cocoa application that works fine for me. But I can't duplicate the behavior in a test case.
Here is the test class:
#import <GHUnit/GHUnit.h>
#import "SimpleHTTPServer.h"
#interface ServerTest : GHTestCase
{
SimpleHTTPServer *server;
}
#end
#implementation ServerTest
-(void)setUpClass
{
[[NSRunLoop currentRunLoop] run];
}
- (NSString*)requestToURL:(NSString*)urlString error:(NSError**)error
{
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:1];
NSURLResponse *response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:error];
NSString *page = nil;
if (error == nil)
{
NSStringEncoding responseEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)[response textEncodingName]));
page = [[NSString alloc] initWithData:data encoding:responseEncoding];
[page autorelease];
}
return page;
}
- (void)testPortReuse
{
unsigned int port = 50001;
NSError *error = nil;
NSString *path, *url;
server = [[SimpleHTTPServer alloc] initWithTCPPort:port delegate:self];
sleep(10);
path = #"/x/y/z";
url = [NSString stringWithFormat:#"http://localhost:%u%#", port, path];
[self requestToURL:url error:&error];
GHAssertNil(error, #"%# : %#", url, error);
[server release];
}
- (void)processURL:(NSURL *)path connection:(SimpleHTTPConnection *)connection
{
NSLog(#"processURL");
}
- (void)stopProcessing
{
NSLog(#"stopProcessing");
}
#end
I've tried sending requests via NSURLRequest and also (during the sleep) via a web browser. The delegate methods -processURL and -stopProcessing are never called. The problem seems to be that [fileHandle acceptConnectionInBackgroundAndNotify] in SimpleHTTPServer -initWithTCPPort:delegate: is not causing any NSFileHandleConnectionAcceptedNotifications to reach the NSNotificationCenter -- so I suspect a problem involving run loops.
The problem seems to be with the NSFileHandle, not the NSNotificationCenter, because when [nc postNotificationName:NSFileHandleConnectionAcceptedNotification object:nil] is added to the end of initWithTCPPort:delegate:, the NSNotificationCenter does get the notification.
if (error == nil)
That should be:
if (data != nil)
error here is the passed-in pointer to an NSError* - it will only be nil if the caller passed nil instead of a reference to an NSError* object, which isn't what your -testPortReuse method does.
It would also be incorrect to dereference it (as in if (*error == nil)), because error arguments are not guaranteed to be set to nil upon error. The return value indicates an error condition, and the value returned in the error argument is only meaningful or reliable if there is an error. Always check the return value to determine if an error happened, then check the error parameter for details only if something did in fact go wrong.
In other words, as it's written above, your -requestToURL:error: method is incapable of handling success. Much like Charlie Sheen. :-)