Weird NSURLSessionDownloadTask behavior over cellular (not wifi) - ios

I've enabled Background Modes with remote-notification tasks to download a small file (100kb) in background when the app receives a push notification.
I've configured the download Session using
NSURLSessionConfiguration *backgroundConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:sessionIdentifier];
[backgroundConfiguration setAllowsCellularAccess:YES];
self.backgroundSession = [NSURLSession sessionWithConfiguration:backgroundConfiguration
delegate:self
delegateQueue:[NSOperationQueue mainQueue]];
and activate it using
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[hostComponents URL]];
[request setAllowsCellularAccess:YES];
NSMutableData *bodyMutableData = [NSMutableData data];
[bodyMutableData appendData:[params dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:[bodyMutableData copy]];
_downloadTask = [self.backgroundSession downloadTaskWithRequest:request];
[self.downloadTask resume];
Now everything works correctly only if I'm connected over Wifi or over Cellular but with the iPhone connected with the cable to xCode, if I disconnect the iPhone and receive a push notification over cellular the code stop at [self.downloadTask resume]; line without call the URL.
The class that handles these operation is a NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSURLSessionTaskDelegate and so implements:
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
I've try to insert a debug line with a UILocalNotification (presented 'now') after the [self.downloadTask resume] but is called after 5 minutes and says that the self.downloadTask.state is 'suspended'
What is due this weird behavior ?

The documentation for NSURLSessionConfiguration Class Reference here:
https://developer.apple.com/Library/ios/documentation/Foundation/Reference/NSURLSessionConfiguration_class/Reference/Reference.html#//apple_ref/occ/instp/NSURLSessionConfiguration/discretionary
Says: for the discretionary property:
Discussion
When this flag is set, transfers are more likely to occur when plugged
into power and on Wi-Fi. This value is false by default.
This property is used only if a session’s configuration object was
originally constructed by calling the backgroundSessionConfiguration:
method, and only for tasks started while the app is in the foreground.
If a task is started while the app is in the background, that task is
treated as though discretionary were true, regardless of the actual
value of this property. For sessions created based on other
configurations, this property is ignored.
This seems to imply that if a download is started in the background the OS always has discretion as to whether and when to proceed with the download. It seems that the OS is always waiting for a wifi connection before completing these tasks.
My experience supports this conjecture. I find that I can send several notifications for downloads while device is on cellular. They remain stuck. When I switch the device to wifi they all go through.

I got the same problem ,finally i set
configuration.discretionary = NO;
the everything works fine,
for backgroundConfiguration ,
discretionary = YES default, it seems the task begin in connection with WIFI and battery both. hope helpful

What are you doing in
(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{}
Are you calling the completionHandler right away before your download completes? I believe that doing this does not affect operation in Wifi mode or when connected to Xcode. But somehow when in background on Cellular it makes the download stall until you go to Wifi.

The only real way around this is to dump NSURLSession when the app is in the background and use CF sockets. I can successfully do HTTP requests over cellular while the app is in background if I use CFStreamCreatePairWithSocketToHost to open CFStream
#import "Communicator.h"
#implementation Communicator {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
NSInputStream *inputStream;
NSOutputStream *outputStream;
CompletionBlock _complete;
}
- (void)setupWithCallBack:(CompletionBlock) completionBlock {
_complete = completionBlock;
NSURL *url = [NSURL URLWithString:_host];
//NSLog(#"Setting up connection to %# : %i", [url absoluteString], _port);
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef)[url host], _port, &readStream, &writeStream);
if(!CFWriteStreamOpen(writeStream)) {
NSLog(#"Error, writeStream not open");
return;
}
[self open];
//NSLog(#"Status of outputStream: %lu", (unsigned long)[outputStream streamStatus]);
return;
}
- (void)open {
//NSLog(#"Opening streams.");
inputStream = (__bridge NSInputStream *)readStream;
outputStream = (__bridge NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
}
- (void)close {
//NSLog(#"Closing streams.");
[inputStream close];
[outputStream close];
[inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream setDelegate:nil];
[outputStream setDelegate:nil];
inputStream = nil;
outputStream = nil;
}
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)event {
//NSLog(#"Stream triggered.");
switch(event) {
case NSStreamEventHasSpaceAvailable: {
if(stream == outputStream) {
if (_complete) {
CompletionBlock copyComplete = [_complete copy];
_complete = nil;
copyComplete();
}
}
break;
}
case NSStreamEventHasBytesAvailable: {
if(stream == inputStream) {
//NSLog(#"inputStream is ready.");
uint8_t buf[1024];
NSInteger len = 0;
len = [inputStream read:buf maxLength:1024];
if(len > 0) {
NSMutableData* data=[[NSMutableData alloc] initWithLength:0];
[data appendBytes: (const void *)buf length:len];
NSString *s = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
[self readIn:s];
}
}
break;
}
default: {
//NSLog(#"Stream is sending an Event: %lu", (unsigned long)event);
break;
}
}
}
- (void)readIn:(NSString *)s {
//NSLog(#"reading : %#",s);
}
- (void)writeOut:(NSString *)s{
uint8_t *buf = (uint8_t *)[s UTF8String];
[outputStream write:buf maxLength:strlen((char *)buf)];
NSLog(#"Writing out the following:");
NSLog(#"%#", s);
}
#end

Related

NSStream refusing to reconnect once disconnected (Getting "error 61 - Connection Refused")

I'm currently creating a chat app based off of Rey Wenderlich's chat app tutorial which is going great except I can't seem to reconnect without having to run the app from Xcode again.
It goes something like this: run server -> run app (At this point, everything is great) then I stop server -> run server again at which point my streams refuse to reconnect and keep giving me the erorr: Error Domain=NSPOSIXErrorDomain Code=61 "Connection refused".
Any ideas why this is?
Method to init streams:
- (void)initStreams {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)SERVER_IP, port, &readStream, &writeStream);
inputStream = (__bridge NSInputStream *)readStream;
outputStream = (__bridge NSOutputStream *)writeStream;
inputStream.delegate = self;
outputStream.delegate = self;
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
}
My delegate
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
NSString *streamName = #"Output Stream";
if (aStream == inputStream) {
streamName = #"Input Stream";
}
switch (eventCode) {
case NSStreamEventOpenCompleted:
NSLog(#"%# opened! (%lu)",streamName, eventCode);
[_delegate socketDidOpenSuccessfully];
break;
case NSStreamEventHasBytesAvailable:
[self readDataFromStream:aStream];
break;
case NSStreamEventErrorOccurred:
NSLog(#"Error: %#",[aStream streamError]);
[self resetStreams];
break;
case NSStreamEventEndEncountered:
NSLog(#"%# read ended! (%lu)",streamName, eventCode);
[self resetStreams];
break;
default:
NSLog(#"Unknown %#: %d",streamName, (int)eventCode);
break;
}
}
Reset streams Method:
- (void)resetStreams {
NSLog(#"Reseting!");
toSend = [NSMutableArray new];
_loginStatus = LTLoginStatusLoggedOut;
[inputStream close];
[outputStream close];
[self performSelector:#selector(initStreams) withObject:self afterDelay:2];
}
So after hours of frustration, I decided to just wrap it in another object and re instantiate that whenever it gets disconnected. Seems to work whereas re-initiating the stream does not.

Recovering a lost connection using sockets

I'm trying to dig deeper by using this example of an iPhone Chart Server and all is working as expected.
What I wanted to learn next is how to recover when the connection to the server is lost (for whatever reason) while the app is running.
First issue is that the app tries to open the input & output streams concurrently thus if I implement an alert, I get it two times. I managed to resolve this by closing the streams if there is an error.
The second is that if I'm another view controller and the connection is lost I can't seem to be able to recover it.I call the sendSocketMessage and if there is the error flat to try to use the initNetworkCommunication but I get a fatal error.
This 2nd issue is troubling me. I've added an All Exceptions breakpoint but got nothing. How I try to 'test' this is by making sure the server works and the app loads and connects. Then I shut-down the server, try a few clicks on the app and I get the alert. I switch-on the server and try to click again, I get the message sent to the server but then the app crashes with no info!
#implementation ViewController
bool connectionError = true;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)initNetworkCommunication {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (CFStringRef)#"192.168.1.1", 6035, &readStream, &writeStream);
inputStream = (__bridge NSInputStream *)readStream;
outputStream = (__bridge NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
}
- (void)closeAll {
NSLog(#"Closing streams.");
[inputStream close];
[outputStream close];
[inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream setDelegate:nil];
[outputStream setDelegate:nil];
inputStream = nil;
outputStream = nil;
}
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
switch (streamEvent) {
case NSStreamEventOpenCompleted:
NSLog(#"Stream opened");
break;
case NSStreamEventHasBytesAvailable:
connectionError = false;
if (theStream == inputStream) {
uint8_t buffer[1024];
long len;
NSMutableData *currentMessage;
currentMessage = [NSMutableData dataWithBytes:"" length:strlen("")];
while ([inputStream hasBytesAvailable]) {
len = [inputStream read:buffer maxLength:sizeof(buffer)];
[currentMessage appendBytes:buffer length:len];
}
NSData * nullByte = [NSMutableData dataWithLength:1];
len = currentMessage.length;
NSRange searchRange = {0, len};
for (NSRange r; (r = [currentMessage rangeOfData:nullByte options:0 range:searchRange]).length; ) {
NSString * message = [[NSString alloc] initWithBytes:currentMessage.bytes length:r.location encoding:NSUTF8StringEncoding];
searchRange.location = r.location+r.length;
searchRange.length = len - searchRange.location;
[self messageReceived:message];
}
[currentMessage replaceBytesInRange:(NSRange){0, searchRange.location} withBytes:NULL length:0];
}
break;
case NSStreamEventErrorOccurred:
NSLog(#"Can not connect to the host!");
connectionError = true;
[self closeAll];
[self connectionLost];
break;
case NSStreamEventEndEncountered:
break;
default:
NSLog(#"Unknown event");
}
}
- (void) connectionLost {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Alert!"
message:#"Connection to the server lost!"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
- (void) messageReceived:(NSString *)message {
[messages addObject:message];
if (inputStream.streamStatus == NSStreamStatusClosed || inputStream.streamStatus == NSStreamStatusError || inputStream.streamStatus == NSStreamStatusNotOpen) {
[self closeAll];
[self initNetworkCommunication];
}
// do things with the message...
}
- (void) initConnection {
[self initNetworkCommunication];
messages = [[NSMutableArray alloc] init];
}
- (IBAction)joinChat:(id)sender {
[self initConnection];
[self sendSocketMessage: #"iam:" message: _inputNameField.text];
}
- (void) sendSocketMessage:(NSString*) sendCommand message:(NSString*) sendMessage
{
if (outputStream.streamStatus == NSStreamStatusClosed || outputStream.streamStatus == NSStreamStatusError || outputStream.streamStatus == NSStreamStatusNotOpen) {
[self closeAll];
[self initNetworkCommunication];
}
NSString *response = [NSString stringWithFormat: #"%#%#", sendCommand, sendMessage];
NSData *data = [[NSData alloc] initWithData:[response dataUsingEncoding:NSASCIIStringEncoding]];
[outputStream write:[data bytes] maxLength:[data length]];
NSLog(#"clint sent: %#", response);
}
#end
The error I get is this screen grab:
int main(int argc, char * argv[]) {
#autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
Thread 1: EXC_BAD_ACCESS(code=1, address=0x0)
Any advice?
First issue:
I would use a strategy that involves pairing your alerts to connection objects. Since there is no built-in connection object in the API for dealing with CF streams, you'll have to create your own. Typically, a useful connection object would have properties for the associated NSInputStream and NSOutputStream. This approach should eliminate the problem of getting two alerts, since you will be showing an alert for the one connection object, instead of it's associated IO streams. This also encapsulates properties and operations neatly into one connection object, which you can separate from your view controller. Another benefit to using a connection object is that you will have more flexibility in your view controllers' object graph, since you will be using the connection object through composition, instead of inheritance in view controller subclasses.
Second issue:
The main function is shown when you get the error, not because it is causing the problem, but rather it is the function at the end of the call stack--when an exception is thrown, the system exits each call in the stack until it is caught, or until it reaches the end of the call stack, which is typically the main function in Objective-C.
I would first go your breakpoints panel, and add a general breakpoint, by tapping on the + sign in the bottom left corner, and selecting Add Exception Breakpoint. This usually helps me, when it successfully shows the point in my code that initially threw an exception, instead of showing the main function, which isn't helpful. You don't need to edit this breakpoint once it is added. Sometimes it causes the debugger to stop unnecessarily, if there are other exceptions that are thrown and caught in the system. If this is the case, then you can simply disable the exception break point until you arrive at the point in question, and then manually re-enable the break point before initiating an operation.
Edit
Another approach that you can use, if the catch-all Exception break point fails, is to use an exception handler. You would typically apply this inside your application:didLaunchWithOptions: method, like this:
NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
And, implement your handler to print out some useful information:
void uncaughtExceptionHandler(NSException *exception) {
debugLog(#"CRASH: %#", exception);
debugLog(#"Stack trace: %#", [exception callStackSymbols]);
}

NSStream Delegate NSStreamEventHasBytesAvailable Not Being Called

I'm writing a messaging app that uses a NSStream to communicate with a server written in Python. The server works perfectly with a companion Python client. But when I connect to it with a NSStream, the NSInputStream doesn't seem to get any data. The NSOutputStream, however, works perfectly. I am opening the string like so:
-(void)openStream
{
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"tihmstar.dyndns.org", 80, &readStream, &writeStream);
inputStream = (__bridge_transfer NSInputStream *)readStream;
outputStream = (__bridge_transfer NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
[self auth];
[[NSNotificationCenter defaultCenter] postNotificationName:#"InitCompleted" object:nil];
}
The delegate method is as so:
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent
{
NSLog(#"Handle Event - ");
switch (streamEvent)
{
case NSStreamEventOpenCompleted:
NSLog(#"Stream opened");
break;
case NSStreamEventHasBytesAvailable:
NSLog(#"Bytes Available!");
if(theStream == inputStream)
{
NSLog(#"inputStream is ready.");
uint8_t buf[1024];
unsigned int len = 0;
len = [inputStream read:buf maxLength:1024];
if(len > 0)
{
NSMutableData* data=[[NSMutableData alloc] initWithLength:0];
[data appendBytes: (const void *)buf length:len];
NSString* string = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSLog(#"Server said- %#", string);
[self messageReceived:[string lowercaseString]];
}
}
break;
case NSStreamEventErrorOccurred:
NSLog(#"Can not connect to the host!");
break;
case NSStreamEventEndEncountered:
NSLog(#"End Encountered");
break;
case NSStreamEventHasSpaceAvailable:
NSLog(#"Space Availible.");
break;
default:
NSLog(#"Unknown event- %u", streamEvent);
}
}
My problem is that case NSStreamEventHasBytesAvailable is never called, and therefore the message from the server is never being received. Anyone have any solutions for this? I've found some related questions here on StackOverflow, but none of them have been answered.
Thanks in advance.
I was just looking into the code. try to remove delegate of NSInputStream. I was looking into the another Using NSXMLParser initWithStream: no parser delegate methods received
which deals with similar situation.
I don't know exactly the answer for your question but I know exactly that you shouldn't call
[self auth];
after NSStream open right away. You have to wait for
NSStreamEventHasSpaceAvailable
event for your NSOutputStream and write data inside it afterwards only.

How to schedule event in runloop in GCD

I'm trying to connect to a server in a custom GCD queue. This is how I'm doing it.
- (void) initNetworkCommunication{
if(!self.connQueue){
self.connQueue = dispatch_queue_create("connection_queue", NULL);
}
dispatch_async(self.connQueue, ^(void) {
if(self.inputStream ==nil && self.outputStream ==nil) {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
NSString *host= [[NSUserDefaults standardUserDefaults] objectForKey:#"ip_preference"];
NSNumber *portNum = [[NSUserDefaults standardUserDefaults] objectForKey:#"port_preference"];
int port = [portNum intValue];
CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream);
CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
self.inputStream = (__bridge NSInputStream *)readStream;
self.outputStream = (__bridge NSOutputStream *)writeStream;
[self.inputStream setDelegate:self];
[self.outputStream setDelegate:self];
[self.inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[self.outputStream open];
[self.inputStream open];
} else {
NSLog(#"persistant connection alerady opened");
return;
}
});
}
Now, if I write this piece of code in dispatch_sync, it calls delegate function correctly.
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent
But, when I use dispatch_asynch, which is what I want to do, it does not call my delegate function.
From what I've read on SO so far, GCD queues have a runloop associated with them but these are not run by the system and we need to do so ourselves. I understand this in theory, but but how is it done. Dispatch sources associated with this somehow?
Thank You in advance.
Add this method after [self.inputStream open];
[[NSRunLoop currentRunLoop]run];
This puts the receiver into a permanent loop, during which time it processes data from all attached input sources.
See apple docs about RunLoops
The other way let run on dispatch_get_main_queue when use dispatch_async();

Good Example of network-based NSStreams on a Separate Thread?

Does anyone know of a simple example that creates (network-based) NSStreams on a separate thread?
What I am actually trying to do is unschedule (from the main thread) and reschedule (to a helper/network thread) an open NSInputStream and NSOutputStream that I am receiving from a third party framework (see Can an open, but inactive, NSStream that is scheduled on the main thread be moved to a different thread?). Nobody has answered that question thus far, so I am trying to do this myself to see if it could be made to work.
To test what is possible, I am trying to modify the code here (which includes a iOS client and a very short, python-based server [awesome!]):
http://www.raywenderlich.com/3932/how-to-create-a-socket-based-iphone-app-and-server
so that after the NSInputStream and NSOutputStream is created and opened I attempt to move it onto a helper thread.
The challenge that I am experiencing is that the helper thread does not seem to be responding to delegate messages from the streams or any messages that I am sending via:
performSelector:onThread:withObject:waitUntilDone:modes:. I suspect that I am doing something wrong with setting up the helper thread's NSRunLoop (see networkThreadMain below).
So, [ChatClientViewController viewDidLoad] now looks like:
- (void)viewDidLoad {
[super viewDidLoad];
[self initNetworkCommunication];
[self decoupleStreamsFromMainThread];
[self spoolUpNetworkThread];
inputNameField.text = #"cesare";
messages = [[NSMutableArray alloc] init];
self.tView.delegate = self;
self.tView.dataSource = self;
}
With these implementations:
- (void) initNetworkCommunication {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"localhost", 80, &readStream, &writeStream);
inputStream = (NSInputStream *)readStream;
outputStream = (NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
}
- (void) decoupleStreamsFromMainThread
{
inputStream.delegate = nil;
outputStream.delegate = nil;
[inputStream removeFromRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
[outputStream removeFromRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
}
- (void) spoolUpNetworkThread
{
[NSThread detachNewThreadSelector: #selector(networkThreadMain) toTarget: self withObject: nil];
}
- (void) networkThreadMain
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Top-level pool
static dispatch_once_t once;
dispatch_once(&once, ^{
[self rescheduleThreads];
((ChatClientAppDelegate * )[[UIApplication sharedApplication] delegate]).networkThread = [NSThread currentThread];
[inputStream setDelegate:self];
[outputStream setDelegate:self];
NSLog(#"inputStream status is: %#", [NSInputStream streamStatusCodeDescription: [inputStream streamStatus]]);
NSLog(#"outputStream status is: %#", [NSOutputStream streamStatusCodeDescription: [outputStream streamStatus]]);
[[NSRunLoop currentRunLoop] runUntilDate: [NSDate distantFuture]];
});
[pool release]; // Release the objects in the pool.
}
- (void) rescheduleThreads
{
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
Any ideas on what might be wrong? Thanks in advance!
Something along the lines of the code in the question below works well ( I have similar code in my app):
How to properly open and close a NSStream on another thread

Resources