diff --git a/Package.resolved b/Package.resolved index f1a340e..04b0797 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "1fb94a628bae24b086e603675b30f843abd900d8930755ee0dd72a4ade351ba5", + "originHash" : "1f7986484b7e8b057dd70f8be4c133ec09b8168d92949426b9d7e9ffb9521815", "pins" : [ { "identity" : "collectionconcurrencykit", @@ -19,15 +19,6 @@ "version" : "1.8.1" } }, - { - "identity" : "ohhttpstubs", - "kind" : "remoteSourceControl", - "location" : "https://github.com/AliSoftware/OHHTTPStubs", - "state" : { - "revision" : "12f19662426d0434d6c330c6974d53e2eb10ecd9", - "version" : "9.1.0" - } - }, { "identity" : "sourcekitten", "kind" : "remoteSourceControl", diff --git a/Package.swift b/Package.swift index de67fef..afb0e8e 100644 --- a/Package.swift +++ b/Package.swift @@ -11,7 +11,6 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/buildkite/test-collector-swift", from: "0.3.0"), - .package(url: "https://github.com/AliSoftware/OHHTTPStubs", from: "9.1.0"), // See https://github.com/erikdoe/ocmock/issues/500#issuecomment-1002700625 .package(url: "https://github.com/realm/SwiftLint", exact: loadSwiftLintVersion()) ], @@ -34,8 +33,7 @@ let package = Package( name: "WordPressSharedTests", dependencies: [ .target(name: "WordPressShared"), - .product(name: "BuildkiteTestCollector", package: "test-collector-swift"), - "OHHTTPStubs", + .product(name: "BuildkiteTestCollector", package: "test-collector-swift") ], plugins: [ .plugin(name: "SwiftLintPlugin", package: "SwiftLint") @@ -45,8 +43,7 @@ let package = Package( name: "WordPressSharedObjCTests", dependencies: [ .target(name: "WordPressShared"), - .product(name: "BuildkiteTestCollector", package: "test-collector-swift"), - "OHHTTPStubs", + .product(name: "BuildkiteTestCollector", package: "test-collector-swift") ], resources: [.process("Resources")], plugins: [ diff --git a/Sources/WordPressSharedObjC/Utility/WPImageSource.m b/Sources/WordPressSharedObjC/Utility/WPImageSource.m deleted file mode 100644 index 35f6600..0000000 --- a/Sources/WordPressSharedObjC/Utility/WPImageSource.m +++ /dev/null @@ -1,176 +0,0 @@ -#import "WPImageSource.h" -#import "WPSharedLogging.h" - -NSString * const WPImageSourceErrorDomain = @"WPImageSourceErrorDomain"; - -@interface WPImageSource() - -@property (nonatomic, strong) NSURLSession *downloadsSession; -@property (nonatomic, strong) NSMutableSet *urlDownloadsInProgress; -@property (nonatomic, strong) NSMutableDictionary *successBlocks; -@property (nonatomic, strong) NSMutableDictionary *failureBlocks; - -@end - -@implementation WPImageSource - -+ (instancetype)sharedSource -{ - static WPImageSource *_sharedSource = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - _sharedSource = [[WPImageSource alloc] init]; - }); - return _sharedSource; -} - -- (void)dealloc -{ - [_downloadsSession invalidateAndCancel]; -} - -- (instancetype)init -{ - self = [super init]; - if (self) { - _urlDownloadsInProgress = [[NSMutableSet alloc] init]; - _successBlocks = [[NSMutableDictionary alloc] init]; - _failureBlocks = [[NSMutableDictionary alloc] init]; - - NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; - _downloadsSession = [NSURLSession sessionWithConfiguration:configuration]; - } - return self; -} - -- (void)downloadImageForURL:(NSURL *)url withSuccess:(void (^)(UIImage *))success failure:(void (^)(NSError *))failure -{ - [self downloadImageForURL:url authToken:nil withSuccess:success failure:failure]; -} - -- (void)downloadImageForURL:(NSURL *)url authToken:(NSString *)authToken withSuccess:(void (^)(UIImage *))success failure:(void (^)(NSError *))failure { - NSParameterAssert(url != nil); - - [self addCallbackForURL:url withSuccess:success failure:failure]; - - if (![self.urlDownloadsInProgress containsObject:url]) { - [self.urlDownloadsInProgress addObject:url]; - [self startDownloadForURL:url authToken:authToken]; - } -} - -#pragma mark - Downloader - -- (void)startDownloadForURL:(NSURL *)url authToken:(NSString *)authToken { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NSURL *requestURL = url; - NSString *token = nil; - - /* - If the URL is not for a WordPress.com file, pretend we don't have an auth token - It could potentially get sent to a third party - */ - if (authToken && [[requestURL host] hasSuffix:@"wordpress.com"]) { - token = authToken; - } - - if (token) { - if (![url.absoluteString hasPrefix:@"https"]) { - NSString *sslUrl = [url.absoluteString stringByReplacingOccurrencesOfString:@"http://" withString:@"https://"]; - requestURL = [NSURL URLWithString:sslUrl]; - } - } - - NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:requestURL cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:60]; - if (token) { - [request addValue:[NSString stringWithFormat:@"Bearer %@", token] forHTTPHeaderField:@"Authorization"]; - } - - NSURLSessionDownloadTask *task = [self.downloadsSession downloadTaskWithRequest:request - completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) { - if (error) { - [self downloadFailedWithError:error forURL:url]; - return; - } - NSError *readError; - NSData *data = [NSData dataWithContentsOfURL:location options:NSDataReadingUncached error:&readError]; - UIImage *image = [UIImage imageWithData:data]; - if (!image) { - [self downloadSucceededWithNilImageForURL:url response:response]; - return; - } - - [self downloadedImage:image forURL:url]; - }]; - [task resume]; - }); -} - -- (void)downloadedImage:(UIImage *)image forURL:(NSURL *)url -{ - NSArray *successBlocks = [self.successBlocks objectForKey:url]; - dispatch_async(dispatch_get_main_queue(), ^{ - [self removeCallbacksForURL:url]; - for (void (^success)(UIImage *) in successBlocks) { - success(image); - } - }); -} - -- (void)downloadFailedWithError:(NSError *)error forURL:(NSURL *)url -{ - NSArray *failureBlocks = [self.failureBlocks objectForKey:url]; - dispatch_async(dispatch_get_main_queue(), ^{ - [self removeCallbacksForURL:url]; - for (void (^failure)(NSError *) in failureBlocks) { - failure(error); - } - }); -} - -- (void)downloadSucceededWithNilImageForURL:(NSURL *)url response:(NSURLResponse *)response -{ - if ([response isKindOfClass:[NSHTTPURLResponse class]]){ - NSHTTPURLResponse *httpURLResponse = (NSHTTPURLResponse *)response; - WPSharedLogError(@"WPImageSource download completed sucessfully but the image was nil. Headers: %@", [httpURLResponse allHeaderFields]); - } - NSString *description = [NSString stringWithFormat:@"A download request ended successfully but the image was nil. URL: %@", [url absoluteString]]; - NSError *error = [NSError errorWithDomain:WPImageSourceErrorDomain - code:WPImageSourceErrorNilImage - userInfo:@{NSLocalizedDescriptionKey:description}]; - [self downloadFailedWithError:error forURL:url]; -} - -#pragma mark - Callback storage - -- (void)addCallbackForURL:(NSURL *)url withSuccess:(void (^)(UIImage *))success failure:(void (^)(NSError *))failure -{ - if (success) { - NSArray *successBlocks = [self.successBlocks objectForKey:url]; - if (!successBlocks) { - successBlocks = @[[success copy]]; - } else { - successBlocks = [successBlocks arrayByAddingObject:[success copy]]; - } - [self.successBlocks setObject:successBlocks forKey:url]; - } - - if (failure) { - NSArray *failureBlocks = [self.failureBlocks objectForKey:url]; - if (!failureBlocks) { - failureBlocks = @[[failure copy]]; - } else { - failureBlocks = [failureBlocks arrayByAddingObject:[failure copy]]; - } - [self.failureBlocks setObject:failureBlocks forKey:url]; - } -} - -- (void)removeCallbacksForURL:(NSURL *)url -{ - [self.successBlocks removeObjectForKey:url]; - [self.failureBlocks removeObjectForKey:url]; - [self.urlDownloadsInProgress removeObject:url]; -} - -@end diff --git a/Sources/WordPressSharedObjC/WordPressShared.h b/Sources/WordPressSharedObjC/WordPressShared.h index 4f64c00..cfab25f 100644 --- a/Sources/WordPressSharedObjC/WordPressShared.h +++ b/Sources/WordPressSharedObjC/WordPressShared.h @@ -12,7 +12,6 @@ FOUNDATION_EXPORT const unsigned char WordPressSharedVersionString[]; #import #import #import -#import #import #import #import diff --git a/Sources/WordPressSharedObjC/include/WPImageSource.h b/Sources/WordPressSharedObjC/include/WPImageSource.h deleted file mode 100644 index f67d9f2..0000000 --- a/Sources/WordPressSharedObjC/include/WPImageSource.h +++ /dev/null @@ -1,60 +0,0 @@ -#import -#import - -/** - WPImageSource Error Codes - */ -typedef NS_ENUM(NSUInteger, WPImageSourceError) { - WPImageSourceErrorUnknown, - WPImageSourceErrorNilImage -}; - -extern NSString * const WPImageSourceErrorDomain; - -/** - WPImageSource takes care of downloading images. - - It's a simple wrapper over AFImageRequestOperation that prevents duplicate requests. When a image URL is requested, it checks if there is a download in progress for that same URL. - In that case, it doesn't start a new download but adds the sender to the list of objects to notify. - - Otherwise, a download is started. - */ -@interface WPImageSource : NSObject - -/** - Returns the shared source object. - - @return the shared source object. - */ -+ (instancetype)sharedSource; - -/** - Schedules a download for the given url, if there isn't one in progress. - - Note that the success or failure block will be called whether the call initiated a network request or reused an existing one. - - @param url the URL to download. - @param success the block to execute if the download was successful. - @param failure the block to execute if the download failed. - */ -- (void)downloadImageForURL:(NSURL *)url - withSuccess:(void (^)(UIImage *image))success - failure:(void (^)(NSError *error))failure; - -/** - Schedules an authenticated download for the given URL, if there isn't one in progress. - - Assumes HTTPS should be used for the protocol. - - @param url the URL to download - @param authToken the authentication token (Bearer) to use (generally assumes this is used for WP.com) - @param success the block to execute if the download was successful - @param failure the block to execute if the download failed - */ - -- (void)downloadImageForURL:(NSURL *)url - authToken:(NSString *)authToken - withSuccess:(void (^)(UIImage *))success - failure:(void (^)(NSError *))failure; - -@end diff --git a/Tests/WordPressSharedObjCTests/WPImageSourceTest.m b/Tests/WordPressSharedObjCTests/WPImageSourceTest.m deleted file mode 100644 index 1848667..0000000 --- a/Tests/WordPressSharedObjCTests/WPImageSourceTest.m +++ /dev/null @@ -1,166 +0,0 @@ -@import OHHTTPStubs; -#import - -#import "WPImageSource.h" - -@interface WPImageSourceTest : XCTestCase - -@end - -@implementation WPImageSourceTest - -- (void)setUp -{ - [super setUp]; - // Put setup code here; it will be run once, before the first test case. -} - -- (void)tearDown -{ - // Put teardown code here; it will be run once, after the last test case. - [super tearDown]; -} - -- (void)testSendTokensToWordPressDotCom -{ - NSString *requestUrl = @"http://test.wordpress.com/images/test-image.jpg"; - NSURL *url = [NSURL URLWithString:requestUrl]; - __block NSString *lastAuthHeader = nil; - [HTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - lastAuthHeader = [request valueForHTTPHeaderField:@"Authorization"]; - return YES; - } withStubResponse:^HTTPStubsResponse *(NSURLRequest *request) { - return [HTTPStubsResponse responseWithFileAtPath:OHPathForFileInBundle(@"test-image.jpg", [self resourceBundle]) statusCode:200 headers:@{@"Content-Type" : @"image/jpeg"}]; - }]; - - WPImageSource *source = [WPImageSource sharedSource]; - - XCTestExpectation *expectation = [self expectationWithDescription:@"Download image with token"]; - - [source downloadImageForURL:url - authToken:@"TOKEN" - withSuccess:^(UIImage *image) { - [expectation fulfill]; - } failure:^(NSError *error) { - [expectation fulfill]; - XCTFail(); - }]; - [self waitForExpectationsWithTimeout:5.0 handler:nil]; - - XCTAssertEqualObjects(lastAuthHeader, @"Bearer TOKEN"); -} - -- (void)testDontSendTokensOutsideWordPressDotCom -{ - NSString *requestUrl = @"http://test.blog/images/test-image.jpg"; - NSURL *url = [NSURL URLWithString:requestUrl]; - __block NSString *lastAuthHeader = nil; - [HTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - lastAuthHeader = [request valueForHTTPHeaderField:@"Authorization"]; - return YES; - } withStubResponse:^HTTPStubsResponse *(NSURLRequest *request) { - return [HTTPStubsResponse responseWithFileAtPath:OHPathForFileInBundle(@"test-image.jpg", [self resourceBundle]) statusCode:200 headers:@{@"Content-Type" : @"image/jpeg"}]; - }]; - - WPImageSource *source = [WPImageSource sharedSource]; - - XCTestExpectation *expectation = [self expectationWithDescription:@"Download image without token"]; - [source downloadImageForURL:url - authToken:@"TOKEN" - withSuccess:^(UIImage *image) { - [expectation fulfill]; - } failure:^(NSError *error) { - [expectation fulfill]; - XCTFail(); - }]; - - [self waitForExpectationsWithTimeout:5.0 handler: nil]; - - XCTAssertNil(lastAuthHeader); -} - -- (void)testImagesArentDownloadedTwice -{ - NSString *requestUrl = @"http://test.blog/images/test-image.jpg"; - NSURL *url = [NSURL URLWithString:requestUrl]; - - __block int downloadCount = 0; - [HTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - return [[request.URL absoluteString] isEqualToString:requestUrl]; - } withStubResponse:^HTTPStubsResponse *(NSURLRequest *request) { - downloadCount++; - return [HTTPStubsResponse responseWithFileAtPath:OHPathForFileInBundle(@"test-image.jpg", [self resourceBundle]) statusCode:200 headers:@{@"Content-Type" : @"image/jpeg"}]; - }]; - - WPImageSource *source = [WPImageSource sharedSource]; - - XCTestExpectation *originalDownloadExpectation = [self expectationWithDescription:@"Start 1st download"]; - [source downloadImageForURL:url - withSuccess:^(UIImage *image) { - [originalDownloadExpectation fulfill]; - } failure:^(NSError *error) { - [originalDownloadExpectation fulfill]; - XCTFail(); - }]; - - XCTestExpectation *duplicateDownloadExpectation = [self expectationWithDescription:@"Start 1st download"]; - [source downloadImageForURL:url - withSuccess:^(UIImage *image) { - [duplicateDownloadExpectation fulfill]; - } failure:^(NSError *error) { - [duplicateDownloadExpectation fulfill]; - XCTFail(); - }]; - [self waitForExpectationsWithTimeout:5.0 handler: nil]; - - XCTAssertEqual(downloadCount, 1, @"it should download the image once"); - - XCTestExpectation *anotherDownloadExpectation = [self expectationWithDescription:@"Start 1st download"]; - [source downloadImageForURL:url - withSuccess:^(UIImage *image) { - [anotherDownloadExpectation fulfill]; - } failure:^(NSError *error) { - [anotherDownloadExpectation fulfill]; - XCTFail(); - }]; - - [self waitForExpectationsWithTimeout:5.0 handler: nil]; - XCTAssertEqual(downloadCount, 2, @"it should download the image"); -} - -- (void)testDownloadOfAnimatedGif -{ - NSString *requestUrl = @"http://test.blog/images/anim-reader.gif"; - NSURL *url = [NSURL URLWithString:requestUrl]; - [HTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) { - return YES; - } withStubResponse:^HTTPStubsResponse *(NSURLRequest *request) { - return [HTTPStubsResponse responseWithFileAtPath:OHPathForFileInBundle(@"anim-reader.gif", [self resourceBundle]) statusCode:200 headers:@{@"Content-Type" : @"image/gif"}]; - }]; - - WPImageSource *source = [WPImageSource sharedSource]; - - XCTestExpectation *expectation = [self expectationWithDescription:@"Download image without token"]; - [source downloadImageForURL:url - authToken:@"TOKEN" - withSuccess:^(UIImage *image) { - [expectation fulfill]; - } failure:^(NSError *error) { - [expectation fulfill]; - XCTFail(); - }]; - - [self waitForExpectationsWithTimeout:5.0 handler: nil]; - -} - -- (NSBundle *)resourceBundle -{ -#if SWIFT_PACKAGE - return SWIFTPM_MODULE_BUNDLE; -#else - return [NSBundle bundleForClass:[self class]]; -#endif -} - -@end diff --git a/WordPressShared.podspec b/WordPressShared.podspec index dd1fdf0..ab638b4 100644 --- a/WordPressShared.podspec +++ b/WordPressShared.podspec @@ -37,9 +37,6 @@ Pod::Spec.new do |s| 'Tests/WordPressSharedTestsObjC/**/*.{h,m}' ] test.resources = 'Tests/WordPressSharedObjCTests/Resources/*.{jpg,gif}' - - test.dependency 'OHHTTPStubs', '~> 9.0' - test.dependency 'OHHTTPStubs/Swift', '~> 9.0' end end