It has been a long way. Here are some questions about this:
In the beginning, I abandoned the idea of saving and checking the remaining cache, because it would work only for the output stream, when further reflection suggested that the input stream could also be blocked.
Instead, I configured while-loops to idle:
- (void) stream:(NSStream *)theStream handleEvent:(NSStreamEvent)eventCode { switch (eventCode) // RECEIVING case NSStreamEventHasBytesAvailable: { if (self.receiveStage == kNothingToReceive) return; // Get the data from the stream. (This method returns NO if bytesRead < 1.) if (![self receiveDataViaStream:(NSInputStream *)theStream]) { // If nothing was actually read, consider the stream to be idling. self.bStreamIn_isIdling = YES; // Repeatedly retry read, until (1) the read is successful, or (2) stopNetwork is called, which will clear the idler. // (Just in case, add nil stream property as a loop breaker.) while (self.bStreamIn_isIdling && self.streamIn) { if ([self receiveDataViaStream:(NSInputStream *)theStream]) { self.bStreamIn_isIdling = NO; // The stream will have started up again; prepare for next event call. [self assessTransmissionStage_uponReadSuccess]; } } } else // Prepare for what happens next. [self assessTransmissionStage_uponReadSuccess]; break; // SENDING case NSStreamEventHasSpaceAvailable: if (self.sendStage == kNothingToSend) return; if (![self sendDataViaStream:(NSOutputStream *)theStream]) { self.bStreamOut_isIdling = YES; while (self.bStreamOut_isIdling && self.streamOut) { if ([self sendDataViaStream:(NSOutputStream *)theStream]) { self.bStreamOut_isIdling = NO; [self assessTransmissionStage_uponWriteSuccess]; } } } else [self assessTransmissionStage_uponWriteSuccess]; break; // other event cases…
Then it’s time to test the user’s canceled cancellation using the Cancel button. Halfway through the synchronization, there is a pause on the Cocoa side, awaiting user input. If the user cancels this point, the Cocoa application closes the streams and removes them from runloop, so I expected the streams on the other side of the connection to NSStreamEventEndEncountered events NSStreamEventEndEncountered or possibly NSStreamEventErrorOccurred . But no, only one event has passed, NSStreamEventHasBytesAvailable ! Hover over your mouse.
Of course, in fact there were no "available bytes" since the stream was closed on the Cocoa side and not written, so the stream handler on the iOS side went into an infinite loop. Not very good.
Then I checked what would happen if one of the devices went to sleep. During a pause for user input, I enable iPhone using automatic lock *, and then feeds user input to the Cocoa side. Surprise again: The Cocoa application continued uninterrupted until the end of the synchronization, and when I woke up with the iPhone, the iOS application also completed its part of the synchronization.
Could there be a hiccup on the side of the iPhone that was fixed by my idle cycle? I started the stop network procedure to check:
if (![self receiveDataViaStream:(NSInputStream *)theStream]) [self stopNetwork];
Synchronization continued until completion. There was no hiccups.
Finally, I checked what happened if the Mac (Cocoa side) slept during this pause for input. This caused a bounce back: two NSStreamEventErrorOccurred events were received - on the Mac side, after which there was no longer any write to the output stream. There were no events on the side of the iPhone, but if I tested the status of the iPhone stream, it would return 5, NSStreamStatusAtEnd.
CONCLUSIONS AND PLAN:
- A "temporary block" is a kind of unicorn. Either the network runs smoothly or is completely turned off.
- If there really is such a thing as a temporary block, there is no way to distinguish it from a complete shutdown. The only stream status constants that seem logical for the time block are
NSStreamStatusAtEnd and NSStreamStatusError . But in the above experiments, they indicate a separation. - As a result, Im discarding while-loops and detecting a trip only by checking for bytesRead / Written <1.
* iPhone will never sleep if it is subordinate to Xcode. You must run it directly from the iPhone.