I want that NSURLSession's delegation is called on the main thread. When I passed [NSOperationQueue mainQueue]
to delegateQueue
, the delegation was not called.
@interface NSURLSessionTests : XCTestCase<NSURLSessionDataDelegate>
{
@private
dispatch_semaphore_t _semaphore;
}
@end
@implementation NSURLSessionTests
- (void)testWithRequest{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString: @"http://example.com/"]];
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
delegate:self
delegateQueue:[NSOperationQueue mainQueue]];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
_semaphore = dispatch_semaphore_create(0);
[dataTask resume];
NSLog(@"Start: %@", dataTask);
dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
}
# pragma mark NSURLSessionDataDelegate
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
NSLog(@"didReceiveResponse: %@", NSThread.currentThread);
NSLog(@"%@", response);
completionHandler(NSURLSessionResponseAllow);
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
NSLog(@"didReceiveData: %@", NSThread.currentThread);
NSLog(@"%@", data);
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
NSLog(@"didCompleteWithError: %@", NSThread.currentThread);
NSLog(@"%@", error);
dispatch_semaphore_signal(_semaphore);
}
@end
The executed results are below.
Test Suite 'Selected tests' started at 2020-10-19 16:53:56.013
Test Suite 'Tests.xctest' started at 2020-10-19 16:53:56.014
Test Suite 'NSURLSessionTests' started at 2020-10-19 16:53:56.014
Test Case '-[NSURLSessionTests testWithRequest]' started.
2020-10-19 16:53:56.016936+0900 xctest[91856:6495007] Start: LocalDataTask <A9DFF949-ED27-4C5E-8E15-93113442D876>.<1>
It is freezing here. All the delegation methods aren't called.
if I pass nil
to delegateQueue
, it worked as expected.
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration
delegate:self
delegateQueue:nil];
but not on the main thread.
Test Suite 'Selected tests' started at 2020-10-19 17:10:03.227
Test Suite 'Tests.xctest' started at 2020-10-19 17:10:03.228
Test Suite 'NSURLSessionTests' started at 2020-10-19 17:10:03.228
Test Case '-[NSURLSessionTests testWithRequest]' started.
2020-10-19 17:10:03.230845+0900 xctest[91910:6506707] Start: LocalDataTask <44C2D9FA-09D3-4C25-9129-B164D451836D>.<1>
2020-10-19 17:10:03.629414+0900 xctest[91910:6506750] didReceiveResponse: <NSThread: 0x7f8d2281f480>{number = 2, name = (null)}
2020-10-19 17:10:03.629867+0900 xctest[91910:6506750] <NSHTTPURLResponse: 0x7f8d20f333e0> { URL: http://example.com/ } { Status Code: 200, Headers {
...
} }
2020-10-19 17:10:03.630215+0900 xctest[91910:6506750] didReceiveData: <NSThread: 0x7f8d2281f480>{number = 2, name = (null)}
2020-10-19 17:10:03.630459+0900 xctest[91910:6506750] #NSData 1KB(1256)
2020-10-19 17:10:03.630940+0900 xctest[91910:6506750] didCompleteWithError: <NSThread: 0x7f8d2281f480>{number = 2, name = (null)}
2020-10-19 17:10:03.631095+0900 xctest[91910:6506750] (null)
Test Case '-[NSURLSessionTests testWithRequest]' passed (0.403 seconds).
Test Suite 'NSURLSessionTests' passed at 2020-10-19 17:10:03.632.
Executed 1 test, with 0 failures (0 unexpected) in 0.403 (0.404) seconds
Test Suite 'Tests.xctest' passed at 2020-10-19 17:10:03.633.
Executed 1 test, with 0 failures (0 unexpected) in 0.403 (0.405) seconds
Test Suite 'Selected tests' passed at 2020-10-19 17:10:03.633.
Executed 1 test, with 0 failures (0 unexpected) in 0.403 (0.406) seconds
Program ended with exit code: 0
Why isn't the delegation called with [NSOperationQueue mainQueue]
? How do I work on the main thread?
2020/10/23 Edited
As commented, the Xctest code worked when I changed to XCTestExpectation
instead of dispatch_semaphore_t
.
@interface NSURLSessionTests : XCTestCase<NSURLSessionDataDelegate>
{
@private
XCTestExpectation *_expection;
}
@end
@implementation NSURLSessionTests
- (void)testWithRequest{
_expection = [self expectationWithDescription:@"UIDocument is opened."];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString: @"http://example.com/"]];
...
[dataTask resume];
[self waitForExpectations:[NSArray arrayWithObject:_expection] timeout:12.0];
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
NSLog(@"didCompleteWithError: %@", NSThread.currentThread);
NSLog(@"%@", error);
[_expection fulfill];
}
@end
But my app code doesn't still work. When I checked the app code, I found processing waiting for the end of HTTP request.
- (void)startSync
{
FcLicenseChecker *license = [FcLicenseChecker sharedInstance];
if(!license.isCheckRunning)
{
[license execute];
}
[license waitUntilCheckFinished];
...
@implementation FcLicenseChecker
- (void)waitUntilCheckFinished
{
while (_isCheckRunning)
{
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
}
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
...
_isCheckRunning = NO;
}
It was doing roop in waitUntilCheckFinished
without the delegation called.
I think that the way using [NSRunLoop currentRunLoop] runUntilDate
for waiting for the end worked with NSURLConnection
's delegation, but not with NSURLSession
.
I think that it will work to change to fire the next process in didCompleteWithError
instead of waiting for the end, but it is very hard because there are many codes using this class.
Is there a way to wait for the end with NSURLSession
?