Skip to content

Commit

Permalink
Execute networking and parsing code on a background thread
Browse files Browse the repository at this point in the history
Fixes 0xced#147
  • Loading branch information
0xced committed Jun 5, 2015
1 parent f96619a commit bcf3e52
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 25 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
#### Version 2.2.0

* Networking and parsing code is executed on a background thread for better performance. The `XCDYouTubeVideoOperation` class has changed from an asynchronous to a synchronous operation and must not be started on the main thread. (#147)

#### Version 2.1.3

* Adaptation to YouTube API change. (#144)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
36 changes: 33 additions & 3 deletions XCDYouTubeKit Tests/XCDYouTubeVideoOperationTestCase.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
// Copyright (c) 2013-2015 Cédric Luthi. All rights reserved.
//

#import <XCTest/XCTest.h>
#import "XCDYouTubeKitTestCase.h"

#import <XCDYouTubeKit/XCDYouTubeVideoOperation.h>

@interface XCDYouTubeVideoOperationTestCase : XCTestCase
@interface XCDYouTubeVideoOperationTestCase : XCDYouTubeKitTestCase
@end

@implementation XCDYouTubeVideoOperationTestCase
Expand All @@ -16,10 +16,40 @@ - (void) testWrongInitializer
XCTAssertThrowsSpecificNamed([[XCDYouTubeVideoOperation alloc] init], NSException, NSGenericException);
}

- (void) testIsAsynchronous
{
XCDYouTubeVideoOperation *operation = [[XCDYouTubeVideoOperation alloc] initWithVideoIdentifier:nil languageIdentifier:nil];
XCTAssertFalse(operation.isAsynchronous);
}

- (void) testIsConcurrent
{
XCDYouTubeVideoOperation *operation = [[XCDYouTubeVideoOperation alloc] initWithVideoIdentifier:nil languageIdentifier:nil];
XCTAssertTrue(operation.isConcurrent);
XCTAssertFalse(operation.isConcurrent);
}

- (void) testStartingOnMainThread
{
XCDYouTubeVideoOperation *operation = [[XCDYouTubeVideoOperation alloc] initWithVideoIdentifier:nil languageIdentifier:nil];
XCTAssertTrue([NSThread isMainThread]);
XCTAssertThrowsSpecificNamed([operation start], NSException, NSGenericException);
}

- (void) testStartingOnBackgroundThread
{
XCDYouTubeVideoOperation *operation = [[XCDYouTubeVideoOperation alloc] initWithVideoIdentifier:nil languageIdentifier:nil];
[self keyValueObservingExpectationForObject:operation keyPath:@"isFinished" handler:^BOOL(id observedObject, NSDictionary *change)
{
XCTAssertNil([observedObject video]);
XCTAssertNotNil([observedObject error]);
return YES;
}];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
XCTAssertFalse([NSThread isMainThread]);
[operation start];
});
[self waitForExpectationsWithTimeout:5 handler:nil];
}

@end
4 changes: 3 additions & 1 deletion XCDYouTubeKit/XCDYouTubeVideoOperation.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
/**
* XCDYouTubeVideoOperation is a subclass of `NSOperation` that connects to the YouTube API and parse the response.
*
* Use this class only if you are very familiar with `NSOperation` and need to manage dependencies between operations. Else you should use the higher level class `XCDYouTubeClient`.
* You should probably use the higher level class `XCDYouTubeClient`. Use this class only if you are very familiar with `NSOperation` and need to manage dependencies between operations.
*
* Since version 2.2.0 this operation is synchronous and must not be started on the main thread. Starting the operation on the main thread throws an exception.
*/
@interface XCDYouTubeVideoOperation : NSOperation <XCDYouTubeOperation>

Expand Down
31 changes: 10 additions & 21 deletions XCDYouTubeKit/XCDYouTubeVideoOperation.m
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ @interface XCDYouTubeVideoOperation () <NSURLConnectionDataDelegate, NSURLConnec
@property (atomic, strong) NSMutableData *connectionData;
@property (atomic, strong) NSMutableArray *eventLabels;

@property (atomic, assign) BOOL isExecuting;
@property (atomic, assign) BOOL isFinished;
@property (atomic, assign) BOOL keepRunning;

@property (atomic, strong) XCDYouTubeVideoWebpage *webpage;
@property (atomic, strong) XCDYouTubeVideoWebpage *embedWebpage;
Expand Down Expand Up @@ -111,7 +110,7 @@ - (void) startRequestWithURL:(NSURL *)url type:(XCDYouTubeRequestType)requestTyp

NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
objc_setAssociatedObject(connection, XCDYouTubeRequestTypeKey, @(requestType), OBJC_ASSOCIATION_RETAIN);
[connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
[connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[connection start];
self.connection = connection;
}
Expand Down Expand Up @@ -216,28 +215,19 @@ - (void) finishWithError

#pragma mark - NSOperation

+ (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key
- (void) main
{
SEL selector = NSSelectorFromString(key);
return selector == @selector(isExecuting) || selector == @selector(isFinished) || [super automaticallyNotifiesObserversForKey:key];
}

- (BOOL) isConcurrent
{
return YES;
}

- (void) start
{
if ([self isCancelled])
return;
if ([NSThread isMainThread])
@throw [NSException exceptionWithName:NSGenericException reason:@"XCDYouTubeVideoOperation must not be executed on the main thread." userInfo:nil];

XCDYouTubeLogInfo(@"Starting video operation: %@", self);

self.isExecuting = YES;

self.eventLabels = [[NSMutableArray alloc] initWithArray:@[ @"embedded", @"detailpage" ]];
[self startNextRequest];

self.keepRunning = YES;
while (self.keepRunning)
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}

- (void) cancel
Expand All @@ -253,8 +243,7 @@ - (void) cancel

- (void) finish
{
self.isExecuting = NO;
self.isFinished = YES;
self.keepRunning = NO;
}

#pragma mark - NSURLConnectionDataDelegate / NSURLConnectionDelegate
Expand Down

0 comments on commit bcf3e52

Please sign in to comment.