Skip to content

Commit

Permalink
More tests.
Browse files Browse the repository at this point in the history
Some code refactoring.
More error handling.
Removed TCLogs.
Travis badge.
  • Loading branch information
thibaultcha committed Oct 25, 2013
1 parent e9fde17 commit 1095fad
Show file tree
Hide file tree
Showing 11 changed files with 395 additions and 67 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# TCBlobDownload

**Unit tests comming.**
[![Build Status](https://api.travis-ci.org/thibaultCha/TCBlobDownload.png)](https://travis-ci.org/thibaultCha/TCBlobDownload)

This library uses **NSOperations** to download big files (typically videos, music... well: BLOBs) using **NSURLConnection** in background threads. This is a static library, very easy to import in your project and it allows you to pull the latest updates. Installation steps are explained in usage section.

Expand Down
15 changes: 7 additions & 8 deletions TCBlobDownload.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@ Pod::Spec.new do |s|
s.license = 'MIT (example)'
s.author = { "Thibault Charbonnier" => "thibaultcha@me.com" }

# s.platform = :ios
# s.platform = :ios, '5.0'
s.platform = :ios
s.ios.deployment_target = '5.0'

# When using multiple platforms
# s.ios.deployment_target = '5.0'
# s.osx.deployment_target = '10.7'

s.source = { :git => "https://github.com/thibaultCha/TCBlobDownload", :tag => "1.3.1" }
s.source_files = 'TCBlobDownload/TCBlobDownload/*.{h,m}'
s.source = {
:git => "https://github.com/thibaultCha/TCBlobDownload",
:tag => "1.3.1"
}
s.source_files = 'TCBlobDownload/TCBlobDownload/*.{h,m}'
s.requires_arc = true
end
46 changes: 29 additions & 17 deletions TCBlobDownload/TCBlobDownload.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
21776214172FFC4A001956C7 /* TCBlobDownloadManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 2177620F172FFBD9001956C7 /* TCBlobDownloadManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
21776215172FFC56001956C7 /* TCBlobDownload.h in Headers */ = {isa = PBXBuildFile; fileRef = 2177620D172FFBD9001956C7 /* TCBlobDownload.h */; settings = {ATTRIBUTES = (Public, ); }; };
21776220172FFE64001956C7 /* TCBlobDownload-Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = 2177621F172FFE64001956C7 /* TCBlobDownload-Prefix.pch */; };
21D06E26181852B600CAADCD /* XCTestCase+AsyncTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = 21D06E25181852B600CAADCD /* XCTestCase+AsyncTesting.m */; };
21D06E281818539500CAADCD /* TCBlobDownloadErrorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 21D06E271818539500CAADCD /* TCBlobDownloadErrorTests.m */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -54,12 +56,16 @@
21203AE2178C593900C19335 /* TCBlobDownloadTests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TCBlobDownloadTests-Prefix.pch"; sourceTree = "<group>"; };
217761FC172FFBBB001956C7 /* libTCBlobDownload.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libTCBlobDownload.a; sourceTree = BUILT_PRODUCTS_DIR; };
217761FF172FFBBB001956C7 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
2177620D172FFBD9001956C7 /* TCBlobDownload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TCBlobDownload.h; sourceTree = "<group>"; };
2177620E172FFBD9001956C7 /* TCBlobDownload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TCBlobDownload.m; sourceTree = "<group>"; };
2177620F172FFBD9001956C7 /* TCBlobDownloadManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TCBlobDownloadManager.h; sourceTree = "<group>"; };
21776210172FFBD9001956C7 /* TCBlobDownloadManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TCBlobDownloadManager.m; sourceTree = "<group>"; };
2177620D172FFBD9001956C7 /* TCBlobDownload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TCBlobDownload.h; path = TCBlobDownload/TCBlobDownload.h; sourceTree = "<group>"; };
2177620E172FFBD9001956C7 /* TCBlobDownload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TCBlobDownload.m; path = TCBlobDownload/TCBlobDownload.m; sourceTree = "<group>"; };
2177620F172FFBD9001956C7 /* TCBlobDownloadManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TCBlobDownloadManager.h; path = TCBlobDownload/TCBlobDownloadManager.h; sourceTree = "<group>"; };
21776210172FFBD9001956C7 /* TCBlobDownloadManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TCBlobDownloadManager.m; path = TCBlobDownload/TCBlobDownloadManager.m; sourceTree = "<group>"; };
2177621F172FFE64001956C7 /* TCBlobDownload-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "TCBlobDownload-Prefix.pch"; path = "TCBlobDownload/TCBlobDownload-Prefix.pch"; sourceTree = "<group>"; };
217AB36D17395D0700A174C5 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = TCBlobDownload/fr.lproj/Localizable.strings; sourceTree = "<group>"; };
21D06E24181852B600CAADCD /* XCTestCase+AsyncTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "XCTestCase+AsyncTesting.h"; sourceTree = "<group>"; };
21D06E25181852B600CAADCD /* XCTestCase+AsyncTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "XCTestCase+AsyncTesting.m"; sourceTree = "<group>"; };
21D06E271818539500CAADCD /* TCBlobDownloadErrorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TCBlobDownloadErrorTests.m; sourceTree = "<group>"; };
21D06E29181853DA00CAADCD /* TestValues.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestValues.h; sourceTree = "<group>"; };
21EE597D17395AA10084CF48 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = TCBlobDownload/en.lproj/Localizable.strings; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -88,7 +94,10 @@
21203ADA178C593900C19335 /* TCBlobDownloadTests */ = {
isa = PBXGroup;
children = (
21D06E29181853DA00CAADCD /* TestValues.h */,
21203AE0178C593900C19335 /* TCBlobDownloadTests.m */,
21D06E271818539500CAADCD /* TCBlobDownloadErrorTests.m */,
21D06E231818529000CAADCD /* libs */,
21203ADB178C593900C19335 /* Supporting Files */,
);
path = TCBlobDownloadTests;
Expand All @@ -107,7 +116,10 @@
217761F3172FFBBB001956C7 = {
isa = PBXGroup;
children = (
21776201172FFBBB001956C7 /* TCBlobDownload */,
2177620D172FFBD9001956C7 /* TCBlobDownload.h */,
2177620E172FFBD9001956C7 /* TCBlobDownload.m */,
2177620F172FFBD9001956C7 /* TCBlobDownloadManager.h */,
21776210172FFBD9001956C7 /* TCBlobDownloadManager.m */,
21776221172FFE8F001956C7 /* Supporting Files */,
21203ADA178C593900C19335 /* TCBlobDownloadTests */,
217761FE172FFBBB001956C7 /* Frameworks */,
Expand All @@ -134,24 +146,22 @@
name = Frameworks;
sourceTree = "<group>";
};
21776201172FFBBB001956C7 /* TCBlobDownload */ = {
21776221172FFE8F001956C7 /* Supporting Files */ = {
isa = PBXGroup;
children = (
2177620D172FFBD9001956C7 /* TCBlobDownload.h */,
2177620E172FFBD9001956C7 /* TCBlobDownload.m */,
2177620F172FFBD9001956C7 /* TCBlobDownloadManager.h */,
21776210172FFBD9001956C7 /* TCBlobDownloadManager.m */,
2177621F172FFE64001956C7 /* TCBlobDownload-Prefix.pch */,
21EE597C17395AA10084CF48 /* Localizable.strings */,
);
path = TCBlobDownload;
name = "Supporting Files";
sourceTree = "<group>";
};
21776221172FFE8F001956C7 /* Supporting Files */ = {
21D06E231818529000CAADCD /* libs */ = {
isa = PBXGroup;
children = (
2177621F172FFE64001956C7 /* TCBlobDownload-Prefix.pch */,
21EE597C17395AA10084CF48 /* Localizable.strings */,
21D06E24181852B600CAADCD /* XCTestCase+AsyncTesting.h */,
21D06E25181852B600CAADCD /* XCTestCase+AsyncTesting.m */,
);
name = "Supporting Files";
name = libs;
sourceTree = "<group>";
};
/* End PBXGroup section */
Expand Down Expand Up @@ -256,7 +266,9 @@
buildActionMask = 2147483647;
files = (
21203AED178C63AE00C19335 /* TCBlobDownload.m in Sources */,
21D06E281818539500CAADCD /* TCBlobDownloadErrorTests.m in Sources */,
21203AE1178C593900C19335 /* TCBlobDownloadTests.m in Sources */,
21D06E26181852B600CAADCD /* XCTestCase+AsyncTesting.m in Sources */,
21203AEC178C63AB00C19335 /* TCBlobDownloadManager.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -323,7 +335,7 @@
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = "TCBlobDownloadTests/TCBlobDownloadTests-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = xctest;
};
Expand All @@ -348,7 +360,7 @@
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = "TCBlobDownloadTests/TCBlobDownloadTests-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = xctest;
};
Expand Down
4 changes: 3 additions & 1 deletion TCBlobDownload/TCBlobDownload/TCBlobDownload.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// Copyright (c) 2013 Thibault Charbonnier. All rights reserved.
//


//#define NO_LOG
#if defined(DEBUG) && !defined(NO_LOG)
#define TCLog(format, ...) NSLog(format, ## __VA_ARGS__)
#else
Expand All @@ -14,6 +14,8 @@

#import <Foundation/Foundation.h>

extern NSString * const HTTPErrorCode;

typedef void (^FirstResponseBlock)(NSURLResponse *response);
typedef void (^ProgressBlock)(float receivedLength, float totalLength);
typedef void (^ErrorBlock)(NSError *error);
Expand Down
77 changes: 45 additions & 32 deletions TCBlobDownload/TCBlobDownload/TCBlobDownload.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
const double kBufferSize = 1024*1024; // 1 MB
const NSTimeInterval kDefaultTimeout = 30;
NSString * const kErrorDomain = @"com.thibaultcha.tcblobdownload";
NSString * const HTTPErrorCode = @"httpStatus";

#import "TCBlobDownload.h"

Expand Down Expand Up @@ -76,15 +77,30 @@ - (void)start
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:kDefaultTimeout];

NSAssert([NSURLConnection canHandleRequest:fileRequest], @"NSURLConnection can't handle provided request");
if (![NSURLConnection canHandleRequest:fileRequest]) {
__autoreleasing NSError *error = [NSError errorWithDomain:kErrorDomain
code:1
userInfo:@{ NSLocalizedDescriptionKey:
[NSString stringWithFormat:@"Invalid URL provided to TCBlobDownload: %@",
fileRequest.URL] }];
if (self.errorBlock) {
self.errorBlock(error);
}
if ([self.delegate respondsToSelector:@selector(download:didStopWithError:)]) {
[self.delegate download:self didStopWithError:error];
}

[self finishOperation];

return;
}

NSFileManager *fm = [NSFileManager defaultManager];
// File already exists or not

if (![fm fileExistsAtPath:self.pathToFile]) {
[fm createFileAtPath:self.pathToFile
contents:nil
attributes:nil];
TCLog(@"Created file at path: %@", self.pathToFile);
}
else {
uint64_t fileSize = [[fm attributesOfItemAtPath:self.pathToFile error:nil] fileSize];
Expand All @@ -99,7 +115,6 @@ - (void)start
delegate:self
startImmediately:NO];
if (self.connection) {
TCLog(@"Operation started for file:\n%@", self.pathToFile);
[self.connection scheduleInRunLoop:[NSRunLoop mainRunLoop]
forMode:NSDefaultRunLoopMode];
[self willChangeValueForKey:@"isExecuting"];
Expand All @@ -124,14 +139,16 @@ - (BOOL)isFinished

- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError *)error
{
TCLog(@"Download failed. Error - %@ %@",
[error localizedDescription],
[error userInfo][NSURLErrorFailingURLStringErrorKey]);
__autoreleasing NSError *downloadError = [NSError errorWithDomain:kErrorDomain
code:4
userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:@"Download failed for file: %@. Reason: %@",
self.fileName,
error.localizedDescription] }];
if (self.errorBlock) {
self.errorBlock(error);
self.errorBlock(downloadError);
}
if ([self.delegate respondsToSelector:@selector(download:didStopWithError:)]) {
[self.delegate download:self didStopWithError:error];
[self.delegate download:self didStopWithError:downloadError];
}

[self cancelDownloadAndRemoveFile:NO];
Expand All @@ -147,23 +164,30 @@ - (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLRespons
if (httpUrlResponse.statusCode >= 400) {
error = [NSError errorWithDomain:kErrorDomain
code:2
userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:NSLocalizedString(@"HTTP error code %d (%@) ", @"HTTP error code {satus code} ({status code description})"),
httpUrlResponse.statusCode,
[NSHTTPURLResponse localizedStringForStatusCode:httpUrlResponse.statusCode]]}];
userInfo:@{ NSLocalizedDescriptionKey: [NSString stringWithFormat:
NSLocalizedString(@"HTTP error code %d (%@) ", @"HTTP error code {satus code} ({status code description})"),
httpUrlResponse.statusCode,
[NSHTTPURLResponse localizedStringForStatusCode:httpUrlResponse.statusCode]],
HTTPErrorCode: @(httpUrlResponse.statusCode) }];
}

if ([TCBlobDownload freeDiskSpace] < _expectedDataLength && _expectedDataLength != -1) {
error = [NSError errorWithDomain:kErrorDomain
code:1
userInfo:@{NSLocalizedDescriptionKey:
NSLocalizedString(@"Not enough free disk space", @"")}];
code:3
userInfo:@{ NSLocalizedDescriptionKey:NSLocalizedString(@"Not enough free disk space", @"") }];
}

if (error) {
TCLog(@"Download failed. Error - %@ %@",
[error localizedDescription],
[error userInfo][NSURLErrorFailingURLStringErrorKey]);
if (!error) {
[self.receivedDataBuffer setData:nil];

if (self.firstResponseBlock) {
self.firstResponseBlock(response);
}
if ([self.delegate respondsToSelector:@selector(download:didReceiveFirstResponse:)]) {
[self.delegate download:self didReceiveFirstResponse:response];
}
}
else {
if (self.errorBlock) {
self.errorBlock(error);
}
Expand All @@ -173,16 +197,6 @@ - (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLRespons

[self cancelDownloadAndRemoveFile:NO];
}
else {
[self.receivedDataBuffer setData:nil];

if (self.firstResponseBlock) {
self.firstResponseBlock(response);
}
if ([self.delegate respondsToSelector:@selector(download:didReceiveFirstResponse:)]) {
[self.delegate download:self didReceiveFirstResponse:response];
}
}
}

- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData *)data
Expand Down Expand Up @@ -210,7 +224,6 @@ - (void)connection:(NSURLConnection*)connection didReceiveData:(NSData *)data

- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
TCLog(@"Download succeeded. Bytes received: %lld", _receivedDataLength);
[self.file writeData:self.receivedDataBuffer];
[self.receivedDataBuffer setData:nil];

Expand All @@ -237,13 +250,12 @@ - (void)finishOperation
[self.file closeFile];
[self didChangeValueForKey:@"isFinished"];
[self didChangeValueForKey:@"isExecuting"];
TCLog(@"Operation ended for file %@", self.fileName);
}

- (void)cancelDownloadAndRemoveFile:(BOOL)remove
{
[self finishOperation];
TCLog(@"Cancel download received for file %@", self.pathToFile);

NSFileManager *fm = [NSFileManager defaultManager];

if (remove && [fm fileExistsAtPath:self.pathToFile]) {
Expand Down Expand Up @@ -288,6 +300,7 @@ + (uint64_t)freeDiskSpace
TCLog(@"Error obtaining system memory infos: Domain = %@, Code = %d",
[error domain],
[error code]);
// TODO handle error
}
return totalFreeSpace;
}
Expand Down
9 changes: 7 additions & 2 deletions TCBlobDownload/TCBlobDownload/TCBlobDownloadManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ - (void)cancelAllDownloadsAndRemoveFiles:(BOOL)remove
for (TCBlobDownload *blob in [self.operationQueue operations]) {
[blob cancelDownloadAndRemoveFile:remove];
}
TCLog(@"Cancelled all downloads.");
}


Expand All @@ -133,6 +132,11 @@ + (BOOL)createPathFromPath:(NSString *)path
{
NSFileManager *fm = [NSFileManager defaultManager];

if (path == nil || [path isEqualToString:@""]) {
// handle error
return false;
}

if ([fm fileExistsAtPath:path]) {
return true;
}
Expand All @@ -143,7 +147,8 @@ + (BOOL)createPathFromPath:(NSString *)path
attributes:nil
error:&error];
if (error) {
TCLog(@"Error creating download directory - %@", error);
TCLog(@"Error creating download directory %@ - %@", path, error);
// TODO handle error
}
return created;
}
Expand Down
Loading

0 comments on commit 1095fad

Please sign in to comment.