POSInputStreamLibrary contains NSInputStream
implementation which uses ALAsset
as its data source. The main features of POSBlobInputStream
are the following:
- Synchronous and asynchronous working modes.
- Autorefresh after
ALAsset
invalidation. - Smart caching of
ALAsset
while reading its data. - Using
NSStreamFileCurrentOffsetKey
property for read offset specification.
The category for NSInputStream
defines initializers for the most common cases:
@interface NSInputStream (POS)
+ (NSInputStream *)pos_inputStreamWithAssetURL:(NSURL *)assetURL;
+ (NSInputStream *)pos_inputStreamWithAssetURL:(NSURL *)assetURL asynchronous:(BOOL)asynchronous;
+ (NSInputStream *)pos_inputStreamForCFNetworkWithAssetURL:(NSURL *)assetURL;
@end
All of them require NSURL
of ALAsset
. POSBlobInputStream
will query
ALAssetLibrary for ALAsset
during the opening.
In sync mode all methods of POSInputStream
completely perform their work during
the call. If it is necessary to obtain some data from ALAssetLibrary
the
calling thread will be blocked. This makes possible to work with a stream without
subscribing to its events, but at the same time neither method of NSInputStream
should be called from the main thread. The reason is that ALAssetLibrary
interacts with the client code in the main thread. Thus there will be a deadlock if
POSBlobInputStream
waits the answer from ALAssetLibrary
in a blocked main
thread. Here is an example of POSBlobInputStream
usage in a sync mode for
calculating checksum of ALAsset
.
NSInputStream *stream = [NSInputStream pos_inputStreamWithAssetURL:assetURL asynchronous:NO];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[stream open];
if ([stream streamStatus] == NSStreamStatusError) {
// Error notification
[stream close];
return;
}
NSParameterAssert([stream streamStatus] == NSStreamStatusOpen);
while ([stream hasBytesAvailable]) {
uint8_t buffer[kBufferSize];
const NSInteger readCount = [stream read:buffer maxLength:kBufferSize];
if (readCount < 0) {
break;
} else {
// Checksum update
}
}
if ([stream streamStatus] != NSStreamStatusAtEnd) {
// Error notification
}
[stream close];
}
In async mode all methods of POSBlobInputStream
return immediately after call.
Client code should provide a delegate to the stream to receive information about its
status. This is the only way to know when the stream opened, when it has data to read
and about errors. You can see async version of checksum calculation below.
@interface ChecksumCalculator () <NSStreamDelegate>
@end
@implementation ChecksumCalculator
- (void)calculateChecksumForStream:(NSInputStream *)aStream {
aStream.delegate = self;
[aStream open];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ @autoreleasepool {
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[aStream scheduleInRunLoop:runLoop forMode:NSDefaultRunLoopMode];
for (;;) { @autoreleasepool {
if (![runLoop runMode:NSDefaultRunLoopMode
beforeDate:[NSDate dateWithTimeIntervalSinceNow:kRunLoopInterval]]) {
break;
}
const NSStreamStatus streamStatus = [aStream streamStatus];
if (streamStatus == NSStreamStatusError || streamStatus == NSStreamStatusClosed) {
break;
}
}}
}});
}
#pragma mark - NSStreamDelegate
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
switch (eventCode) {
case NSStreamEventHasBytesAvailable: {
[self updateChecksumForStream:aStream];
} break;
case NSStreamEventEndEncountered: {
[self notifyChecksumCalculationCompleted];
[aStream close];
} break;
case NSStreamEventErrorOccurred: {
[self notifyErrorOccurred:[aStream streamError]];
[aStream close];
} break;
}
}
@end
POSBlobInputStream
provides pos_inputStreamForCFNetworkWithAssetURL
initializer
for NSURLRequest integration. It takes into account the following CFNetwork "features":
- CFNetwork works with a stream in a sync mode.
- CFNetowrk uses deprecated
CFReadStreamGetError
method to get error description from the stream. This action will crash the app because of the bug in a "toll-free bridging" implementation for NSInputStream. This is the reason whystreamStatus
method will never returnNSStreamStatusError
. More over,POSBlobInputStream
will not notify about its status change via C-callbacks. The only way to receive actual status of the stream is viaNSStreamDelagate
callback.