diff --git a/AWSAPIGateway.podspec b/AWSAPIGateway.podspec
index 755262c1f75..67707c128a4 100644
--- a/AWSAPIGateway.podspec
+++ b/AWSAPIGateway.podspec
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = 'AWSAPIGateway'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -13,7 +13,7 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSAPIGateway/*.{h,m}'
end
diff --git a/AWSAPIGateway/AWSAPIGatewayClient.m b/AWSAPIGateway/AWSAPIGatewayClient.m
index 6923d09e383..b50824c8c87 100644
--- a/AWSAPIGateway/AWSAPIGatewayClient.m
+++ b/AWSAPIGateway/AWSAPIGatewayClient.m
@@ -23,7 +23,7 @@
static NSString *const AWSAPIGatewayAPIKeyHeader = @"x-api-key";
-static NSString *const AWSAPIGatewaySDKVersion = @"2.6.12";
+static NSString *const AWSAPIGatewaySDKVersion = @"2.6.13";
static int defaultChunkSize = 1024;
diff --git a/AWSAPIGateway/Info.plist b/AWSAPIGateway/Info.plist
index faaa68791a9..4f1b86ecbe1 100644
--- a/AWSAPIGateway/Info.plist
+++ b/AWSAPIGateway/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSAuth.podspec b/AWSAuth.podspec
index 14e1c2f7942..f1d1f3c2844 100644
--- a/AWSAuth.podspec
+++ b/AWSAuth.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSAuth'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -14,23 +14,23 @@ Pod::Spec.new do |s|
s.requires_arc = true
s.subspec 'Core' do |authcore|
- authcore.dependency 'AWSAuthCore', '2.6.12'
+ authcore.dependency 'AWSAuthCore', '2.6.13'
end
s.subspec 'FacebookSignIn' do |facebook|
- facebook.dependency 'AWSFacebookSignIn', '2.6.12'
+ facebook.dependency 'AWSFacebookSignIn', '2.6.13'
end
s.subspec 'GoogleSignIn' do |google|
- google.dependency 'AWSGoogleSignIn', '2.6.12'
+ google.dependency 'AWSGoogleSignIn', '2.6.13'
end
s.subspec 'UserPoolsSignIn' do |up|
- up.dependency 'AWSUserPoolsSignIn', '2.6.12'
+ up.dependency 'AWSUserPoolsSignIn', '2.6.13'
end
s.subspec 'UI' do |ui|
- ui.dependency 'AWSAuthUI', '2.6.12'
+ ui.dependency 'AWSAuthUI', '2.6.13'
end
end
diff --git a/AWSAuthCore.podspec b/AWSAuthCore.podspec
index a108c658978..f35a73f1dcc 100644
--- a/AWSAuthCore.podspec
+++ b/AWSAuthCore.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSAuthCore'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,7 +12,7 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSAuthSDK/Sources/AWSAuthCore/*.{h,m}'
s.public_header_files = 'AWSAuthSDK/Sources/AWSAuthCore/*.h'
end
diff --git a/AWSAuthSDK/Sources/AWSAuthCore/Info.plist b/AWSAuthSDK/Sources/AWSAuthCore/Info.plist
index 9f4043017b3..471d2df6be9 100644
--- a/AWSAuthSDK/Sources/AWSAuthCore/Info.plist
+++ b/AWSAuthSDK/Sources/AWSAuthCore/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleVersion
$(CURRENT_PROJECT_VERSION)
NSPrincipalClass
diff --git a/AWSAuthSDK/Sources/AWSAuthUI/Info.plist b/AWSAuthSDK/Sources/AWSAuthUI/Info.plist
index 9f4043017b3..471d2df6be9 100644
--- a/AWSAuthSDK/Sources/AWSAuthUI/Info.plist
+++ b/AWSAuthSDK/Sources/AWSAuthUI/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleVersion
$(CURRENT_PROJECT_VERSION)
NSPrincipalClass
diff --git a/AWSAuthSDK/Sources/AWSFacebookSignIn/Info.plist b/AWSAuthSDK/Sources/AWSFacebookSignIn/Info.plist
index 9f4043017b3..471d2df6be9 100644
--- a/AWSAuthSDK/Sources/AWSFacebookSignIn/Info.plist
+++ b/AWSAuthSDK/Sources/AWSFacebookSignIn/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleVersion
$(CURRENT_PROJECT_VERSION)
NSPrincipalClass
diff --git a/AWSAuthSDK/Sources/AWSGoogleSignIn/Info.plist b/AWSAuthSDK/Sources/AWSGoogleSignIn/Info.plist
index 9f4043017b3..471d2df6be9 100644
--- a/AWSAuthSDK/Sources/AWSGoogleSignIn/Info.plist
+++ b/AWSAuthSDK/Sources/AWSGoogleSignIn/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleVersion
$(CURRENT_PROJECT_VERSION)
NSPrincipalClass
diff --git a/AWSAuthSDK/Sources/AWSMobileClient/Info.plist b/AWSAuthSDK/Sources/AWSMobileClient/Info.plist
index 4fd88feaab1..93aa2dfabae 100644
--- a/AWSAuthSDK/Sources/AWSMobileClient/Info.plist
+++ b/AWSAuthSDK/Sources/AWSMobileClient/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleVersion
$(CURRENT_PROJECT_VERSION)
NSPrincipalClass
diff --git a/AWSAuthSDK/Sources/AWSUserPoolsSignIn/Info.plist b/AWSAuthSDK/Sources/AWSUserPoolsSignIn/Info.plist
index 9f4043017b3..471d2df6be9 100644
--- a/AWSAuthSDK/Sources/AWSUserPoolsSignIn/Info.plist
+++ b/AWSAuthSDK/Sources/AWSUserPoolsSignIn/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleVersion
$(CURRENT_PROJECT_VERSION)
NSPrincipalClass
diff --git a/AWSAuthUI.podspec b/AWSAuthUI.podspec
index cdd553a9070..f01a8f9d497 100644
--- a/AWSAuthUI.podspec
+++ b/AWSAuthUI.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSAuthUI'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,8 +12,8 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
- s.dependency 'AWSAuthCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
+ s.dependency 'AWSAuthCore', '2.6.13'
s.source_files = 'AWSAuthSDK/Sources/AWSAuthUI/*.{h,m}', 'AWSAuthSDK/Sources/AWSAuthUI/**/*.{h,m}', 'AWSAuthSDK/Sources/AWSUserPoolsSignIn/UserPoolsUI/AWSFormTableCell.h', 'AWSAuthSDK/Sources/AWSUserPoolsSignIn/UserPoolsUI/AWSTableInputCell.h', 'AWSAuthSDK/Sources/AWSUserPoolsSignIn/UserPoolsUI/AWSFormTableDelegate.h', 'AWSAuthSDK/Sources/AWSUserPoolsSignIn/UserPoolsUI/AWSUserPoolsUIHelper.h'
s.public_header_files = 'AWSAuthSDK/Sources/AWSAuthUI/AWSAuthUI.h', 'AWSAuthSDK/Sources/AWSAuthUI/AWSAuthUIViewController.h', 'AWSAuthSDK/Sources/AWSAuthUI/AWSAuthUIConfiguration.h'
s.private_header_files = 'AWSAuthSDK/Sources/AWSUserPoolsSignIn/UserPoolsUI/AWSFormTableCell.h', 'AWSAuthSDK/Sources/AWSAuthUI/AWSSignInViewController.h', 'AWSAuthSDK/Sources/AWSUserPoolsSignIn/UserPoolsUI/AWSTableInputCell.h', 'AWSAuthSDK/Sources/AWSUserPoolsSignIn/UserPoolsUI/AWSFormTableDelegate.h', 'AWSAuthSDK/Sources/AWSUserPoolsSignIn/UserPoolsUI/AWSUserPoolsUIHelper.h'
diff --git a/AWSAutoScaling.podspec b/AWSAutoScaling.podspec
index c64cdb90062..df45a02d702 100644
--- a/AWSAutoScaling.podspec
+++ b/AWSAutoScaling.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSAutoScaling'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,6 +12,6 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSAutoScaling/*.{h,m}'
end
diff --git a/AWSAutoScaling/AWSAutoScalingService.m b/AWSAutoScaling/AWSAutoScalingService.m
index da7087a2c44..a198b000565 100644
--- a/AWSAutoScaling/AWSAutoScalingService.m
+++ b/AWSAutoScaling/AWSAutoScalingService.m
@@ -26,7 +26,7 @@
#import "AWSAutoScalingResources.h"
static NSString *const AWSInfoAutoScaling = @"AutoScaling";
-static NSString *const AWSAutoScalingSDKVersion = @"2.6.12";
+static NSString *const AWSAutoScalingSDKVersion = @"2.6.13";
@interface AWSAutoScalingResponseSerializer : AWSXMLResponseSerializer
diff --git a/AWSAutoScaling/Info.plist b/AWSAutoScaling/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSAutoScaling/Info.plist
+++ b/AWSAutoScaling/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSCloudWatch.podspec b/AWSCloudWatch.podspec
index 0fa5689947e..e7dc39023b8 100644
--- a/AWSCloudWatch.podspec
+++ b/AWSCloudWatch.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSCloudWatch'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,6 +12,6 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSCloudWatch/*.{h,m}'
end
diff --git a/AWSCloudWatch/AWSCloudWatchService.m b/AWSCloudWatch/AWSCloudWatchService.m
index 679960e7ab0..00ca5189afb 100644
--- a/AWSCloudWatch/AWSCloudWatchService.m
+++ b/AWSCloudWatch/AWSCloudWatchService.m
@@ -26,7 +26,7 @@
#import "AWSCloudWatchResources.h"
static NSString *const AWSInfoCloudWatch = @"CloudWatch";
-static NSString *const AWSCloudWatchSDKVersion = @"2.6.12";
+static NSString *const AWSCloudWatchSDKVersion = @"2.6.13";
@interface AWSCloudWatchResponseSerializer : AWSXMLResponseSerializer
diff --git a/AWSCloudWatch/Info.plist b/AWSCloudWatch/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSCloudWatch/Info.plist
+++ b/AWSCloudWatch/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSCognito.podspec b/AWSCognito.podspec
index d7546e0f503..15721341738 100644
--- a/AWSCognito.podspec
+++ b/AWSCognito.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSCognito'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Cognito SDK for iOS'
s.description = 'Amazon Cognito offers multi device data synchronization with offline access'
@@ -13,7 +13,7 @@ Pod::Spec.new do |s|
:tag => s.version}
s.requires_arc = true
s.library = 'sqlite3'
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSCognito/*.{h,m}', 'AWSCognito/**/*.{h,m}'
s.public_header_files = 'AWSCognito/*.h', 'AWSCognito/CognitoSync/*.h'
s.private_header_files = 'AWSCognito/Fabric/*.h', 'AWSCognito/Internal/*.h'
diff --git a/AWSCognito/AWSCognitoService.m b/AWSCognito/AWSCognitoService.m
index 76f06fadbd8..7d013243e60 100644
--- a/AWSCognito/AWSCognitoService.m
+++ b/AWSCognito/AWSCognitoService.m
@@ -34,7 +34,7 @@
#import "Fabric+FABKits.h"
static NSString *const AWSInfoCognito = @"Cognito";
-static NSString *const AWSCognitoSDKVersion = @"2.6.12";
+static NSString *const AWSCognitoSDKVersion = @"2.6.13";
NSString *const AWSCognitoDidStartSynchronizeNotification = @"com.amazon.cognito.AWSCognitoDidStartSynchronizeNotification";
NSString *const AWSCognitoDidEndSynchronizeNotification = @"com.amazon.cognito.AWSCognitoDidEndSynchronizeNotification";
diff --git a/AWSCognito/CognitoSync/AWSCognitoSyncService.m b/AWSCognito/CognitoSync/AWSCognitoSyncService.m
index f25db2f4c30..f29233312ce 100644
--- a/AWSCognito/CognitoSync/AWSCognitoSyncService.m
+++ b/AWSCognito/CognitoSync/AWSCognitoSyncService.m
@@ -26,7 +26,7 @@
#import "AWSCognitoSyncResources.h"
static NSString *const AWSInfoCognitoSync = @"CognitoSync";
-static NSString *const AWSCognitoSyncSDKVersion = @"2.6.12";
+static NSString *const AWSCognitoSyncSDKVersion = @"2.6.13";
@interface AWSCognitoSyncResponseSerializer : AWSJSONResponseSerializer
diff --git a/AWSCognito/Info.plist b/AWSCognito/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSCognito/Info.plist
+++ b/AWSCognito/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSCognitoAuth.podspec b/AWSCognitoAuth.podspec
index 929308f0c5b..b82506a166c 100644
--- a/AWSCognitoAuth.podspec
+++ b/AWSCognitoAuth.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSCognitoAuth'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Cognito Auth SDK for iOS'
s.description = 'Amazon Cognito Auth enables sign up and authentication of your end users via a hosted UI'
diff --git a/AWSCognitoAuth/AWSCognitoAuth.m b/AWSCognitoAuth/AWSCognitoAuth.m
index 72fb8201a30..d34995fa367 100644
--- a/AWSCognitoAuth/AWSCognitoAuth.m
+++ b/AWSCognitoAuth/AWSCognitoAuth.m
@@ -42,7 +42,7 @@ @interface AWSCognitoAuth()CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSCognitoIdentityProvider.podspec b/AWSCognitoIdentityProvider.podspec
index 2789d244cdf..6359903b972 100644
--- a/AWSCognitoIdentityProvider.podspec
+++ b/AWSCognitoIdentityProvider.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSCognitoIdentityProvider'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Cognito Identity Provider SDK for iOS (Beta)'
s.description = 'Amazon Cognito Identity Provider enables sign up and authentication of your end users'
@@ -13,7 +13,7 @@ Pod::Spec.new do |s|
:tag => s.version}
s.requires_arc = true
s.dependency 'AWSCognitoIdentityProviderASF', '1.0.1'
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSCognitoIdentityProvider/**/*.{h,m,c}'
s.public_header_files = 'AWSCognitoIdentityProvider/*.h', 'AWSCognitoIdentityProvider/CognitoIdentityProvider/*.h'
s.private_header_files = 'AWSCognitoIdentityProvider/Internal/*.h'
diff --git a/AWSCognitoIdentityProvider/CognitoIdentityProvider/AWSCognitoIdentityProviderService.m b/AWSCognitoIdentityProvider/CognitoIdentityProvider/AWSCognitoIdentityProviderService.m
index 5745a416ebe..6ab0ecfacb0 100644
--- a/AWSCognitoIdentityProvider/CognitoIdentityProvider/AWSCognitoIdentityProviderService.m
+++ b/AWSCognitoIdentityProvider/CognitoIdentityProvider/AWSCognitoIdentityProviderService.m
@@ -26,7 +26,7 @@
#import "AWSCognitoIdentityProviderResources.h"
static NSString *const AWSInfoCognitoIdentityProvider = @"CognitoIdentityProvider";
-static NSString *const AWSCognitoIdentityProviderSDKVersion = @"2.6.12";
+static NSString *const AWSCognitoIdentityProviderSDKVersion = @"2.6.13";
@interface AWSCognitoIdentityProviderResponseSerializer : AWSJSONResponseSerializer
diff --git a/AWSCognitoIdentityProvider/Info.plist b/AWSCognitoIdentityProvider/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSCognitoIdentityProvider/Info.plist
+++ b/AWSCognitoIdentityProvider/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSCognitoSync.podspec b/AWSCognitoSync.podspec
index fa09f03cac5..2033ea5b38c 100644
--- a/AWSCognitoSync.podspec
+++ b/AWSCognitoSync.podspec
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = 'AWSCognitoSync'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Cognito SDK for iOS'
s.description = 'Amazon Cognito offers multi device data synchronization with offline access'
@@ -14,7 +14,7 @@ Pod::Spec.new do |s|
:tag => s.version}
s.requires_arc = true
s.library = 'sqlite3'
- s.dependency 'AWSCognito', '2.6.12'
+ s.dependency 'AWSCognito', '2.6.13'
s.deprecated = true
s.deprecated_in_favor_of = 'AWSCognito'
diff --git a/AWSCore.podspec b/AWSCore.podspec
index 08b03c1dc93..247b70cea3c 100644
--- a/AWSCore.podspec
+++ b/AWSCore.podspec
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = 'AWSCore'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
diff --git a/AWSCore/Info.plist b/AWSCore/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSCore/Info.plist
+++ b/AWSCore/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSCore/Service/AWSService.m b/AWSCore/Service/AWSService.m
index 50748edcb92..d2743f727ae 100644
--- a/AWSCore/Service/AWSService.m
+++ b/AWSCore/Service/AWSService.m
@@ -21,7 +21,7 @@
#import "AWSCocoaLumberjack.h"
#import "AWSCategory.h"
-NSString *const AWSiOSSDKVersion = @"2.6.12";
+NSString *const AWSiOSSDKVersion = @"2.6.13";
NSString *const AWSServiceErrorDomain = @"com.amazonaws.AWSServiceErrorDomain";
static NSString *const AWSServiceConfigurationUnknown = @"Unknown";
diff --git a/AWSDynamoDB.podspec b/AWSDynamoDB.podspec
index d2571069c34..985c0b2c956 100644
--- a/AWSDynamoDB.podspec
+++ b/AWSDynamoDB.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSDynamoDB'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,6 +12,6 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSDynamoDB/*.{h,m}'
end
diff --git a/AWSDynamoDB/AWSDynamoDBService.m b/AWSDynamoDB/AWSDynamoDBService.m
index 0ac7cfadf31..85f94f0d989 100644
--- a/AWSDynamoDB/AWSDynamoDBService.m
+++ b/AWSDynamoDB/AWSDynamoDBService.m
@@ -27,7 +27,7 @@
#import "AWSDynamoDBRequestRetryHandler.h"
static NSString *const AWSInfoDynamoDB = @"DynamoDB";
-static NSString *const AWSDynamoDBSDKVersion = @"2.6.12";
+static NSString *const AWSDynamoDBSDKVersion = @"2.6.13";
@interface AWSDynamoDBResponseSerializer : AWSJSONResponseSerializer
diff --git a/AWSDynamoDB/Info.plist b/AWSDynamoDB/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSDynamoDB/Info.plist
+++ b/AWSDynamoDB/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSEC2.podspec b/AWSEC2.podspec
index a5487183edc..dab13a8013f 100644
--- a/AWSEC2.podspec
+++ b/AWSEC2.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSEC2'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,6 +12,6 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSEC2/*.{h,m}'
end
diff --git a/AWSEC2/AWSEC2Service.m b/AWSEC2/AWSEC2Service.m
index 603e935617c..1cea2eef66f 100644
--- a/AWSEC2/AWSEC2Service.m
+++ b/AWSEC2/AWSEC2Service.m
@@ -27,7 +27,7 @@
#import "AWSEC2Serializer.h"
static NSString *const AWSInfoEC2 = @"EC2";
-static NSString *const AWSEC2SDKVersion = @"2.6.12";
+static NSString *const AWSEC2SDKVersion = @"2.6.13";
@interface AWSEC2ResponseSerializer : AWSXMLResponseSerializer
diff --git a/AWSEC2/Info.plist b/AWSEC2/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSEC2/Info.plist
+++ b/AWSEC2/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSElasticLoadBalancing.podspec b/AWSElasticLoadBalancing.podspec
index d30fd62dd48..880dfa59021 100644
--- a/AWSElasticLoadBalancing.podspec
+++ b/AWSElasticLoadBalancing.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSElasticLoadBalancing'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,6 +12,6 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSElasticLoadBalancing/*.{h,m}'
end
diff --git a/AWSElasticLoadBalancing/AWSElasticLoadBalancingService.m b/AWSElasticLoadBalancing/AWSElasticLoadBalancingService.m
index 8dee98e829e..8d031553319 100644
--- a/AWSElasticLoadBalancing/AWSElasticLoadBalancingService.m
+++ b/AWSElasticLoadBalancing/AWSElasticLoadBalancingService.m
@@ -26,7 +26,7 @@
#import "AWSElasticLoadBalancingResources.h"
static NSString *const AWSInfoElasticLoadBalancing = @"ElasticLoadBalancing";
-static NSString *const AWSElasticLoadBalancingSDKVersion = @"2.6.12";
+static NSString *const AWSElasticLoadBalancingSDKVersion = @"2.6.13";
@interface AWSElasticLoadBalancingResponseSerializer : AWSXMLResponseSerializer
diff --git a/AWSElasticLoadBalancing/Info.plist b/AWSElasticLoadBalancing/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSElasticLoadBalancing/Info.plist
+++ b/AWSElasticLoadBalancing/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSFacebookSignIn.podspec b/AWSFacebookSignIn.podspec
index e24a3953a5d..2216aafbd1b 100644
--- a/AWSFacebookSignIn.podspec
+++ b/AWSFacebookSignIn.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSFacebookSignIn'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,7 +12,7 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSAuthCore', '2.6.12'
+ s.dependency 'AWSAuthCore', '2.6.13'
s.dependency 'FBSDKLoginKit', '~> 4.0'
s.dependency 'FBSDKCoreKit', '~> 4.0'
s.source_files = 'AWSAuthSDK/Sources/AWSFacebookSignIn/*.{h,m}'
diff --git a/AWSGoogleSignIn.podspec b/AWSGoogleSignIn.podspec
index 8d8f0249d7e..e8a43cb1437 100644
--- a/AWSGoogleSignIn.podspec
+++ b/AWSGoogleSignIn.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSGoogleSignIn'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,7 +12,7 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSAuthCore', '2.6.12'
+ s.dependency 'AWSAuthCore', '2.6.13'
s.source_files = 'AWSAuthSDK/Sources/AWSGoogleSignIn/*.{h,m}', 'AWSAuthSDK/Dependencies/GoogleHeaders/*.h'
s.public_header_files = 'AWSAuthSDK/Sources/AWSGoogleSignIn/*.h'
s.private_header_files = 'AWSAuthSDK/Dependencies/GoogleHeaders/*.h'
diff --git a/AWSIoT.podspec b/AWSIoT.podspec
index 6a1a7d153ec..915ba5baa6e 100644
--- a/AWSIoT.podspec
+++ b/AWSIoT.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSIoT'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,7 +12,7 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSIoT/*.{h,m}', 'AWSIoT/**/*.{h,m}'
s.private_header_files = 'AWSIoT/Internal/*.h'
end
diff --git a/AWSIoT/AWSIoTDataService.m b/AWSIoT/AWSIoTDataService.m
index 62ffb43a4c8..5b30f97f21e 100644
--- a/AWSIoT/AWSIoTDataService.m
+++ b/AWSIoT/AWSIoTDataService.m
@@ -26,7 +26,7 @@
#import "AWSIoTDataResources.h"
static NSString *const AWSInfoIoTData = @"IoTData";
-static NSString *const AWSIoTDataSDKVersion = @"2.6.12";
+static NSString *const AWSIoTDataSDKVersion = @"2.6.13";
@interface AWSIoTDataResponseSerializer : AWSJSONResponseSerializer
diff --git a/AWSIoT/AWSIoTService.m b/AWSIoT/AWSIoTService.m
index d7396c22399..48012f230a5 100644
--- a/AWSIoT/AWSIoTService.m
+++ b/AWSIoT/AWSIoTService.m
@@ -26,7 +26,7 @@
#import "AWSIoTResources.h"
static NSString *const AWSInfoIoT = @"IoT";
-static NSString *const AWSIoTSDKVersion = @"2.6.12";
+static NSString *const AWSIoTSDKVersion = @"2.6.13";
@interface AWSIoTResponseSerializer : AWSJSONResponseSerializer
diff --git a/AWSIoT/Info.plist b/AWSIoT/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSIoT/Info.plist
+++ b/AWSIoT/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSKMS.podspec b/AWSKMS.podspec
index 36384db5d09..509cc0263fc 100644
--- a/AWSKMS.podspec
+++ b/AWSKMS.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSKMS'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,6 +12,6 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSKMS/*.{h,m}'
end
diff --git a/AWSKMS/AWSKMSService.m b/AWSKMS/AWSKMSService.m
index 87afff0ae2d..236aca87bef 100644
--- a/AWSKMS/AWSKMSService.m
+++ b/AWSKMS/AWSKMSService.m
@@ -26,7 +26,7 @@
#import "AWSKMSResources.h"
static NSString *const AWSInfoKMS = @"KMS";
-static NSString *const AWSKMSSDKVersion = @"2.6.12";
+static NSString *const AWSKMSSDKVersion = @"2.6.13";
@interface AWSKMSResponseSerializer : AWSJSONResponseSerializer
diff --git a/AWSKMS/Info.plist b/AWSKMS/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSKMS/Info.plist
+++ b/AWSKMS/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSKinesis.podspec b/AWSKinesis.podspec
index d92970c1824..44f4c54b090 100644
--- a/AWSKinesis.podspec
+++ b/AWSKinesis.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSKinesis'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,6 +12,6 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSKinesis/*.{h,m}'
end
diff --git a/AWSKinesis/AWSFirehoseService.m b/AWSKinesis/AWSFirehoseService.m
index 822bac16575..7f25d5640f1 100644
--- a/AWSKinesis/AWSFirehoseService.m
+++ b/AWSKinesis/AWSFirehoseService.m
@@ -26,7 +26,7 @@
#import "AWSFirehoseResources.h"
static NSString *const AWSInfoFirehose = @"Firehose";
-static NSString *const AWSFirehoseSDKVersion = @"2.6.12";
+static NSString *const AWSFirehoseSDKVersion = @"2.6.13";
@interface AWSFirehoseResponseSerializer : AWSJSONResponseSerializer
diff --git a/AWSKinesis/AWSKinesisService.m b/AWSKinesis/AWSKinesisService.m
index c174d32098e..054dfeb4c9f 100644
--- a/AWSKinesis/AWSKinesisService.m
+++ b/AWSKinesis/AWSKinesisService.m
@@ -27,7 +27,7 @@
#import "AWSKinesisRequestRetryHandler.h"
static NSString *const AWSInfoKinesis = @"Kinesis";
-static NSString *const AWSKinesisSDKVersion = @"2.6.12";
+static NSString *const AWSKinesisSDKVersion = @"2.6.13";
@interface AWSKinesisResponseSerializer : AWSJSONResponseSerializer
diff --git a/AWSKinesis/Info.plist b/AWSKinesis/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSKinesis/Info.plist
+++ b/AWSKinesis/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSLambda.podspec b/AWSLambda.podspec
index 39291d91a25..d80f3d9a078 100644
--- a/AWSLambda.podspec
+++ b/AWSLambda.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSLambda'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,6 +12,6 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSLambda/*.{h,m}'
end
diff --git a/AWSLambda/AWSLambdaService.m b/AWSLambda/AWSLambdaService.m
index 76fd6cdece7..9b4e40e102f 100644
--- a/AWSLambda/AWSLambdaService.m
+++ b/AWSLambda/AWSLambdaService.m
@@ -27,7 +27,7 @@
#import "AWSLambdaRequestRetryHandler.h"
static NSString *const AWSInfoLambda = @"Lambda";
-static NSString *const AWSLambdaSDKVersion = @"2.6.12";
+static NSString *const AWSLambdaSDKVersion = @"2.6.13";
@interface AWSLambdaResponseSerializer : AWSJSONResponseSerializer
diff --git a/AWSLambda/Info.plist b/AWSLambda/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSLambda/Info.plist
+++ b/AWSLambda/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSLex.podspec b/AWSLex.podspec
index 8274d595947..53acea1a389 100644
--- a/AWSLex.podspec
+++ b/AWSLex.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSLex'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,7 +12,7 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSLex/*.{h,m}', 'AWSLex/Bluefront/include/*.h'
s.public_header_files = 'AWSLex/*.h'
s.private_header_files = 'AWSLex/Bluefront/include/*.h'
diff --git a/AWSLex/AWSLexInteractionKit.m b/AWSLex/AWSLexInteractionKit.m
index 016a515c073..536f5ab69d7 100644
--- a/AWSLex/AWSLexInteractionKit.m
+++ b/AWSLex/AWSLexInteractionKit.m
@@ -22,7 +22,7 @@
#import
NSString *const AWSInfoInteractionKit = @"LexInteractionKit";
-NSString *const AWSInteractionKitSDKVersion = @"2.6.12";
+NSString *const AWSInteractionKitSDKVersion = @"2.6.13";
NSString *const AWSInternalLexInteractionKit = @"LexInteractionKitClient";
NSString *const AWSLexInteractionKitUserAgent = @"interactionkit";
NSString *const AWSLexInteractionKitErrorDomain = @"com.amazonaws.AWSLexInteractionKitErrorDomain";
diff --git a/AWSLex/AWSLexService.m b/AWSLex/AWSLexService.m
index e90a82aad8f..842c3c5a1db 100644
--- a/AWSLex/AWSLexService.m
+++ b/AWSLex/AWSLexService.m
@@ -28,7 +28,7 @@
#import "AWSLexSignature.h"
static NSString *const AWSInfoLex = @"Lex";
-static NSString *const AWSLexSDKVersion = @"2.6.12";
+static NSString *const AWSLexSDKVersion = @"2.6.13";
@interface AWSLexResponseSerializer : AWSJSONResponseSerializer
diff --git a/AWSLex/Info.plist b/AWSLex/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSLex/Info.plist
+++ b/AWSLex/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSLogs.podspec b/AWSLogs.podspec
index 93d8e7d9838..8f59a4c3e45 100644
--- a/AWSLogs.podspec
+++ b/AWSLogs.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSLogs'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,6 +12,6 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSLogs/*.{h,m}'
end
diff --git a/AWSLogs/AWSLogsService.m b/AWSLogs/AWSLogsService.m
index 68dc90725ae..1b3172df39d 100644
--- a/AWSLogs/AWSLogsService.m
+++ b/AWSLogs/AWSLogsService.m
@@ -26,7 +26,7 @@
#import "AWSLogsResources.h"
static NSString *const AWSInfoLogs = @"Logs";
-static NSString *const AWSLogsSDKVersion = @"2.6.12";
+static NSString *const AWSLogsSDKVersion = @"2.6.13";
@interface AWSLogsResponseSerializer : AWSJSONResponseSerializer
diff --git a/AWSLogs/Info.plist b/AWSLogs/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSLogs/Info.plist
+++ b/AWSLogs/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSMachineLearning.podspec b/AWSMachineLearning.podspec
index e6ba1c375ff..bfff15c58a2 100644
--- a/AWSMachineLearning.podspec
+++ b/AWSMachineLearning.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSMachineLearning'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,6 +12,6 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSMachineLearning/*.{h,m}'
end
diff --git a/AWSMachineLearning/AWSMachineLearningService.m b/AWSMachineLearning/AWSMachineLearningService.m
index 41bcb511c7c..7863bbae0cb 100644
--- a/AWSMachineLearning/AWSMachineLearningService.m
+++ b/AWSMachineLearning/AWSMachineLearningService.m
@@ -26,7 +26,7 @@
#import "AWSMachineLearningResources.h"
static NSString *const AWSInfoMachineLearning = @"MachineLearning";
-static NSString *const AWSMachineLearningSDKVersion = @"2.6.12";
+static NSString *const AWSMachineLearningSDKVersion = @"2.6.13";
diff --git a/AWSMachineLearning/Info.plist b/AWSMachineLearning/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSMachineLearning/Info.plist
+++ b/AWSMachineLearning/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSMobileAnalytics.podspec b/AWSMobileAnalytics.podspec
index c1a7ab36ce1..fe3d92b600c 100644
--- a/AWSMobileAnalytics.podspec
+++ b/AWSMobileAnalytics.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSMobileAnalytics'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,7 +12,7 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSMobileAnalytics/*.{h,m}', 'AWSMobileAnalytics/**/*.{h,m}'
s.private_header_files = 'AWSMobileAnalytics/Internal/*.h'
end
diff --git a/AWSMobileAnalytics/AWSMobileAnalyticsERS/AWSMobileAnalyticsERSService.m b/AWSMobileAnalytics/AWSMobileAnalyticsERS/AWSMobileAnalyticsERSService.m
index 4d520334cb8..40f82d88a86 100644
--- a/AWSMobileAnalytics/AWSMobileAnalyticsERS/AWSMobileAnalyticsERSService.m
+++ b/AWSMobileAnalytics/AWSMobileAnalyticsERS/AWSMobileAnalyticsERSService.m
@@ -26,7 +26,7 @@
#import "AWSMobileAnalyticsERSResources.h"
static NSString *const AWSInfoMobileAnalyticsERS = @"MobileAnalyticsERS";
-static NSString *const AWSMobileAnalyticsERSSDKVersion = @"2.6.12";
+static NSString *const AWSMobileAnalyticsERSSDKVersion = @"2.6.13";
@interface AWSMobileAnalyticsERSResponseSerializer : AWSJSONResponseSerializer
diff --git a/AWSMobileAnalytics/Info.plist b/AWSMobileAnalytics/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSMobileAnalytics/Info.plist
+++ b/AWSMobileAnalytics/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSMobileClient.podspec b/AWSMobileClient.podspec
index 2a7cdc5703c..f52adf6fecf 100644
--- a/AWSMobileClient.podspec
+++ b/AWSMobileClient.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSMobileClient'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,7 +12,7 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSAuthCore', '2.6.12'
+ s.dependency 'AWSAuthCore', '2.6.13'
s.source_files = 'AWSAuthSDK/Sources/AWSMobileClient/*.{h,m}'
s.public_header_files = 'AWSAuthSDK/Sources/AWSMobileClient/AWSMobileClient.h'
end
diff --git a/AWSPinpoint.podspec b/AWSPinpoint.podspec
index 11081107eed..3bba9b8b7ee 100644
--- a/AWSPinpoint.podspec
+++ b/AWSPinpoint.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSPinpoint'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,7 +12,7 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSPinpoint/*.{h,m}', 'AWSPinpoint/**/*.{h,m}'
s.private_header_files = 'AWSPinpoint/Internal/*.h'
end
diff --git a/AWSPinpoint/AWSPinpointAnalytics/AWSPinpointAnalyticsService.m b/AWSPinpoint/AWSPinpointAnalytics/AWSPinpointAnalyticsService.m
index 9575a8883cf..2c02e41f300 100644
--- a/AWSPinpoint/AWSPinpointAnalytics/AWSPinpointAnalyticsService.m
+++ b/AWSPinpoint/AWSPinpointAnalytics/AWSPinpointAnalyticsService.m
@@ -26,7 +26,7 @@
#import "AWSPinpointAnalyticsResources.h"
static NSString *const AWSInfoPinpointAnalytics = @"PinpointAnalytics";
-static NSString *const AWSPinpointAnalyticsSDKVersion = @"2.6.12";
+static NSString *const AWSPinpointAnalyticsSDKVersion = @"2.6.13";
@interface AWSPinpointAnalyticsResponseSerializer : AWSJSONResponseSerializer
diff --git a/AWSPinpoint/AWSPinpointTargeting/AWSPinpointTargetingService.m b/AWSPinpoint/AWSPinpointTargeting/AWSPinpointTargetingService.m
index 284f9d79860..14301b6b6e0 100644
--- a/AWSPinpoint/AWSPinpointTargeting/AWSPinpointTargetingService.m
+++ b/AWSPinpoint/AWSPinpointTargeting/AWSPinpointTargetingService.m
@@ -26,7 +26,7 @@
#import "AWSPinpointTargetingResources.h"
static NSString *const AWSInfoPinpointTargeting = @"PinpointTargeting";
-static NSString *const AWSPinpointTargetingSDKVersion = @"2.6.12";
+static NSString *const AWSPinpointTargetingSDKVersion = @"2.6.13";
@interface AWSPinpointTargetingResponseSerializer : AWSJSONResponseSerializer
diff --git a/AWSPinpoint/Info.plist b/AWSPinpoint/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSPinpoint/Info.plist
+++ b/AWSPinpoint/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSPolly.podspec b/AWSPolly.podspec
index 46e07f21e7e..2395673383c 100644
--- a/AWSPolly.podspec
+++ b/AWSPolly.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSPolly'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,6 +12,6 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSPolly/*.{h,m}'
end
diff --git a/AWSPolly/AWSPollyService.m b/AWSPolly/AWSPollyService.m
index c3dabe2d3fa..fedb52620a7 100644
--- a/AWSPolly/AWSPollyService.m
+++ b/AWSPolly/AWSPollyService.m
@@ -26,7 +26,7 @@
#import "AWSPollyResources.h"
static NSString *const AWSInfoPolly = @"Polly";
-static NSString *const AWSPollySDKVersion = @"2.6.12";
+static NSString *const AWSPollySDKVersion = @"2.6.13";
@interface AWSPollyResponseSerializer : AWSJSONResponseSerializer
diff --git a/AWSPolly/AWSPollySynthesizeSpeechURLBuilder.m b/AWSPolly/AWSPollySynthesizeSpeechURLBuilder.m
index c14bff639e3..caf8782b1ad 100644
--- a/AWSPolly/AWSPollySynthesizeSpeechURLBuilder.m
+++ b/AWSPolly/AWSPollySynthesizeSpeechURLBuilder.m
@@ -16,7 +16,7 @@
#import "AWSPollySynthesizeSpeechURLBuilder.h"
static NSString *const AWSInfoPollySynthesizeSpeechURLBuilder = @"PollySynthesizeSpeechUrlBuilder";
-static NSString *const AWSPollySDKVersion = @"2.6.12";
+static NSString *const AWSPollySDKVersion = @"2.6.13";
NSString *const AWSPollySynthesizeSpeechURLBuilderErrorDomain = @"com.amazonaws.AWSPollySynthesizeSpeechURLBuilderErrorDomain";
NSString *const AWSPollyPresignedUrlPath = @"v1/speech";
diff --git a/AWSPolly/Info.plist b/AWSPolly/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSPolly/Info.plist
+++ b/AWSPolly/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSRekognition.podspec b/AWSRekognition.podspec
index f71281971e2..85fc2688b2f 100644
--- a/AWSRekognition.podspec
+++ b/AWSRekognition.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSRekognition'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,6 +12,6 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSRekognition/*.{h,m}'
end
diff --git a/AWSRekognition/AWSRekognitionService.m b/AWSRekognition/AWSRekognitionService.m
index e1358e59874..bc423117fd0 100644
--- a/AWSRekognition/AWSRekognitionService.m
+++ b/AWSRekognition/AWSRekognitionService.m
@@ -26,7 +26,7 @@
#import "AWSRekognitionResources.h"
static NSString *const AWSInfoRekognition = @"Rekognition";
-static NSString *const AWSRekognitionSDKVersion = @"2.6.12";
+static NSString *const AWSRekognitionSDKVersion = @"2.6.13";
@interface AWSRekognitionResponseSerializer : AWSJSONResponseSerializer
diff --git a/AWSRekognition/Info.plist b/AWSRekognition/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSRekognition/Info.plist
+++ b/AWSRekognition/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSS3.podspec b/AWSS3.podspec
index 130e90f8513..9e9108dd0f4 100644
--- a/AWSS3.podspec
+++ b/AWSS3.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSS3'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,6 +12,6 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSS3/*.{h,m}'
end
diff --git a/AWSS3/AWSS3PreSignedURL.h b/AWSS3/AWSS3PreSignedURL.h
index 9f3a5991d7f..cbfff2282bc 100644
--- a/AWSS3/AWSS3PreSignedURL.h
+++ b/AWSS3/AWSS3PreSignedURL.h
@@ -204,7 +204,6 @@ typedef NS_ENUM(NSInteger, AWSS3PresignedURLErrorType) {
*/
@interface AWSS3GetPreSignedURLRequest : NSObject
-
/**
Returns whether the client has enabled accelerate mode for getting and putting objects. The default is `NO`.
*/
diff --git a/AWSS3/AWSS3PreSignedURL.m b/AWSS3/AWSS3PreSignedURL.m
index 5e8cee44b01..fa5d271dfb0 100644
--- a/AWSS3/AWSS3PreSignedURL.m
+++ b/AWSS3/AWSS3PreSignedURL.m
@@ -26,7 +26,7 @@
static NSString *const AWSS3PreSignedURLBuilderAcceleratedEndpoint = @"s3-accelerate.amazonaws.com";
static NSString *const AWSInfoS3PreSignedURLBuilder = @"S3PreSignedURLBuilder";
-static NSString *const AWSS3PreSignedURLBuilderSDKVersion = @"2.6.12";
+static NSString *const AWSS3PreSignedURLBuilderSDKVersion = @"2.6.13";
@interface AWSS3PreSignedURLBuilder()
@@ -46,6 +46,14 @@ - (void) setRegion:(AWSRegionType)regionType service:(AWSServiceType)serviceType
@end
+@interface AWSS3GetPreSignedURLRequest ()
+
+@property (nonatomic, strong) NSMutableDictionary *internalRequestParameters;
+@property (nonatomic, strong) NSMutableDictionary *internalRequestHeaders;
+@property NSString *uploadID;
+@property NSNumber *partNumber;
+@end
+
@implementation AWSS3PreSignedURLBuilder
static AWSSynchronizedMutableDictionary *_serviceClients = nil;
@@ -273,6 +281,17 @@ - (instancetype)initWithConfiguration:(AWSServiceConfiguration *)configuration {
}
[getPreSignedURLRequest setValue:host forRequestHeader:@"host"];
+ //If this is a presigned request for a multipart upload, set the uploadID and partNumber on the request.
+ if (getPreSignedURLRequest.uploadID
+ && getPreSignedURLRequest.partNumber) {
+
+ [getPreSignedURLRequest setValue:getPreSignedURLRequest.uploadID
+ forRequestParameter:@"uploadId"];
+
+ [getPreSignedURLRequest setValue:[NSString stringWithFormat:@"%@", getPreSignedURLRequest.partNumber]
+ forRequestParameter:@"partNumber"];
+ }
+
AWSEndpoint *newEndpoint = [[AWSEndpoint alloc]initWithRegion:configuration.regionType service:AWSServiceS3 URL:[NSURL URLWithString:[NSString stringWithFormat:@"%@://%@", endpoint.useUnsafeURL?@"http":@"https", host]]];
int32_t expireDuration = [expires timeIntervalSinceNow];
@@ -296,13 +315,6 @@ - (instancetype)initWithConfiguration:(AWSServiceConfiguration *)configuration {
@end
-@interface AWSS3GetPreSignedURLRequest ()
-
-@property (nonatomic, strong) NSMutableDictionary *internalRequestParameters;
-@property (nonatomic, strong) NSMutableDictionary *internalRequestHeaders;
-
-@end
-
@implementation AWSS3GetPreSignedURLRequest
- (instancetype)init {
diff --git a/AWSS3/AWSS3Service.m b/AWSS3/AWSS3Service.m
index dc18b4a33a9..c229c5da206 100644
--- a/AWSS3/AWSS3Service.m
+++ b/AWSS3/AWSS3Service.m
@@ -28,7 +28,7 @@
#import "AWSS3Serializer.h"
static NSString *const AWSInfoS3 = @"S3";
-static NSString *const AWSS3SDKVersion = @"2.6.12";
+static NSString *const AWSS3SDKVersion = @"2.6.13";
diff --git a/AWSS3/AWSS3TransferUtility+Validation.h b/AWSS3/AWSS3TransferUtility+Validation.h
new file mode 100644
index 00000000000..4624bda37c3
--- /dev/null
+++ b/AWSS3/AWSS3TransferUtility+Validation.h
@@ -0,0 +1,21 @@
+//
+// Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License").
+// You may not use this file except in compliance with the License.
+// A copy of the License is located at
+//
+// http://aws.amazon.com/apache2.0
+//
+// or in the "license" file accompanying this file. This file is distributed
+// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+// express or implied. See the License for the specific language governing
+// permissions and limitations under the License.
+//
+
+#import
+
+@interface AWSS3TransferUtility (Validation)
+- (AWSTask *) validateParameters: (NSString * )bucket fileURL:(NSURL *)fileURL accelerationModeEnabled: (BOOL) accelerationModeEnabled;
+
+@end
diff --git a/AWSS3/AWSS3TransferUtility+Validation.m b/AWSS3/AWSS3TransferUtility+Validation.m
new file mode 100644
index 00000000000..b12df40a922
--- /dev/null
+++ b/AWSS3/AWSS3TransferUtility+Validation.m
@@ -0,0 +1,47 @@
+//
+// Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License").
+// You may not use this file except in compliance with the License.
+// A copy of the License is located at
+//
+// http://aws.amazon.com/apache2.0
+//
+// or in the "license" file accompanying this file. This file is distributed
+// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+// express or implied. See the License for the specific language governing
+// permissions and limitations under the License.
+//
+
+#import "AWSS3TransferUtility+Validation.h"
+
+@implementation AWSS3TransferUtility (Validation)
+
+- (AWSTask *) validateParameters: (NSString * )bucket fileURL:(NSURL *)fileURL accelerationModeEnabled: (BOOL) accelerationModeEnabled
+{
+ //Validate input parameter: bucket
+ if (!bucket || [bucket length] == 0) {
+ NSInteger errorCode = (accelerationModeEnabled) ?
+ AWSS3PresignedURLErrorInvalidBucketNameForAccelerateModeEnabled : AWSS3PresignedURLErrorInvalidBucketName;
+
+ NSString *errorMessage = @"Invalid bucket specified. Please specify a bucket name or configure the bucket property in `AWSS3TransferUtilityConfiguration`.";
+ NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errorMessage
+ forKey:NSLocalizedDescriptionKey];
+
+ return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3PresignedURLErrorDomain
+ code:errorCode
+ userInfo:userInfo]];
+ }
+
+ NSString *filePath = [fileURL path];
+ // Error out if the length of file name < minimum file path length (2 characters) or file does not exist
+ if ([filePath length] < 2 ||
+ ! [[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
+ AWSDDLogDebug(@"I am here");
+ return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferUtilityErrorDomain
+ code:AWSS3TransferUtilityErrorLocalFileNotFound
+ userInfo:nil]];
+ }
+ return nil;
+}
+@end
diff --git a/AWSS3/AWSS3TransferUtility.h b/AWSS3/AWSS3TransferUtility.h
index f1ac6910e4e..7817d4a2771 100644
--- a/AWSS3/AWSS3TransferUtility.h
+++ b/AWSS3/AWSS3TransferUtility.h
@@ -16,6 +16,8 @@
#import
#import
+#import "AWSS3Service.h"
+
NS_ASSUME_NONNULL_BEGIN
FOUNDATION_EXPORT NSString *const AWSS3TransferUtilityErrorDomain;
@@ -27,16 +29,20 @@ typedef NS_ENUM(NSInteger, AWSS3TransferUtilityErrorType) {
AWSS3TransferUtilityErrorLocalFileNotFound
};
+
FOUNDATION_EXPORT NSString *const AWSS3TransferUtilityURLSessionDidBecomeInvalidNotification;
@class AWSS3TransferUtilityConfiguration;
@class AWSS3TransferUtilityTask;
@class AWSS3TransferUtilityUploadTask;
+@class AWSS3TransferUtilityMultiPartUploadTask;
@class AWSS3TransferUtilityDownloadTask;
@class AWSS3TransferUtilityExpression;
@class AWSS3TransferUtilityUploadExpression;
+@class AWSS3TransferUtilityMultiPartUploadExpression;
@class AWSS3TransferUtilityDownloadExpression;
+
/**
The upload completion handler.
@@ -46,6 +52,15 @@ FOUNDATION_EXPORT NSString *const AWSS3TransferUtilityURLSessionDidBecomeInvalid
typedef void (^AWSS3TransferUtilityUploadCompletionHandlerBlock) (AWSS3TransferUtilityUploadTask *task,
NSError * _Nullable error);
+/**
+ The upload completion handler for MultiPart.
+
+ @param task The upload task object.
+ @param error Returns the error object when the download failed.
+ */
+typedef void (^AWSS3TransferUtilityMultiPartUploadCompletionHandlerBlock) (AWSS3TransferUtilityMultiPartUploadTask *task,
+ NSError * _Nullable error);
+
/**
The download completion handler.
@@ -70,6 +85,16 @@ typedef void (^AWSS3TransferUtilityDownloadCompletionHandlerBlock) (AWSS3Transfe
typedef void (^AWSS3TransferUtilityProgressBlock) (AWSS3TransferUtilityTask *task,
NSProgress *progress);
+/**
+ The multi part transfer progress feedback block.
+
+ @param task The upload task object.
+ @param progress The progress object.
+*/
+typedef void (^AWSS3TransferUtilityMultiPartProgressBlock) (AWSS3TransferUtilityMultiPartUploadTask *task,
+ NSProgress *progress);
+
+
#pragma mark - AWSS3TransferUtility
/**
@@ -257,7 +282,7 @@ typedef void (^AWSS3TransferUtilityProgressBlock) (AWSS3TransferUtilityTask *tas
+ (void)removeS3TransferUtilityForKey:(NSString *)key;
/**
- Tells `AWSS3TransferUtility` that events related to a URL session are waiting to be processed. This method needs to be called in the `- application:handleEventsForBackgroundURLSession:completionHandler:` applicatoin delegate for `AWSS3TransferUtility` to work.
+ Tells `AWSS3TransferUtility` that events related to a URL session are waiting to be processed. This method needs to be called in the `- application:handleEventsForBackgroundURLSession:completionHandler:` application delegate for `AWSS3TransferUtility` to work.
@param application The singleton app object.
@param identifier The identifier of the URL session requiring attention.
@@ -340,6 +365,84 @@ handleEventsForBackgroundURLSession:(NSString *)identifier
expression:(nullable AWSS3TransferUtilityUploadExpression *)expression
completionHandler:(nullable AWSS3TransferUtilityUploadCompletionHandlerBlock)completionHandler;
+/**
+ Saves the `NSData` to a temporary directory and uploads it to the configured Amazon S3 bucket in `AWSS3TransferUtilityConfiguration` using Multipart.
+
+ @param data The data to upload.
+ @param key The Amazon S3 object key name.
+ @param contentType `Content-Type` of the data.
+ @param expression The container object to configure the upload request.
+ @param completionHandler The completion handler when the upload completes.
+
+ @return Returns an instance of `AWSTask`. On successful initialization, `task.result` contains an instance of `AWSS3TransferUtilityMultiPartUploadTask`.
+ */
+- (AWSTask *)uploadDataUsingMultiPart:(NSData *)data
+ key:(NSString *)key
+ contentType:(NSString *)contentType
+ expression:(nullable AWSS3TransferUtilityMultiPartUploadExpression *)expression
+ completionHandler:(nullable AWSS3TransferUtilityMultiPartUploadCompletionHandlerBlock)completionHandler
+ NS_SWIFT_NAME(uploadUsingMultiPart(data:key:contentType:expression:completionHandler:));
+
+/**
+ Saves the `NSData` to a temporary directory and uploads it to the specified Amazon S3 bucket using Multipart.
+
+ @param data The data to upload.
+ @param bucket The Amazon S3 bucket name.
+ @param key The Amazon S3 object key name.
+ @param contentType `Content-Type` of the data.
+ @param expression The container object to configure the upload request.
+ @param completionHandler The completion handler when the upload completes.
+
+ @return Returns an instance of `AWSTask`. On successful initialization, `task.result` contains an instance of `AWSS3TransferUtilityMultiPartUploadTask`.
+ */
+- (AWSTask *)uploadDataUsingMultiPart:(NSData *)data
+ bucket:(NSString *)bucket
+ key:(NSString *)key
+ contentType:(NSString *)contentType
+ expression:(nullable AWSS3TransferUtilityMultiPartUploadExpression *)expression
+ completionHandler:(nullable AWSS3TransferUtilityMultiPartUploadCompletionHandlerBlock)completionHandler
+ NS_SWIFT_NAME(uploadUsingMultiPart(data:bucket:key:contentType:expression:completionHandler:));
+
+/**
+ Uploads the file to the configured Amazon S3 bucket in `AWSS3TransferUtilityConfiguration` using MultiPart.
+
+ @param fileURL The file URL of the file to upload.
+ @param key The Amazon S3 object key name.
+ @param contentType `Content-Type` of the file.
+ @param expression The container object to configure the upload request.
+ @param completionHandler The completion handler when the upload completes.
+
+ @return Returns an instance of `AWSTask`. On successful initialization, `task.result` contains an instance of `AWSS3TransferUtilityMultiPartUploadTask`.
+ */
+- (AWSTask *)uploadFileUsingMultiPart:(NSURL *)fileURL
+ key:(NSString *)key
+ contentType:(NSString *)contentType
+ expression:(nullable AWSS3TransferUtilityMultiPartUploadExpression *)expression
+ completionHandler:(nullable AWSS3TransferUtilityMultiPartUploadCompletionHandlerBlock)completionHandler
+ NS_SWIFT_NAME(uploadUsingMultiPart(fileURL:key:contentType:expression:completionHandler:));
+
+
+/**
+ Uploads the file to the specified Amazon S3 bucket using MultiPart.
+
+ @param fileURL The file URL of the file to upload.
+ @param bucket The Amazon S3 bucket name.
+ @param key The Amazon S3 object key name.
+ @param contentType `Content-Type` of the file.
+ @param expression The container object to configure the upload request.
+ @param completionHandler The completion handler when the upload completes.
+
+ @return Returns an instance of `AWSTask`. On successful initialization, `task.result` contains an instance of `AWSS3TransferUtilityMultiPartUploadTask`.
+ */
+- (AWSTask *)uploadFileUsingMultiPart:(NSURL *)fileURL
+ bucket:(NSString *)bucket
+ key:(NSString *)key
+ contentType:(NSString *)contentType
+ expression:(nullable AWSS3TransferUtilityMultiPartUploadExpression *)expression
+ completionHandler:(nullable AWSS3TransferUtilityMultiPartUploadCompletionHandlerBlock)completionHandler
+ NS_SWIFT_NAME(uploadUsingMultiPart(fileURL:bucket:key:contentType:expression:completionHandler:));
+
+
/**
Downloads the specified Amazon S3 object as `NSData` from the bucket configured in `AWSS3TransferUtilityConfiguration`.
@@ -404,8 +507,8 @@ handleEventsForBackgroundURLSession:(NSString *)identifier
/**
Assigns progress feedback and completion handler blocks. This method should be called when the app was suspended while the transfer is still happening.
- @param uploadBlocksAssigner The block for assigning the upload pregree feedback and completion handler blocks.
- @param downloadBlocksAssigner The block for assigning the download pregree feedback and completion handler blocks.
+ @param uploadBlocksAssigner The block for assigning the upload progress feedback and completion handler blocks.
+ @param downloadBlocksAssigner The block for assigning the download progress feedback and completion handler blocks.
*/
- (void)enumerateToAssignBlocksForUploadTask:(nullable void (^)(AWSS3TransferUtilityUploadTask *uploadTask,
_Nullable AWSS3TransferUtilityProgressBlock * _Nullable uploadProgressBlockReference,
@@ -415,11 +518,28 @@ handleEventsForBackgroundURLSession:(NSString *)identifier
_Nullable AWSS3TransferUtilityDownloadCompletionHandlerBlock * _Nullable completionHandlerReference))downloadBlocksAssigner;
/**
- Retrieves all running tasks.
+ Assigns progress feedback and completion handler blocks. This method should be called when the app was suspended while the transfer is still happening.
+
+ @param uploadBlocksAssigner The block for assigning the upload progress feedback and completion handler blocks.
+ @param multiPartUploadBlocksAssigner The block for assigning the multipart upload progress feedback and completion handler blocks.
+ @param downloadBlocksAssigner The block for assigning the download progress feedback and completion handler blocks.
+ */
+-(void)enumerateToAssignBlocksForUploadTask:(void (^)(AWSS3TransferUtilityUploadTask *uploadTask,
+ AWSS3TransferUtilityProgressBlock _Nullable * _Nullable uploadProgressBlockReference,
+ AWSS3TransferUtilityUploadCompletionHandlerBlock _Nullable * _Nullable completionHandlerReference))uploadBlocksAssigner
+ multiPartUploadBlocksAssigner: (void (^) (AWSS3TransferUtilityMultiPartUploadTask *multiPartUploadTask,
+ AWSS3TransferUtilityMultiPartProgressBlock _Nullable * _Nullable multiPartUploadProgressBlockReference,
+ AWSS3TransferUtilityMultiPartUploadCompletionHandlerBlock _Nullable * _Nullable completionHandlerReference)) multiPartUploadBlocksAssigner
+ downloadBlocksAssigner:(void (^)(AWSS3TransferUtilityDownloadTask *downloadTask,
+ AWSS3TransferUtilityProgressBlock _Nullable * _Nullable downloadProgressBlockReference,
+ AWSS3TransferUtilityDownloadCompletionHandlerBlock _Nullable * _Nullable completionHandlerReference))downloadBlocksAssigner;
- @return An array of `AWSS3TransferUtilityTask`.
+/**
+ Retrieves all running tasks.
+ @deprecated Use `getUploadTasks:, getMultiPartUploadTasks: and getDownloadTasks:` methods instead.
+ @return An array of containing `AWSS3TransferUtilityUploadTask` and `AWSS3TransferUtilityDownloadTask` objects.
*/
-- (AWSTask *> *)getAllTasks;
+- (AWSTask *> *)getAllTasks __attribute((deprecated));
/**
Retrieves all running upload tasks.
@@ -428,6 +548,13 @@ handleEventsForBackgroundURLSession:(NSString *)identifier
*/
- (AWSTask *> *)getUploadTasks;
+/**
+ Retrieves all running MultiPart upload tasks.
+
+ @return An array of `AWSS3TransferUtilityMultiPartUploadTask`.
+ */
+- (AWSTask *> *)getMultiPartUploadTasks;
+
/**
Retrieves all running download tasks.
@@ -445,6 +572,9 @@ handleEventsForBackgroundURLSession:(NSString *)identifier
@property (nonatomic, nullable, copy) NSString *bucket;
+@property NSInteger retryLimit;
+
+@property (nonatomic, nullable) NSNumber *multiPartConcurrencyLimit;
@end
@@ -455,6 +585,12 @@ handleEventsForBackgroundURLSession:(NSString *)identifier
*/
@interface AWSS3TransferUtilityTask : NSObject
+/**
+ An identifier uniquely identifies the transferID.
+ */
+
+@property (readonly) NSString *transferID;
+
/**
An identifier uniquely identifies the task within a given `AWSS3TransferUtility` instance.
*/
@@ -514,6 +650,48 @@ handleEventsForBackgroundURLSession:(NSString *)identifier
@end
+/**
+ The task object to represent a multipart upload task.
+ */
+@interface AWSS3TransferUtilityMultiPartUploadTask: NSObject
+
+/**
+ An identifier uniquely identifies the transferID.
+ */
+@property (readonly) NSString *transferID;
+
+/**
+ The Amazon S3 bucket name associated with the transfer.
+ */
+@property (readonly) NSString *bucket;
+
+/**
+ The Amazon S3 object key name associated with the transfer.
+ */
+@property (readonly) NSString *key;
+
+/**
+ The transfer progress.
+ */
+@property (readonly) NSProgress *progress;
+
+/**
+ Cancels the task.
+ */
+- (void)cancel;
+
+/**
+ Resumes the task, if it is suspended.
+ */
+- (void)resume;
+
+/**
+ Temporarily suspends a task.
+ */
+- (void)suspend;
+@end
+
+
/**
The task object to represent a download task.
*/
@@ -573,6 +751,45 @@ handleEventsForBackgroundURLSession:(NSString *)identifier
@end
+
+/**
+ The expression object for configuring a Multipart upload task.
+ */
+@interface AWSS3TransferUtilityMultiPartUploadExpression : NSObject
+
+/**
+ This NSDictionary can contains additional request headers to be included in the pre-signed URL. Default is emtpy.
+ */
+@property (nonatomic, readonly) NSDictionary *requestHeaders;
+
+/**
+ This NSDictionary can contains additional request parameters to be included in the pre-signed URL. Adding additional request parameters enables more advanced pre-signed URLs, such as accessing Amazon S3's torrent resource for an object, or for specifying a version ID when accessing an object. Default is emtpy.
+ */
+@property (nonatomic, readonly) NSDictionary *requestParameters;
+
+/**
+ The progress feedback block.
+ */
+@property (copy, nonatomic, nullable) AWSS3TransferUtilityMultiPartProgressBlock progressBlock;
+/**
+ Set an additional request header to be included in the pre-signed URL.
+
+ @param value The value of the request parameter being added. Set to nil if parameter doesn't contains value.
+ @param requestHeader The name of the request header.
+ */
+- (void)setValue:(nullable NSString *)value forRequestHeader:(NSString *)requestHeader;
+
+/**
+ Set an additional request parameter to be included in the pre-signed URL. Adding additional request parameters enables more advanced pre-signed URLs, such as accessing Amazon S3's torrent resource for an object, or for specifying a version ID when accessing an object.
+
+ @param value The value of the request parameter being added. Set to nil if parameter doesn't contains value.
+ @param requestParameter The name of the request parameter, as it appears in the URL's query string (e.g. AWSS3PresignedURLVersionID).
+ */
+- (void)setValue:(nullable NSString *)value forRequestParameter:(NSString *)requestParameter;
+
+@end
+
+
/**
The expression object for configuring a download task.
*/
diff --git a/AWSS3/AWSS3TransferUtility.m b/AWSS3/AWSS3TransferUtility.m
index 1ad7436834f..512e1f6bad8 100644
--- a/AWSS3/AWSS3TransferUtility.m
+++ b/AWSS3/AWSS3TransferUtility.m
@@ -15,57 +15,116 @@
#import "AWSS3TransferUtility.h"
#import "AWSS3PreSignedURL.h"
+#import "AWSS3Service.h"
#import "AWSSynchronizedMutableDictionary.h"
+#import "AWSFMDB.h"
+#import "AWSS3TransferUtility+Validation.h"
+
// Public constants
NSString *const AWSS3TransferUtilityErrorDomain = @"com.amazonaws.AWSS3TransferUtilityErrorDomain";
NSString *const AWSS3TransferUtilityURLSessionDidBecomeInvalidNotification = @"com.amazonaws.AWSS3TransferUtility.AWSS3TransferUtilityURLSessionDidBecomeInvalidNotification";
-// Private constnats
+
+// Private constants
static NSString *const AWSS3TransferUtilityIdentifier = @"com.amazonaws.AWSS3TransferUtility.Identifier";
static NSTimeInterval const AWSS3TransferUtilityTimeoutIntervalForResource = 50 * 60; // 50 minutes
static NSString *const AWSS3TransferUtilityUserAgent = @"transfer-utility";
static NSString *const AWSInfoS3TransferUtility = @"S3TransferUtility";
+static NSString *const AWSS3TransferUtilityRetryExceeded = @"AWSS3TransferUtilityRetryExceeded";
+static NSString *const AWSS3TransferUtilityRetrySucceeded = @"AWSS3TransferUtilityRetrySucceeded";
+static NSUInteger const AWSS3TransferUtilityMultiPartSize = 5 * 1024 * 1024;
+static NSString *const AWSS3TransferUtiltityRequestTimeoutErrorCode = @"RequestTimeout";
+static int const AWSS3TransferUtilityMultiPartDefaultConcurrencyLimit = 5;
+
#pragma mark - Private classes
+@interface AWSS3TransferUtilityUploadSubTask: NSObject
+@end
+
+@interface AWSS3TransferUtilityUploadSubTask()
+@property (strong, nonatomic) NSURLSessionTask *sessionTask;
+@property (strong, nonatomic) NSNumber *partNumber;
+@property (readwrite) NSUInteger taskIdentifier;
+@property (strong, nonatomic) NSString *eTag;
+@property int64_t totalBytesExpectedToSend;
+@property int64_t totalBytesSent;
+@property NSString *responseData;
+@property NSString *file;
+@property NSString *transferID;
+@property NSString *status;
+@property NSString *uploadID;
+
+@end
+
@interface AWSS3TransferUtility()
@property (strong, nonatomic) AWSServiceConfiguration *configuration;
@property (strong, nonatomic) AWSS3TransferUtilityConfiguration *transferUtilityConfiguration;
@property (strong, nonatomic) AWSS3PreSignedURLBuilder *preSignedURLBuilder;
+@property (strong, nonatomic) AWSS3 *s3;
@property (strong, nonatomic) NSURLSession *session;
@property (strong, nonatomic) NSString *sessionIdentifier;
-@property (strong, nonatomic) NSString *temporaryDirectoryPath;
+@property (strong, nonatomic) NSString *cacheDirectoryPath;
@property (strong, nonatomic) AWSSynchronizedMutableDictionary *taskDictionary;
@property (copy, nonatomic) void (^backgroundURLSessionCompletionHandler)(void);
-
+@property (strong, nonatomic) AWSFMDatabaseQueue *databaseQueue;
@end
@interface AWSS3TransferUtilityTask()
@property (strong, nonatomic) NSURLSessionTask *sessionTask;
-
+@property (strong, nonatomic) NSString *transferID;
@property (strong, nonatomic) NSString *bucket;
@property (strong, nonatomic) NSString *key;
-
-// Temporary storages
@property (strong, nonatomic) NSData *data;
@property (strong, nonatomic) NSURL *location;
@property (strong, nonatomic) NSError *error;
-
+@property int retryCount;
+@property NSString *nsURLSessionID;
+@property NSString *file;
+@property NSString *transferType;
+@property NSString *status;
+@property (strong) AWSFMDatabaseQueue *databaseQueue;
@end
@interface AWSS3TransferUtilityUploadTask()
@property (strong, nonatomic) AWSS3TransferUtilityUploadExpression *expression;
+@property NSString *responseData;
+@property (atomic) BOOL cancelled;
+@property BOOL temporaryFileCreated;
+@end
+@interface AWSS3TransferUtilityMultiPartUploadTask()
+
+@property (strong, nonatomic) AWSS3TransferUtilityMultiPartUploadExpression *expression;
+@property NSString * uploadID;
+@property BOOL cancelled;
+@property BOOL temporaryFileCreated;
+@property NSMutableDictionary *waitingPartsDictionary;
+@property (strong, nonatomic) NSMutableDictionary *completedPartsDictionary;
+@property (strong, nonatomic) NSMutableDictionary *inProgressPartsDictionary;
+@property int retryCount;
+@property int partNumber;
+@property NSString *file;
+@property NSString *transferType;
+@property NSString *nsURLSessionID;
+@property (strong) AWSFMDatabaseQueue *databaseQueue;
+@property (strong, nonatomic) NSError *error;
+@property (strong, nonatomic) NSString *bucket;
+@property (strong, nonatomic) NSString *key;
+@property (strong, nonatomic) NSString *transferID;
+@property NSString *status;
+@property NSNumber *contentLength;
@end
@interface AWSS3TransferUtilityDownloadTask()
@property (strong, nonatomic) AWSS3TransferUtilityDownloadExpression *expression;
-
+@property BOOL cancelled;
+@property NSString *responseData;
@end
@interface AWSS3TransferUtilityExpression()
@@ -85,6 +144,17 @@ @interface AWSS3TransferUtilityUploadExpression()
@end
+@interface AWSS3TransferUtilityMultiPartUploadExpression()
+
+@property (strong, nonatomic) NSMutableDictionary *internalRequestHeaders;
+@property (strong, nonatomic) NSMutableDictionary *internalRequestParameters;
+- (void)assignRequestParameters:(AWSS3GetPreSignedURLRequest *)getPreSignedURLRequest;
+- (void)assignRequestHeaders:(AWSS3GetPreSignedURLRequest *)getPreSignedURLRequest;
+@property (copy, atomic) AWSS3TransferUtilityMultiPartUploadCompletionHandlerBlock completionHandler;
+
+@end
+
+
@interface AWSS3TransferUtilityDownloadExpression()
@property (copy, atomic) AWSS3TransferUtilityDownloadCompletionHandlerBlock completionHandler;
@@ -97,6 +167,19 @@ - (instancetype)initWithConfiguration:(AWSServiceConfiguration *)configuration;
@end
+
+@interface AWSS3()
+
+- (instancetype)initWithConfiguration:(AWSServiceConfiguration *)configuration;
+
+@end
+
+
+@interface AWSS3GetPreSignedURLRequest()
+@property NSString *uploadID;
+@property NSNumber *partNumber;
+@end
+
#pragma mark - AWSS3TransferUtility
@implementation AWSS3TransferUtility
@@ -112,6 +195,7 @@ + (instancetype)defaultS3TransferUtility {
AWSServiceConfiguration *serviceConfiguration = nil;
AWSS3TransferUtilityConfiguration *transferUtilityConfiguration = nil;
AWSServiceInfo *serviceInfo = [[AWSInfo defaultAWSInfo] defaultServiceInfo:AWSInfoS3TransferUtility];
+
if (serviceInfo) {
serviceConfiguration = [[AWSServiceConfiguration alloc] initWithRegion:serviceInfo.region
credentialsProvider:serviceInfo.cognitoCredentialsProvider];
@@ -172,7 +256,7 @@ + (instancetype)S3TransferUtilityForKey:(NSString *)key {
forKey:key];
if (serviceInfo) {
AWSServiceConfiguration *serviceConfiguration = [[AWSServiceConfiguration alloc] initWithRegion:serviceInfo.region
- credentialsProvider:serviceInfo.cognitoCredentialsProvider];
+ credentialsProvider:serviceInfo.cognitoCredentialsProvider];
NSNumber *accelerateModeEnabled = [serviceInfo.infoDictionary valueForKey:@"AccelerateModeEnabled"];
AWSS3TransferUtilityConfiguration *transferUtilityConfiguration = [AWSS3TransferUtilityConfiguration new];
@@ -209,14 +293,16 @@ - (instancetype)initWithConfiguration:(AWSServiceConfiguration *)serviceConfigur
[_configuration addUserAgentProductToken:AWSS3TransferUtilityUserAgent];
_transferUtilityConfiguration = [transferUtilityConfiguration copy];
- _transferUtilityConfiguration.bucket = transferUtilityConfiguration.bucket;
-
+
_preSignedURLBuilder = [[AWSS3PreSignedURLBuilder alloc] initWithConfiguration:_configuration];
+ _s3 = [[AWSS3 alloc] initWithConfiguration:_configuration];
if (identifier) {
_sessionIdentifier = identifier;
- } else {
- _sessionIdentifier = AWSS3TransferUtilityIdentifier;
+ }
+ else {
+ NSString *uuid = [[NSUUID UUID] UUIDString];
+ _sessionIdentifier = [AWSS3TransferUtilityIdentifier stringByAppendingString:uuid];
}
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:_sessionIdentifier];
@@ -237,9 +323,15 @@ - (instancetype)initWithConfiguration:(AWSServiceConfiguration *)serviceConfigur
_taskDictionary = [AWSSynchronizedMutableDictionary new];
- // Creates a temporary directory for data uploads
- _temporaryDirectoryPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[_sessionIdentifier aws_md5String]];
- NSURL *directoryURL = [NSURL fileURLWithPath:_temporaryDirectoryPath];
+ // Creates a temporary directory for data uploads in the caches directory
+
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
+ NSString *cachePath = [paths objectAtIndex:0];
+
+ _cacheDirectoryPath = [cachePath stringByAppendingPathComponent:AWSInfoS3TransferUtility];
+ AWSDDLogDebug(@"Temporary dir Path is %@", _cacheDirectoryPath);
+
+ NSURL *directoryURL = [NSURL fileURLWithPath:_cacheDirectoryPath];
NSError *error = nil;
BOOL result = [[NSFileManager defaultManager] createDirectoryAtURL:directoryURL
withIntermediateDirectories:YES
@@ -248,16 +340,293 @@ - (instancetype)initWithConfiguration:(AWSServiceConfiguration *)serviceConfigur
if (!result) {
AWSDDLogError(@"Failed to create a temporary directory: %@", error);
}
-
- // Clean up the temporary directory
- __weak AWSS3TransferUtility *weakSelf = self;
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
- [weakSelf cleanUpTemporaryDirectory];
- });
+
+ //Instantiate the Database Helper
+ self.databaseQueue = [self createDatabase];
}
return self;
}
+#pragma mark - recovery methods
+
+- (void) recover:(void (^)(AWSS3TransferUtilityUploadTask *uploadTask,
+ AWSS3TransferUtilityProgressBlock *uploadProgressBlockReference,
+ AWSS3TransferUtilityUploadCompletionHandlerBlock *completionHandlerReference))uploadBlocksAssigner
+multiPartUploadBlocksAssigner: (void (^) (AWSS3TransferUtilityMultiPartUploadTask *multiPartUploadTask,
+ AWSS3TransferUtilityMultiPartProgressBlock *multiPartUploadProgressBlockReference,
+ AWSS3TransferUtilityMultiPartUploadCompletionHandlerBlock *completionHandlerReference)) multiPartUploadBlocksAssigner
+downloadBlocksAssigner:(void (^)(AWSS3TransferUtilityDownloadTask *downloadTask,
+ AWSS3TransferUtilityProgressBlock *downloadProgressBlockReference,
+ AWSS3TransferUtilityDownloadCompletionHandlerBlock *completionHandlerReference))downloadBlocksAssigner
+ completionHandler:(void (^)(NSError *_Nullable error)) completionHandler {
+
+ //Create temporary datastructures to hold the database records.
+ NSMutableDictionary *multiPartUploads = [NSMutableDictionary new];
+ NSMutableDictionary *transferRequests = [NSMutableDictionary new];
+
+ //Get All Tasks from DB
+ NSMutableArray *tasks = [self getTransferTaskDataFromDB:_sessionIdentifier];
+
+
+ //Iterate through the tasks and populate
+ for( NSMutableDictionary *task in tasks ) {
+ NSString *transferType = [task objectForKey:@"transfer_type"];
+ int sessionTaskID = [[task objectForKey:@"session_task_id"] intValue];
+
+ if ([transferType isEqualToString:@"UPLOAD"]) {
+ AWSS3TransferUtilityUploadTask *transferUtilityUploadTask = [AWSS3TransferUtilityUploadTask new];
+ transferUtilityUploadTask.nsURLSessionID = self.sessionIdentifier;
+ transferUtilityUploadTask.databaseQueue = self.databaseQueue;
+ transferUtilityUploadTask.bucket = [task objectForKey:@"bucket"];
+ transferUtilityUploadTask.key = [task objectForKey:@"key"];
+ transferUtilityUploadTask.expression = [AWSS3TransferUtilityUploadExpression new];
+ transferUtilityUploadTask.expression.internalRequestHeaders = [[self getDictionaryFromJson:[task objectForKey:@"request_headers"]] mutableCopy];
+ transferUtilityUploadTask.expression.internalRequestParameters = [[self getDictionaryFromJson:[task objectForKey:@"request_parameters"]] mutableCopy];
+ transferUtilityUploadTask.transferID = [task objectForKey:@"transfer_id"];
+ transferUtilityUploadTask.file = [task objectForKey:@"file"];
+ transferUtilityUploadTask.cancelled = NO;
+ transferUtilityUploadTask.retryCount = [[task objectForKey:@"retry_count"] intValue];
+ transferUtilityUploadTask.temporaryFileCreated = [[task objectForKey:@"temporary_file_created"] boolValue];
+ transferUtilityUploadTask.status = [task objectForKey:@"status"];
+
+ //Add the progress block and callback function
+ if (uploadBlocksAssigner) {
+ AWSS3TransferUtilityProgressBlock progressBlock = nil;
+ AWSS3TransferUtilityUploadCompletionHandlerBlock completionHandler = nil;
+
+ uploadBlocksAssigner(transferUtilityUploadTask, &progressBlock, &completionHandler);
+
+ if (progressBlock) {
+ transferUtilityUploadTask.expression.progressBlock = progressBlock;
+ }
+ if (completionHandler) {
+ transferUtilityUploadTask.expression.completionHandler = completionHandler;
+ }
+ }
+
+ //Lodge in temporary Dictionary
+ [transferRequests setObject:transferUtilityUploadTask forKey:@(sessionTaskID)];
+ }
+ else if ([transferType isEqualToString:@"DOWNLOAD"]) {
+ AWSS3TransferUtilityDownloadTask *transferUtilityDownloadTask = [AWSS3TransferUtilityDownloadTask new];
+ transferUtilityDownloadTask.nsURLSessionID = self.sessionIdentifier;
+ transferUtilityDownloadTask.databaseQueue = self.databaseQueue;
+ transferUtilityDownloadTask.bucket = [task objectForKey:@"bucket"];
+ transferUtilityDownloadTask.key = [task objectForKey:@"key"];
+ transferUtilityDownloadTask.expression = [AWSS3TransferUtilityDownloadExpression new];
+ transferUtilityDownloadTask.expression.internalRequestHeaders = [[self getDictionaryFromJson:[task objectForKey:@"request_headers"]] mutableCopy];
+ transferUtilityDownloadTask.expression.internalRequestParameters = [[self getDictionaryFromJson:[task objectForKey:@"request_parameters"]] mutableCopy];
+ transferUtilityDownloadTask.transferID = [task objectForKey:@"transfer_id"];
+ transferUtilityDownloadTask.file = [task objectForKey:@"file"];
+ transferUtilityDownloadTask.cancelled = NO;
+ transferUtilityDownloadTask.retryCount = [[task objectForKey:@"retry_count"] intValue];
+ transferUtilityDownloadTask.status = [task objectForKey:@"status"];
+
+ //Add the progress block and callback Function
+ if (downloadBlocksAssigner) {
+ AWSS3TransferUtilityProgressBlock progressBlock = nil;
+ AWSS3TransferUtilityDownloadCompletionHandlerBlock completionHandler = nil;
+
+ downloadBlocksAssigner(transferUtilityDownloadTask, &progressBlock, &completionHandler);
+
+ if (progressBlock) {
+ transferUtilityDownloadTask.expression.progressBlock = progressBlock;
+ }
+ if (completionHandler) {
+ transferUtilityDownloadTask.expression.completionHandler = completionHandler;
+ }
+ }
+
+ //Lodge in temporary Dictionary
+ [transferRequests setObject:transferUtilityDownloadTask forKey:@(sessionTaskID)];
+
+ [self.taskDictionary setObject:transferUtilityDownloadTask forKey:@(sessionTaskID) ];
+ AWSDDLogDebug(@"Added Download Transfer task %d to task dictionary", sessionTaskID);
+ }
+ else if ([transferType isEqualToString:@"MULTI_PART_UPLOAD"]) {
+ AWSS3TransferUtilityMultiPartUploadTask *transferUtilityMultiPartUploadTask = [AWSS3TransferUtilityMultiPartUploadTask new];
+ transferUtilityMultiPartUploadTask.nsURLSessionID = self.sessionIdentifier;
+ transferUtilityMultiPartUploadTask.databaseQueue = self.databaseQueue;
+ transferUtilityMultiPartUploadTask.bucket = [task objectForKey:@"bucket"];
+ transferUtilityMultiPartUploadTask.key = [task objectForKey:@"key"];
+ transferUtilityMultiPartUploadTask.expression = [AWSS3TransferUtilityMultiPartUploadExpression new];
+ transferUtilityMultiPartUploadTask.expression.internalRequestHeaders = [[self getDictionaryFromJson:[task objectForKey:@"request_headers"]] mutableCopy];
+ transferUtilityMultiPartUploadTask.expression.internalRequestParameters = [[self getDictionaryFromJson:[task objectForKey:@"request_parameters"]] mutableCopy];
+ transferUtilityMultiPartUploadTask.transferID = [task objectForKey:@"transfer_id"];
+ transferUtilityMultiPartUploadTask.file = [task objectForKey:@"file"];
+ transferUtilityMultiPartUploadTask.temporaryFileCreated = [[task objectForKey:@"temporary_file_created"] boolValue];
+ transferUtilityMultiPartUploadTask.contentLength = [task objectForKey:@"content_length"];
+ transferUtilityMultiPartUploadTask.cancelled = NO;
+ transferUtilityMultiPartUploadTask.retryCount = [[task objectForKey:@"retry_count"] intValue];
+ transferUtilityMultiPartUploadTask.uploadID = [task objectForKey:@"multi_part_id"];
+
+ //Add the progress block and callback Function
+ if (multiPartUploadBlocksAssigner) {
+ AWSS3TransferUtilityMultiPartProgressBlock progressBlock = nil;
+ AWSS3TransferUtilityMultiPartUploadCompletionHandlerBlock completionHandler = nil;
+ multiPartUploadBlocksAssigner(transferUtilityMultiPartUploadTask, &progressBlock, &completionHandler);
+ if (progressBlock) {
+ transferUtilityMultiPartUploadTask.expression.progressBlock = progressBlock;
+ }
+ if (completionHandler) {
+ transferUtilityMultiPartUploadTask.expression.completionHandler = completionHandler;
+ }
+ }
+ [multiPartUploads setObject:transferUtilityMultiPartUploadTask forKey:transferUtilityMultiPartUploadTask.uploadID];
+ }
+ else if ([transferType isEqualToString:@"MULTI_PART_UPLOAD_SUB_TASK"]) {
+ AWSS3TransferUtilityUploadSubTask *subTask = [AWSS3TransferUtilityUploadSubTask new];
+ subTask.taskIdentifier = sessionTaskID;
+ subTask.file = [task objectForKey:@"file"];
+ subTask.partNumber = [task objectForKey:@"part_number"];
+ subTask.eTag =[task objectForKey:@"etag"];
+ subTask.uploadID = [task objectForKey:@"multi_part_id"];
+ subTask.status = [task objectForKey:@"status"];
+ subTask.transferID = [task objectForKey:@"transfer_id"];
+ subTask.totalBytesExpectedToSend = [[task objectForKey:@"content_length"] integerValue];
+
+ //Lodge in temporary Dictionary
+ [transferRequests setObject:subTask forKey:@(sessionTaskID)];
+
+ }
+ }
+
+ //Reattach to the NSURLsession objects
+ [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
+
+ if ([dataTasks count] != 0) {
+ AWSDDLogError(@"The underlying NSURLSession contains data tasks. This should not happen.");
+ }
+
+ //Loop through all the upload Tasks.
+ for( NSURLSessionUploadTask *task in uploadTasks ) {
+
+ //Get the Task
+ id obj = [transferRequests objectForKey:@(task.taskIdentifier)];
+
+ if ([obj isKindOfClass:[AWSS3TransferUtilityUploadTask class]])
+ {
+
+ //Found a upload task.
+ AWSS3TransferUtilityUploadTask *uploadTask = obj;
+ uploadTask.sessionTask = task;
+ [self.taskDictionary setObject:uploadTask forKey:@(uploadTask.taskIdentifier)];
+ AWSDDLogDebug(@"Added Upload Transfer task %@ to task dictionary", @(uploadTask.taskIdentifier));
+
+ //Remove this object from the transferRequests list
+ [transferRequests removeObjectForKey:@(task.taskIdentifier)];
+
+ //Check if it is InProgress
+ if ([uploadTask.status isEqualToString:AWSS3TransferUtilityInProgressStatus]) {
+ //Check if the the underlying task is completed. If so, delete the record from the DB, clean up any temp files and call the completion handler.
+ if ([task state] == NSURLSessionTaskStateCompleted ) {
+ [self.taskDictionary removeObjectForKey:@(uploadTask.taskIdentifier)];
+ if ( uploadTask.temporaryFileCreated) {
+ [self removeFile:uploadTask.file];
+ }
+ [self deleteTransferRequestFromDB:uploadTask.transferID databaseQueue:_databaseQueue];
+ if(uploadTask.expression.completionHandler) {
+ uploadTask.expression.completionHandler(uploadTask,nil);
+ }
+ continue;
+ }
+ //If it is in any other status than running, then we need to recover by retrying.
+ if ([task state] != NSURLSessionTaskStateRunning ) {
+ //We think the task in IN_PROGRESS. The underlying task is not running.
+ //Recover the situation by retrying.
+ [self retryUpload:uploadTask];
+ continue;
+ }
+ }
+ }
+ else if ([obj isKindOfClass:[AWSS3TransferUtilityUploadSubTask class]]) {
+ //Found a upload subtask.
+ AWSS3TransferUtilityUploadSubTask *subTaskObj = obj;
+ subTaskObj.sessionTask = task;
+ AWSS3TransferUtilityMultiPartUploadTask *multiPartUploadTask = [multiPartUploads objectForKey:subTaskObj.uploadID];
+
+ [self.taskDictionary setObject:multiPartUploadTask forKey:@(task.taskIdentifier)];
+ AWSDDLogDebug(@"Added MP task[%@] for session ID: %@",multiPartUploadTask.uploadID, @(task.taskIdentifier));
+
+ //Remove this object from the transferRequests list
+ [transferRequests removeObjectForKey:@(task.taskIdentifier)];
+
+ //Check if it is is already completed. If it is, add it to the completed parts list and go to the next iteration of the loop
+ if ( [subTaskObj.status isEqualToString:AWSS3TransferUtilityCompletedStatus]) {
+ [multiPartUploadTask.completedPartsDictionary setObject:subTaskObj forKey:@(task.taskIdentifier)];
+ multiPartUploadTask.progress.completedUnitCount += subTaskObj.totalBytesExpectedToSend;
+ continue;
+ }
+
+ //Check if it is in Waiting status. If it is, add it to the waiting parts list and go to the next iteration of the loop.
+ if ( [subTaskObj.status isEqualToString:AWSS3TransferUtilityWaitingStatus]) {
+ [multiPartUploadTask.waitingPartsDictionary setObject:subTaskObj forKey:@(task.taskIdentifier)];
+ continue;
+ }
+
+ //Add it to the InProgress list
+ [multiPartUploadTask.inProgressPartsDictionary setObject:subTaskObj forKey:@(task.taskIdentifier)];
+
+ //Check if it is in Paused status. If it is, there is nothing more to do.
+ if ( [subTaskObj.status isEqualToString:AWSS3TransferUtilityPausedStatus]) {
+ continue;
+ }
+
+ //The only state that it can be now is in IN_PROGRESS. Check if the underlying NSURLSessionTask is Not running.
+ if ([task state] != NSURLSessionTaskStateRunning ) {
+ AWSDDLogDebug(@"SubTask %lu is in %@ according to DB, but the underlying task is not running. Retrying", (unsigned long)subTaskObj.taskIdentifier,
+ subTaskObj.status);
+ //We think the task in IN_PROGRESS. The underlying task is not running.
+ //Recover the situation by retrying.
+ [self retryUploadSubTask:multiPartUploadTask subTask:subTaskObj];
+ }
+ }
+ else {
+ AWSDDLogWarn(@"Object not found in taskDictionary for %lu. Ignoring.",(unsigned long)task.taskIdentifier);
+ }
+ }
+
+ for( NSURLSessionDownloadTask *task in downloadTasks ) {
+ id obj = [self.taskDictionary objectForKey:@(task.taskIdentifier)];
+ if ([obj isKindOfClass:[AWSS3TransferUtilityDownloadTask class]])
+ {
+ //Found a download task
+ AWSS3TransferUtilityDownloadTask *downloadTask = obj;
+ downloadTask.sessionTask = task;
+ [self.taskDictionary setObject:downloadTask forKey:@(downloadTask.taskIdentifier)];
+
+ //Remove this request from the transferRequests list.
+ [transferRequests removeObjectForKey:@(task.taskIdentifier)];
+
+ //Check if this is in progress
+ if ([downloadTask.status isEqualToString:AWSS3TransferUtilityInProgressStatus]) {
+ //Check if the underlying task's status is not in Progress.
+ if ( [task state] != NSURLSessionTaskStateRunning ) {
+ //We think the task in Progress. The underlying task is not in progress.
+ //Recover the situation by retrying
+ [self retryDownload:downloadTask];
+ continue;
+ }
+ }
+ }
+ else {
+ AWSDDLogError(@"Object not found in taskDictionary for %lu",(unsigned long)task.taskIdentifier);
+ }
+ }
+
+ //Finished iterating through the tasks present in the NSURLSession.
+ //If there are any left in the transferRequests list, it means that we think they are running, but NSURLSession doesn't know about them.
+ //We will ignore these tasks for now.
+
+ //Call the completion handler if one was provided.
+ if (completionHandler) {
+ completionHandler(nil);
+ }
+
+ }];
+}
+
+
#pragma mark - Upload methods
- (AWSTask *)uploadData:(NSData *)data
@@ -279,9 +648,10 @@ - (instancetype)initWithConfiguration:(AWSServiceConfiguration *)serviceConfigur
contentType:(NSString *)contentType
expression:(AWSS3TransferUtilityUploadExpression *)expression
completionHandler:(AWSS3TransferUtilityUploadCompletionHandlerBlock)completionHandler {
+
// Saves the data as a file in the temporary directory.
NSString *fileName = [NSString stringWithFormat:@"%@.tmp", [[NSProcessInfo processInfo] globallyUniqueString]];
- NSString *filePath = [self.temporaryDirectoryPath stringByAppendingPathComponent:fileName];
+ NSString *filePath = [self.cacheDirectoryPath stringByAppendingPathComponent:fileName];
NSURL *fileURL = [NSURL fileURLWithPath:filePath];
NSError *error = nil;
@@ -293,16 +663,17 @@ - (instancetype)initWithConfiguration:(AWSServiceConfiguration *)serviceConfigur
AWSS3TransferUtilityUploadTask *uploadTask = [AWSS3TransferUtilityUploadTask new];
uploadTask.bucket = bucket;
uploadTask.key = key;
- completionHandler(uploadTask, error);
+ completionHandler(uploadTask,error);
}
return [AWSTask taskWithError:error];
}
- return [self uploadFile:fileURL
+ return [self internalUploadFile:fileURL
bucket:bucket
key:key
contentType:contentType
expression:expression
+ temporaryFileCreated:YES
completionHandler:completionHandler];
}
@@ -325,54 +696,68 @@ - (instancetype)initWithConfiguration:(AWSServiceConfiguration *)serviceConfigur
contentType:(NSString *)contentType
expression:(AWSS3TransferUtilityUploadExpression *)expression
completionHandler:(AWSS3TransferUtilityUploadCompletionHandlerBlock)completionHandler {
- if (!bucket || [bucket length] == 0) {
- NSInteger errorCode = (self.transferUtilityConfiguration.isAccelerateModeEnabled) ?
- AWSS3PresignedURLErrorInvalidBucketNameForAccelerateModeEnabled : AWSS3PresignedURLErrorInvalidBucketName;
-
- NSString *errorMessage = @"Invalid bucket specified. Please specify a bucket name or configure the bucket property in `AWSS3TransferUtilityConfiguration`.";
- NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errorMessage
- forKey:NSLocalizedDescriptionKey];
-
- return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3PresignedURLErrorDomain
- code:errorCode
- userInfo:userInfo]];
- }
-
- NSString *filePath = [fileURL path];
+ return [self internalUploadFile:fileURL
+ bucket:bucket
+ key:key
+ contentType:contentType
+ expression:expression
+ temporaryFileCreated:NO
+ completionHandler:completionHandler];
+}
- // Error out if the length of file name < minimum file path length (2 characters) or file does not exist
- if ([filePath length] < 2 ||
- ! [[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
- return [AWSTask taskWithError:[NSError errorWithDomain:AWSS3TransferUtilityErrorDomain
- code:AWSS3TransferUtilityErrorLocalFileNotFound
- userInfo:nil]];
+- (AWSTask *)internalUploadFile:(NSURL *)fileURL
+ bucket:(NSString *)bucket
+ key:(NSString *)key
+ contentType:(NSString *)contentType
+ expression:(AWSS3TransferUtilityUploadExpression *)expression
+ temporaryFileCreated: (BOOL) temporaryFileCreated
+ completionHandler:(AWSS3TransferUtilityUploadCompletionHandlerBlock)completionHandler {
+ //Validate input parameters.
+ AWSTask *error = [self validateParameters:bucket fileURL:fileURL accelerationModeEnabled:self.transferUtilityConfiguration.isAccelerateModeEnabled];
+ if (error ) {
+ if (temporaryFileCreated ) {
+ [self removeFile:[fileURL path]];
+ }
+ return error;
}
+ //Create Expression if required and set it up
if (!expression) {
expression = [AWSS3TransferUtilityUploadExpression new];
}
-
- AWSS3TransferUtilityUploadTask *transferUtilityTask = [AWSS3TransferUtilityUploadTask new];
- transferUtilityTask.bucket = bucket;
- transferUtilityTask.key = key;
-
[expression setValue:contentType forRequestHeader:@"Content-Type"];
-
expression.completionHandler = completionHandler;
- transferUtilityTask.expression = expression;
+ //Create TransferUtility Upload Task
+ AWSS3TransferUtilityUploadTask *transferUtilityUploadTask = [AWSS3TransferUtilityUploadTask new];
+ transferUtilityUploadTask.nsURLSessionID = self.sessionIdentifier;
+ transferUtilityUploadTask.databaseQueue = self.databaseQueue;
+ transferUtilityUploadTask.bucket = bucket;
+ transferUtilityUploadTask.key = key;
+ transferUtilityUploadTask.retryCount = 0;
+ transferUtilityUploadTask.expression = expression;
+ transferUtilityUploadTask.transferID = [[NSUUID UUID] UUIDString];
+ transferUtilityUploadTask.file = [fileURL path];
+ transferUtilityUploadTask.cancelled = NO;
+ transferUtilityUploadTask.temporaryFileCreated = temporaryFileCreated;
+ transferUtilityUploadTask.responseData = @"";
+
+ return [self createUploadTask:transferUtilityUploadTask];
+}
+
+-(AWSTask *) createUploadTask: (AWSS3TransferUtilityUploadTask *) transferUtilityUploadTask {
+ //Create PreSigned URL Request
AWSS3GetPreSignedURLRequest *getPreSignedURLRequest = [AWSS3GetPreSignedURLRequest new];
- getPreSignedURLRequest.bucket = bucket;
- getPreSignedURLRequest.key = key;
+ getPreSignedURLRequest.bucket = transferUtilityUploadTask.bucket;
+ getPreSignedURLRequest.key = transferUtilityUploadTask.key;
getPreSignedURLRequest.HTTPMethod = AWSHTTPMethodPUT;
getPreSignedURLRequest.expires = [NSDate dateWithTimeIntervalSinceNow:AWSS3TransferUtilityTimeoutIntervalForResource];
getPreSignedURLRequest.minimumCredentialsExpirationInterval = AWSS3TransferUtilityTimeoutIntervalForResource;
getPreSignedURLRequest.accelerateModeEnabled = self.transferUtilityConfiguration.isAccelerateModeEnabled;
- [expression assignRequestHeaders:getPreSignedURLRequest];
- [expression assignRequestParameters:getPreSignedURLRequest];
+ [transferUtilityUploadTask.expression assignRequestHeaders:getPreSignedURLRequest];
+ [transferUtilityUploadTask.expression assignRequestParameters:getPreSignedURLRequest];
- __weak AWSS3TransferUtility *weakSelf = self;
return [[self.preSignedURLBuilder getPreSignedURL:getPreSignedURLRequest] continueWithSuccessBlock:^id(AWSTask *task) {
NSURL *presignedURL = task.result;
@@ -382,23 +767,313 @@ - (instancetype)initWithConfiguration:(AWSServiceConfiguration *)serviceConfigur
[request setValue:self.configuration.userAgent forHTTPHeaderField:@"User-Agent"];
- for (NSString *key in expression.requestHeaders) {
- [request setValue:expression.requestHeaders[key] forHTTPHeaderField:key];
+ for (NSString *key in transferUtilityUploadTask.expression.requestHeaders) {
+ [request setValue: transferUtilityUploadTask.expression.requestHeaders[key] forHTTPHeaderField:key];
}
-
AWSDDLogDebug(@"Request headers:\n%@", request.allHTTPHeaderFields);
- NSURLSessionUploadTask *uploadTask = [weakSelf.session uploadTaskWithRequest:request
- fromFile:fileURL];
+ NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request
+ fromFile:[NSURL fileURLWithPath:transferUtilityUploadTask.file]];
+ transferUtilityUploadTask.sessionTask = uploadTask;
+
+ AWSDDLogDebug(@"Setting taskIdentifier to %@", @(transferUtilityUploadTask.sessionTask.taskIdentifier));
+
+ //Add to task Dictionary
+ [self.taskDictionary setObject:transferUtilityUploadTask forKey:@(transferUtilityUploadTask.sessionTask.taskIdentifier) ];
+
+ //Add to Database
+ [self insertUploadTransferRequestInDB:transferUtilityUploadTask databaseQueue:_databaseQueue];
[uploadTask resume];
+ return [AWSTask taskWithResult:transferUtilityUploadTask];
+ }];
+}
+
+
+- (void) retryUpload: (AWSS3TransferUtilityUploadTask *) transferUtilityUploadTask {
+ //Remove from taskDictionary
+ [self.taskDictionary removeObjectForKey:@(transferUtilityUploadTask.taskIdentifier)];
+
+ //Remove from Database
+ [self deleteTransferRequestFromDB:transferUtilityUploadTask.transferID taskIdentifier:transferUtilityUploadTask.taskIdentifier databaseQueue:_databaseQueue ];
+
+ AWSDDLogDebug(@"Removed object from key %@", @(transferUtilityUploadTask.taskIdentifier) );
+ transferUtilityUploadTask.retryCount = transferUtilityUploadTask.retryCount + 1;
+
+ //This will update the AWSS3TransferUtilityUploadTask passed into it with a new URL Session
+ //task and add it into the task Dictionary.
+ [self createUploadTask:transferUtilityUploadTask];
+}
+
+#pragma mark - MultiPart Upload methods
+
+- (AWSTask *)uploadDataUsingMultiPart:(NSData *)data
+ key:(NSString *)key
+ contentType:(NSString *)contentType
+ expression:(AWSS3TransferUtilityMultiPartUploadExpression *)expression
+ completionHandler:(AWSS3TransferUtilityMultiPartUploadCompletionHandlerBlock)completionHandler {
+ return [self uploadDataUsingMultiPart:data
+ bucket:self.transferUtilityConfiguration.bucket
+ key:key
+ contentType:contentType
+ expression:expression
+ completionHandler:completionHandler];
+}
+
+- (AWSTask *)uploadDataUsingMultiPart:(NSData *)data
+ bucket:(NSString *)bucket
+ key:(NSString *)key
+ contentType:(NSString *)contentType
+ expression:(AWSS3TransferUtilityMultiPartUploadExpression *)expression
+ completionHandler:(AWSS3TransferUtilityMultiPartUploadCompletionHandlerBlock)completionHandler {
+
+ // Saves the data as a file in the temporary directory.
+ NSString *fileName = [NSString stringWithFormat:@"%@.tmp", [[NSProcessInfo processInfo] globallyUniqueString]];
+ NSString *filePath = [self.cacheDirectoryPath stringByAppendingPathComponent:fileName];
+ NSURL *fileURL = [NSURL fileURLWithPath:filePath];
+
+ NSError *error = nil;
+ BOOL result = [data writeToURL:fileURL
+ options:NSDataWritingAtomic
+ error:&error];
+ if (!result) {
+ if (completionHandler) {
+ AWSS3TransferUtilityMultiPartUploadTask *uploadTask = [AWSS3TransferUtilityMultiPartUploadTask new];
+ uploadTask.bucket = bucket;
+ uploadTask.key = key;
+ completionHandler(uploadTask, error);
+ }
+ return [AWSTask taskWithError:error];
+ }
+
+ return [self internalUploadFileUsingMultiPart:fileURL
+ bucket:bucket
+ key:key
+ contentType:contentType
+ expression:expression
+ temporaryFileCreated:YES
+ completionHandler:completionHandler];
+}
+
+- (AWSTask *)uploadFileUsingMultiPart:(NSURL *)fileURL
+ key:(NSString *)key
+ contentType:(NSString *)contentType
+ expression:(AWSS3TransferUtilityMultiPartUploadExpression *)expression
+ completionHandler:(AWSS3TransferUtilityMultiPartUploadCompletionHandlerBlock)completionHandler {
+ return [self uploadFileUsingMultiPart:fileURL
+ bucket:self.transferUtilityConfiguration.bucket
+ key:key
+ contentType:contentType
+ expression:expression
+ completionHandler:completionHandler];
+}
+
+- (AWSTask *)uploadFileUsingMultiPart:(NSURL *)fileURL
+ bucket:(NSString *)bucket
+ key:(NSString *)key
+ contentType:(NSString *)contentType
+ expression:(AWSS3TransferUtilityMultiPartUploadExpression *)expression
+ completionHandler:(AWSS3TransferUtilityMultiPartUploadCompletionHandlerBlock) completionHandler
+{
+ return [self internalUploadFileUsingMultiPart:fileURL
+ bucket:bucket
+ key:key
+ contentType:contentType
+ expression:expression
+ temporaryFileCreated:NO
+ completionHandler:completionHandler];
+}
+
+- (AWSTask *)internalUploadFileUsingMultiPart:(NSURL *)fileURL
+ bucket:(NSString *)bucket
+ key:(NSString *)key
+ contentType:(NSString *)contentType
+ expression:(AWSS3TransferUtilityMultiPartUploadExpression *)expression
+ temporaryFileCreated: (BOOL) temporaryFileCreated
+ completionHandler:(AWSS3TransferUtilityMultiPartUploadCompletionHandlerBlock) completionHandler {
+
+ //Validate input parameters.
+ AWSTask *error = [self validateParameters:bucket fileURL:fileURL accelerationModeEnabled:self.transferUtilityConfiguration.isAccelerateModeEnabled];
+ if (error ) {
+ if (temporaryFileCreated) {
+ [self removeFile:[fileURL path]];
+ }
+ return error;
+ }
+
+ //Create Expression if required and set values on the object
+ if (!expression) {
+ expression = [AWSS3TransferUtilityMultiPartUploadExpression new];
+ }
+ [expression setValue:contentType forRequestHeader:@"Content-Type"];
+ expression.completionHandler = completionHandler;
+
+ //Create TransferUtility Multipart Upload Task
+ AWSS3TransferUtilityMultiPartUploadTask *transferUtilityMultiPartUploadTask = [AWSS3TransferUtilityMultiPartUploadTask new];
+ transferUtilityMultiPartUploadTask.nsURLSessionID = self.sessionIdentifier;
+ transferUtilityMultiPartUploadTask.databaseQueue = self.databaseQueue;
+ transferUtilityMultiPartUploadTask.bucket = bucket;
+ transferUtilityMultiPartUploadTask.key = key;
+ transferUtilityMultiPartUploadTask.expression = expression;
+ transferUtilityMultiPartUploadTask.transferID = [[NSUUID UUID] UUIDString];
+ transferUtilityMultiPartUploadTask.file = [fileURL path];
+ transferUtilityMultiPartUploadTask.retryCount = 0;
+ transferUtilityMultiPartUploadTask.temporaryFileCreated = temporaryFileCreated;
+
+ //Get the size of the file and calculate the number of parts.
+ NSError *nsError = nil;
+ NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[fileURL path]
+ error:&nsError];
+ if (!attributes) {
+ if (transferUtilityMultiPartUploadTask.temporaryFileCreated) {
+ [self removeFile:transferUtilityMultiPartUploadTask.file];
+ }
+ return [AWSTask taskWithError:nsError];
+ }
+ unsigned long long fileSize = [attributes fileSize];
+ AWSDDLogDebug(@"File size is %llu", fileSize);
+ NSUInteger partCount = ceil((float)fileSize /(unsigned long) AWSS3TransferUtilityMultiPartSize);
+ AWSDDLogDebug(@"Number of parts is %lu", (unsigned long) partCount);
+ transferUtilityMultiPartUploadTask.progress.totalUnitCount = fileSize;
+ transferUtilityMultiPartUploadTask.progress.completedUnitCount = (long long) 0;
+ transferUtilityMultiPartUploadTask.cancelled = NO;
+ transferUtilityMultiPartUploadTask.contentLength = [ [NSNumber alloc] initWithUnsignedLongLong:fileSize];
+
+ //Create the initial request to start the multipart process.
+ AWSS3CreateMultipartUploadRequest *uploadRequest = [AWSS3CreateMultipartUploadRequest new];
+
+ uploadRequest.bucket = bucket;
+ uploadRequest.key = key;
+
+ //Initiate the multi part
+ return [[self.s3 createMultipartUpload:uploadRequest] continueWithBlock:^id(AWSTask *task) {
+ //Initiation of multi part failed.
+ if (task.error) {
+ if (transferUtilityMultiPartUploadTask.temporaryFileCreated) {
+ [self removeFile:transferUtilityMultiPartUploadTask.file];
+ }
+ return [AWSTask taskWithResult:self];
+ }
+ //Get the uploadID. This will be used with every part that we will upload.
+ AWSS3CreateMultipartUploadOutput *output = task.result;
+ transferUtilityMultiPartUploadTask.uploadID = output.uploadId;
+
+ //Save the Multipart Upload in the DB
+ [self insertMultiPartUploadRequestInDB:transferUtilityMultiPartUploadTask databaseQueue:_databaseQueue];
+
+ AWSDDLogInfo(@"Initiated multipart upload on server: %@", output.uploadId);
+ AWSDDLogInfo(@"Concurrency Limit is %@", self.transferUtilityConfiguration.multiPartConcurrencyLimit);
+ //Loop through the file and upload the parts one by one
+ for (int32_t i = 1; i < partCount + 1; i++) {
+ NSUInteger dataLength = AWSS3TransferUtilityMultiPartSize;
+ if ( i == partCount) {
+ dataLength = fileSize - ( (i-1) * AWSS3TransferUtilityMultiPartSize);
+ }
+
+ //Create a temporary file for this part.
+ NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:[fileURL path]];
+ [fileHandle seekToFileOffset:(i - 1) * AWSS3TransferUtilityMultiPartSize];
+ NSData *partData = [fileHandle readDataOfLength:dataLength];
+ NSString *file = [self.cacheDirectoryPath stringByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
+ NSURL *tempURL = [NSURL fileURLWithPath:file];
+ [partData writeToURL:tempURL atomically:YES];
+ partData = nil;
+ [fileHandle closeFile];
+
+ AWSS3TransferUtilityUploadSubTask *subTask = [AWSS3TransferUtilityUploadSubTask new];
+ subTask.transferID = transferUtilityMultiPartUploadTask.transferID;
+ subTask.partNumber = @(i);
+ subTask.totalBytesExpectedToSend = dataLength;
+ subTask.totalBytesSent = (long long) 0;
+ subTask.file = file;
+ subTask.responseData = @"";
+ //Move the waitingParts to inProgress based on concurrency limit
+ if ( i <= [self.transferUtilityConfiguration.multiPartConcurrencyLimit integerValue] ) {
+ subTask.status = AWSS3TransferUtilityInProgressStatus;
+ [self createUploadSubTask:transferUtilityMultiPartUploadTask subTask:subTask startTransfer:YES];
+ }
+ else {
+ subTask.status = AWSS3TransferUtilityWaitingStatus;
+ [self createUploadSubTask:transferUtilityMultiPartUploadTask subTask:subTask startTransfer:NO];
+ }
+
+ }
+ if (transferUtilityMultiPartUploadTask.temporaryFileCreated) {
+ [self removeFile:transferUtilityMultiPartUploadTask.file];
+ }
+ return [AWSTask taskWithResult:transferUtilityMultiPartUploadTask];
+ }];
+ return [AWSTask taskWithResult:transferUtilityMultiPartUploadTask];
+}
+
+-(void) createUploadSubTask:(AWSS3TransferUtilityMultiPartUploadTask *) transferUtilityMultiPartUploadTask
+ subTask: (AWSS3TransferUtilityUploadSubTask *) subTask
+ startTransfer: (BOOL) startTransfer
+{
+ //Create a presignedURL for this part.
+ AWSS3GetPreSignedURLRequest *request = [AWSS3GetPreSignedURLRequest new];
+ request.bucket = transferUtilityMultiPartUploadTask.bucket;
+ request.key = transferUtilityMultiPartUploadTask.key;
+ request.partNumber = subTask.partNumber;
+ request.uploadID = transferUtilityMultiPartUploadTask.uploadID;
+ request.HTTPMethod = AWSHTTPMethodPUT;
+
+ request.expires = [NSDate dateWithTimeIntervalSinceNow:AWSS3TransferUtilityTimeoutIntervalForResource];
+ request.minimumCredentialsExpirationInterval = AWSS3TransferUtilityTimeoutIntervalForResource;
+ request.accelerateModeEnabled = self.transferUtilityConfiguration.isAccelerateModeEnabled;
+
+ [transferUtilityMultiPartUploadTask.expression assignRequestHeaders:request];
+ [transferUtilityMultiPartUploadTask.expression assignRequestParameters:request];
+
+ [[self.preSignedURLBuilder getPreSignedURL:request] continueWithSuccessBlock:^id(AWSTask *task) {
+ NSURL *presignedURL = task.result;
+ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:presignedURL];
+ request.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
+ request.HTTPMethod = @"PUT";
+ [request setValue:[self.configuration.userAgent stringByAppendingString:@" MultiPart"] forHTTPHeaderField:@"User-Agent"];
+
+ for (NSString *key in transferUtilityMultiPartUploadTask.expression.requestHeaders) {
+ [request setValue:transferUtilityMultiPartUploadTask.expression.requestHeaders[key] forHTTPHeaderField:key];
+ }
+
+ NSURLSessionUploadTask *nsURLUploadTask = [_session uploadTaskWithRequest:request
+ fromFile:[NSURL fileURLWithPath:subTask.file]];
+ //Create subtask to track this upload
+ subTask.sessionTask = nsURLUploadTask;
+ subTask.taskIdentifier = nsURLUploadTask.taskIdentifier;
+
+ if (startTransfer) {
+ [transferUtilityMultiPartUploadTask.inProgressPartsDictionary setObject:subTask forKey:@(subTask.taskIdentifier)];
+ [nsURLUploadTask resume];
+ AWSDDLogDebug(@"Upload started %lu", (unsigned long)nsURLUploadTask.taskIdentifier);
+ }
+ else {
+ //Put the subtask into the waiting parts dictionary, with uploadTask as the key.
+ [transferUtilityMultiPartUploadTask.waitingPartsDictionary setObject:subTask forKey:@(subTask.taskIdentifier)];
+ }
- transferUtilityTask.sessionTask = uploadTask;
- [weakSelf.taskDictionary setObject:transferUtilityTask
- forKey:@(transferUtilityTask.taskIdentifier)];
- return [AWSTask taskWithResult:transferUtilityTask];
+ //Also register transferUtilityMultiPartUploadTask into the taskDictionary for easy lookup in the NSURLCallback
+ [_taskDictionary setObject:transferUtilityMultiPartUploadTask forKey:@(subTask.taskIdentifier)];
+
+ //Save in Database
+ [self insertMultiPartUploadRequestSubTaskInDB:transferUtilityMultiPartUploadTask subTask:subTask databaseQueue:self.databaseQueue];
+ return nil;
}];
}
+-(void) retryUploadSubTask: (AWSS3TransferUtilityMultiPartUploadTask *) transferUtilityMultiPartUploadTask
+ subTask: (AWSS3TransferUtilityUploadSubTask *) subTask {
+
+ //Remove from TaskDictionary and inProgressPartsDictionary
+ [self.taskDictionary removeObjectForKey:@(subTask.taskIdentifier)];
+ [transferUtilityMultiPartUploadTask.inProgressPartsDictionary removeObjectForKey:@(subTask.taskIdentifier)];
+
+ //Remove subTask from Database
+ [self deleteTransferRequestFromDB:subTask.transferID taskIdentifier:subTask.taskIdentifier databaseQueue:_databaseQueue];
+
+ transferUtilityMultiPartUploadTask.retryCount = transferUtilityMultiPartUploadTask.retryCount + 1;
+ [self createUploadSubTask:transferUtilityMultiPartUploadTask subTask:subTask startTransfer:YES];
+}
+
#pragma mark - Download methods
- (AWSTask *)downloadDataForKey:(NSString *)key
@@ -450,6 +1125,7 @@ - (instancetype)initWithConfiguration:(AWSServiceConfiguration *)serviceConfigur
key:(NSString *)key
expression:(AWSS3TransferUtilityDownloadExpression *)expression
completionHandler:(AWSS3TransferUtilityDownloadCompletionHandlerBlock)completionHandler {
+ //Validate that bucket has been specified.
if (!bucket || [bucket length] == 0) {
NSInteger errorCode = (self.transferUtilityConfiguration.isAccelerateModeEnabled) ?
AWSS3PresignedURLErrorInvalidBucketNameForAccelerateModeEnabled : AWSS3PresignedURLErrorInvalidBucketName;
@@ -462,29 +1138,41 @@ - (instancetype)initWithConfiguration:(AWSServiceConfiguration *)serviceConfigur
userInfo:userInfo]];
}
+ //Create Expression if required and set completion Handler.
if (!expression) {
expression = [AWSS3TransferUtilityDownloadExpression new];
}
-
- AWSS3TransferUtilityDownloadTask *transferUtilityTask = [AWSS3TransferUtilityDownloadTask new];
- transferUtilityTask.location = fileURL;
- transferUtilityTask.bucket = bucket;
- transferUtilityTask.key = key;
-
expression.completionHandler = completionHandler;
- transferUtilityTask.expression = expression;
+ //Create Download Task and set it up.
+ AWSS3TransferUtilityDownloadTask *transferUtilityDownloadTask = [AWSS3TransferUtilityDownloadTask new];
+ transferUtilityDownloadTask.nsURLSessionID = self.sessionIdentifier;
+ transferUtilityDownloadTask.databaseQueue = self.databaseQueue;
+ transferUtilityDownloadTask.location = fileURL;
+ transferUtilityDownloadTask.bucket = bucket;
+ transferUtilityDownloadTask.key = key;
+ transferUtilityDownloadTask.expression = expression;
+ transferUtilityDownloadTask.transferID = [[NSUUID UUID] UUIDString];
+ transferUtilityDownloadTask.file = [fileURL absoluteString];
+ transferUtilityDownloadTask.cancelled = NO;
+ transferUtilityDownloadTask.retryCount = 0;
+ transferUtilityDownloadTask.responseData = @"";
+
+ return [self createDownloadTask:transferUtilityDownloadTask];
+}
+
+-(AWSTask *) createDownloadTask: (AWSS3TransferUtilityDownloadTask *) transferUtilityDownloadTask {
AWSS3GetPreSignedURLRequest *getPreSignedURLRequest = [AWSS3GetPreSignedURLRequest new];
- getPreSignedURLRequest.bucket = bucket;
- getPreSignedURLRequest.key = key;
+ getPreSignedURLRequest.bucket = transferUtilityDownloadTask.bucket;
+ getPreSignedURLRequest.key = transferUtilityDownloadTask.key;
getPreSignedURLRequest.HTTPMethod = AWSHTTPMethodGET;
getPreSignedURLRequest.expires = [NSDate dateWithTimeIntervalSinceNow:AWSS3TransferUtilityTimeoutIntervalForResource];
+ getPreSignedURLRequest.minimumCredentialsExpirationInterval = AWSS3TransferUtilityTimeoutIntervalForResource; //Was there in upload but not in download
getPreSignedURLRequest.accelerateModeEnabled = self.transferUtilityConfiguration.isAccelerateModeEnabled;
- [expression assignRequestHeaders:getPreSignedURLRequest];
- [expression assignRequestParameters:getPreSignedURLRequest];
+ [transferUtilityDownloadTask.expression assignRequestHeaders:getPreSignedURLRequest];
+ [transferUtilityDownloadTask.expression assignRequestParameters:getPreSignedURLRequest];
- __weak AWSS3TransferUtility *weakSelf = self;
return [[self.preSignedURLBuilder getPreSignedURL:getPreSignedURLRequest] continueWithSuccessBlock:^id(AWSTask *task) {
NSURL *presignedURL = task.result;
@@ -494,193 +1182,225 @@ - (instancetype)initWithConfiguration:(AWSServiceConfiguration *)serviceConfigur
[request setValue:[AWSServiceConfiguration baseUserAgent] forHTTPHeaderField:@"User-Agent"];
- for (NSString *key in expression.requestHeaders) {
- [request setValue:expression.requestHeaders[key] forHTTPHeaderField:key];
+ for (NSString *key in transferUtilityDownloadTask.expression.requestHeaders) {
+ [request setValue:transferUtilityDownloadTask.expression.requestHeaders[key] forHTTPHeaderField:key];
}
AWSDDLogDebug(@"Request headers:\n%@", request.allHTTPHeaderFields);
- NSURLSessionDownloadTask *downloadTask = [weakSelf.session downloadTaskWithRequest:request];
- [downloadTask resume];
+ NSURLSessionDownloadTask *downloadTask = [self.session downloadTaskWithRequest:request];
+ transferUtilityDownloadTask.sessionTask = downloadTask;
+ transferUtilityDownloadTask.retryCount = 0;
- transferUtilityTask.sessionTask = downloadTask;
- [weakSelf.taskDictionary setObject:transferUtilityTask
- forKey:@(transferUtilityTask.taskIdentifier)];
- return [AWSTask taskWithResult:transferUtilityTask];
+ AWSDDLogDebug(@"Setting taskIdentifier to %@", @(transferUtilityDownloadTask.sessionTask.taskIdentifier));
+
+ //Add to taskDictionary
+ [self.taskDictionary setObject:transferUtilityDownloadTask forKey:@(transferUtilityDownloadTask.sessionTask.taskIdentifier) ];
+
+ //Add to Database
+ [self insertDownloadTransferRequestInDB:transferUtilityDownloadTask databaseQueue:_databaseQueue];
+ [downloadTask resume];
+ return [AWSTask taskWithResult:transferUtilityDownloadTask];
}];
}
-#pragma mark - Utility methods
-
-- (void)enumerateToAssignBlocksForUploadTask:(void (^)(AWSS3TransferUtilityUploadTask *uploadTask,
- AWSS3TransferUtilityProgressBlock *uploadProgressBlockReference,
- AWSS3TransferUtilityUploadCompletionHandlerBlock *completionHandlerReference))uploadBlocksAssigner
+- (void) retryDownload: (AWSS3TransferUtilityDownloadTask *) transferUtilityDownloadTask {
+
+ //Remove from taskDictionary
+ [self.taskDictionary removeObjectForKey:@(transferUtilityDownloadTask.sessionTask.taskIdentifier)];
+
+ //Remove from Database
+ [self deleteTransferRequestFromDB:transferUtilityDownloadTask.transferID taskIdentifier:transferUtilityDownloadTask.sessionTask.taskIdentifier
+ databaseQueue:_databaseQueue];
+
+ AWSDDLogDebug(@"Removed object from key %@", @(transferUtilityDownloadTask.sessionTask.taskIdentifier) );
+ transferUtilityDownloadTask.retryCount = transferUtilityDownloadTask.retryCount + 1;
+
+ //This will update the AWSS3TransferUtilityDownloadTask passed into it with a new URL Session
+ //task and add it into the task Dictionary.
+ [self createDownloadTask:transferUtilityDownloadTask];
+}
+
+#pragma mark - Utility methods
+
+- (void)enumerateToAssignBlocksForUploadTask:(void (^)(AWSS3TransferUtilityUploadTask *uploadTask,
+ AWSS3TransferUtilityProgressBlock *uploadProgressBlockReference,
+ AWSS3TransferUtilityUploadCompletionHandlerBlock *completionHandlerReference))uploadBlocksAssigner
downloadTask:(void (^)(AWSS3TransferUtilityDownloadTask *downloadTask,
AWSS3TransferUtilityProgressBlock *downloadProgressBlockReference,
AWSS3TransferUtilityDownloadCompletionHandlerBlock *completionHandlerReference))downloadBlocksAssigner {
- __weak AWSS3TransferUtility *weakSelf = self;
- [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
- if ([dataTasks count] != 0) {
- AWSDDLogError(@"The underlying NSURLSession contains data tasks. This should not happen.");
+
+ // Iterate through Tasks
+ for (id key in [self.taskDictionary allKeys]) {
+ id value = [self.taskDictionary objectForKey:key];
+ if ( [value isKindOfClass:[AWSS3TransferUtilityUploadTask class]]) {
+ AWSS3TransferUtilityUploadTask *transferUtilityUploadTask = value;
+ if (uploadBlocksAssigner) {
+ AWSS3TransferUtilityProgressBlock progressBlock = nil;
+ AWSS3TransferUtilityUploadCompletionHandlerBlock completionHandler = nil;
+
+ uploadBlocksAssigner(transferUtilityUploadTask, &progressBlock, &completionHandler);
+
+ if (progressBlock) {
+ transferUtilityUploadTask.expression.progressBlock = progressBlock;
+ }
+ if (completionHandler) {
+ transferUtilityUploadTask.expression.completionHandler = completionHandler;
+ }
+ }
}
-
- [uploadTasks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
- NSURLSessionUploadTask *uploadTask = obj;
- AWSS3TransferUtilityUploadTask *transferUtilityUploadTask = [weakSelf getUploadTask:uploadTask];
- if (transferUtilityUploadTask) {
- if (uploadBlocksAssigner) {
- AWSS3TransferUtilityProgressBlock progressBlock = nil;
- AWSS3TransferUtilityUploadCompletionHandlerBlock completionHandler = nil;
-
- uploadBlocksAssigner(transferUtilityUploadTask, &progressBlock, &completionHandler);
-
- if (progressBlock) {
- transferUtilityUploadTask.expression.progressBlock = progressBlock;
- }
- if (completionHandler) {
- transferUtilityUploadTask.expression.completionHandler = completionHandler;
- }
+ else if ([value isKindOfClass:[AWSS3TransferUtilityDownloadTask class]]) {
+ AWSS3TransferUtilityDownloadTask *transferUtilityDownloadTask = value;
+ if (downloadBlocksAssigner) {
+ AWSS3TransferUtilityProgressBlock progressBlock = nil;
+ AWSS3TransferUtilityDownloadCompletionHandlerBlock completionHandler = nil;
+
+ downloadBlocksAssigner(transferUtilityDownloadTask, &progressBlock, &completionHandler);
+
+ if (progressBlock) {
+ transferUtilityDownloadTask.expression.progressBlock = progressBlock;
+ }
+ if (completionHandler) {
+ transferUtilityDownloadTask.expression.completionHandler = completionHandler;
}
}
- }];
-
- [downloadTasks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
- NSURLSessionDownloadTask *downloadTask = obj;
- AWSS3TransferUtilityDownloadTask *transferUtilityDownloadTask = [weakSelf getDownloadTask:downloadTask];
- if (transferUtilityDownloadTask) {
- if (downloadBlocksAssigner) {
- AWSS3TransferUtilityProgressBlock progressBlock = nil;
- AWSS3TransferUtilityDownloadCompletionHandlerBlock completionHandler = nil;
-
- downloadBlocksAssigner(transferUtilityDownloadTask, &progressBlock, &completionHandler);
-
- if (progressBlock) {
- transferUtilityDownloadTask.expression.progressBlock = progressBlock;
- }
- if (completionHandler) {
- transferUtilityDownloadTask.expression.completionHandler = completionHandler;
- }
+ }
+ }
+}
+
+-(void)enumerateToAssignBlocksForUploadTask:(void (^)(AWSS3TransferUtilityUploadTask *uploadTask,
+ AWSS3TransferUtilityProgressBlock *uploadProgressBlockReference,
+ AWSS3TransferUtilityUploadCompletionHandlerBlock *completionHandlerReference))uploadBlocksAssigner
+ multiPartUploadBlocksAssigner: (void (^) (AWSS3TransferUtilityMultiPartUploadTask *multiPartUploadTask,
+ AWSS3TransferUtilityMultiPartProgressBlock *multiPartUploadProgressBlockReference,
+ AWSS3TransferUtilityMultiPartUploadCompletionHandlerBlock *completionHandlerReference)) multiPartUploadBlocksAssigner
+ downloadBlocksAssigner:(void (^)(AWSS3TransferUtilityDownloadTask *downloadTask,
+ AWSS3TransferUtilityProgressBlock *downloadProgressBlockReference,
+ AWSS3TransferUtilityDownloadCompletionHandlerBlock *completionHandlerReference))downloadBlocksAssigner {
+
+ [self enumerateToAssignBlocksForUploadTask:uploadBlocksAssigner downloadTask:downloadBlocksAssigner];
+
+ // Iterate through MultiPartUploadTasks
+ for (id key in [self.taskDictionary allKeys]) {
+ id value = [self.taskDictionary objectForKey:key];
+ if ( [value isKindOfClass:[AWSS3TransferUtilityMultiPartUploadTask class]]) {
+ AWSS3TransferUtilityMultiPartUploadTask *task = value;
+ if (multiPartUploadBlocksAssigner) {
+ AWSS3TransferUtilityMultiPartProgressBlock progressBlock = nil;
+ AWSS3TransferUtilityMultiPartUploadCompletionHandlerBlock completionHandler = nil;
+ multiPartUploadBlocksAssigner(task, &progressBlock, &completionHandler);
+ if (progressBlock) {
+ task.expression.progressBlock = progressBlock;
+ }
+ if (completionHandler) {
+ task.expression.completionHandler = completionHandler;
}
}
- }];
- }];
-
+ }
+ }
}
+
- (AWSTask *)getAllTasks {
AWSTaskCompletionSource *completionSource = [AWSTaskCompletionSource new];
-
- NSMutableArray<__kindof AWSS3TransferUtilityTask *> *allTasks = [NSMutableArray new];
- __weak AWSS3TransferUtility *weakSelf = self;
- [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
- if ([dataTasks count] != 0) {
- AWSDDLogError(@"The underlying NSURLSession contains data tasks. This should not happen.");
+ NSMutableArray *allTasks = [NSMutableArray new];
+ for (id key in [self.taskDictionary allKeys]) {
+ id value = [self.taskDictionary objectForKey:key];
+ //To retain backward compatability, make sure that we filter out Multipart tasks.
+ if ( ! [value isKindOfClass:[AWSS3TransferUtilityMultiPartUploadTask class]]) {
+ [allTasks addObject:value];
}
-
- [uploadTasks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
- NSURLSessionUploadTask *uploadTask = obj;
- AWSS3TransferUtilityUploadTask *transferUtilityUploadTask = [weakSelf getUploadTask:uploadTask];
- if (transferUtilityUploadTask) {
- [allTasks addObject:transferUtilityUploadTask];
- }
- }];
-
- [downloadTasks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
- NSURLSessionDownloadTask *downloadTask = obj;
- AWSS3TransferUtilityDownloadTask *transferUtilityDownloadTask = [weakSelf getDownloadTask:downloadTask];
- if (transferUtilityDownloadTask) {
- [allTasks addObject:transferUtilityDownloadTask];
- }
- }];
-
- [completionSource setResult:allTasks];
- }];
-
+ [allTasks addObject:[self.taskDictionary objectForKey:key]];
+ }
+ [completionSource setResult:allTasks];
return completionSource.task;
}
- (AWSTask *)getUploadTasks {
AWSTaskCompletionSource *completionSource = [AWSTaskCompletionSource new];
+ NSMutableArray *allTasks = [NSMutableArray new];
- NSMutableArray *allUploadTasks = [NSMutableArray new];
- __weak AWSS3TransferUtility *weakSelf = self;
- [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
- if ([dataTasks count] != 0) {
- AWSDDLogError(@"The underlying NSURLSession contains data tasks. This should not happen.");
+ for (id key in [self.taskDictionary allKeys]) {
+ id value = [self.taskDictionary objectForKey:key];
+ if ( [value isKindOfClass:[AWSS3TransferUtilityUploadTask class]]) {
+ [allTasks addObject:value];
}
-
- [uploadTasks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
- NSURLSessionUploadTask *uploadTask = obj;
- AWSS3TransferUtilityUploadTask *transferUtilityUploadTask = [weakSelf getUploadTask:uploadTask];
- if (transferUtilityUploadTask) {
- [allUploadTasks addObject:transferUtilityUploadTask];
- }
- }];
-
- [completionSource setResult:allUploadTasks];
- }];
-
+ }
+ [completionSource setResult:allTasks];
return completionSource.task;
}
- (AWSTask *)getDownloadTasks {
AWSTaskCompletionSource *completionSource = [AWSTaskCompletionSource new];
+ NSMutableArray *allTasks = [NSMutableArray new];
- NSMutableArray *allDownloadTasks = [NSMutableArray new];
- __weak AWSS3TransferUtility *weakSelf = self;
- [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
- if ([dataTasks count] != 0) {
- AWSDDLogError(@"The underlying NSURLSession contains data tasks. This should not happen.");
+ for (id key in [self.taskDictionary allKeys]) {
+ id value = [self.taskDictionary objectForKey:key];
+ if ( [value isKindOfClass:[AWSS3TransferUtilityDownloadTask class]]) {
+ [allTasks addObject:value];
}
-
- [downloadTasks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
- NSURLSessionDownloadTask *downloadTask = obj;
- AWSS3TransferUtilityDownloadTask *transferUtilityDownloadTask = [weakSelf getDownloadTask:downloadTask];
- if (transferUtilityDownloadTask) {
- [allDownloadTasks addObject:transferUtilityDownloadTask];
- }
- }];
-
- [completionSource setResult:allDownloadTasks];
- }];
-
+ }
+ [completionSource setResult:allTasks];
return completionSource.task;
}
-- (void)cleanUpTemporaryDirectory {
- NSError *error = nil;
- NSArray *contentsOfDirectory = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:self.temporaryDirectoryPath
- error:&error];
- if (!contentsOfDirectory) {
- AWSDDLogError(@"Failed to retrieve the contents of the tempoprary directory: %@", error);
- }
+- (AWSTask *)getMultiPartUploadTasks {
+ AWSTaskCompletionSource *completionSource = [AWSTaskCompletionSource new];
+ NSMutableArray *allTasks = [NSMutableArray new];
- // Goes through the temporary directory.
- __weak AWSS3TransferUtility *weakSelf = self;
- [contentsOfDirectory enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
- NSString *fileName = (NSString *)obj;
- NSString *filePath = [weakSelf.temporaryDirectoryPath stringByAppendingPathComponent:fileName];
- NSError *error = nil;
- NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath
- error:&error];
- if (!attributes) {
- AWSDDLogError(@"Failed to load temporary file attributes: %@", error);
- }
- NSDate *fileCreationDate = [attributes objectForKey:NSFileCreationDate];
- // Removes an 'expired' temporary file.
- // Adds 60 seconds buffer to detemine if a file was 'expired'. e.g. Removes files older than 51 minutes old.
- if ([fileCreationDate timeIntervalSince1970] < [[NSDate date] timeIntervalSince1970] - AWSS3TransferUtilityTimeoutIntervalForResource - 60) {
- BOOL result = [[NSFileManager defaultManager] removeItemAtPath:filePath
- error:&error];
- if (!result) {
- AWSDDLogError(@"Failed to remove a temporary file: %@", error);
- }
+ for (id key in [self.taskDictionary allKeys]) {
+ id value = [self.taskDictionary objectForKey:key];
+ if ( [value isKindOfClass:[AWSS3TransferUtilityMultiPartUploadTask class]]) {
+ [allTasks addObject:value];
}
- }];
+ }
+ [completionSource setResult:allTasks];
+ return completionSource.task;
}
+
#pragma mark - Internal helper methods
+- (AWSTask *)callFinishMultiPartForUploadTask:(AWSS3TransferUtilityMultiPartUploadTask *)uploadTask {
+
+ NSMutableArray *completedParts = [NSMutableArray arrayWithCapacity:[uploadTask.completedPartsDictionary count]];
+ NSMutableDictionary *tempDictionary = [NSMutableDictionary new];
+
+ //Create a new Dictionary with the partNumber as the Key
+ for(id key in uploadTask.completedPartsDictionary) {
+ AWSS3TransferUtilityUploadSubTask *subTask = [uploadTask.completedPartsDictionary objectForKey:key];
+ [tempDictionary setObject:subTask forKey:subTask.partNumber];
+ }
+
+ //Compose the request.
+ for(int i = 1; i <= [uploadTask.completedPartsDictionary count]; i++) {
+ AWSS3TransferUtilityUploadSubTask *subTask = [tempDictionary objectForKey: [NSNumber numberWithInt:i]];
+ AWSS3CompletedPart *completedPart = [AWSS3CompletedPart new];
+ completedPart.partNumber = subTask.partNumber;
+ completedPart.ETag = subTask.eTag;
+ [completedParts addObject:completedPart];
+ }
+
+ AWSS3CompletedMultipartUpload *multipartUpload = [AWSS3CompletedMultipartUpload new];
+ multipartUpload.parts = completedParts;
+
+ AWSS3CompleteMultipartUploadRequest *compReq = [AWSS3CompleteMultipartUploadRequest new];
+ compReq.bucket = uploadTask.bucket;
+ compReq.key = uploadTask.key;
+ compReq.uploadId = uploadTask.uploadID;
+ compReq.multipartUpload = multipartUpload;
+
+ return [self.s3 completeMultipartUpload:compReq];
+}
+
+- (AWSTask *) callAbortMultiPartForUploadTask:(AWSS3TransferUtilityMultiPartUploadTask *) uploadTask {
+ AWSS3AbortMultipartUploadRequest *abortReq = [AWSS3AbortMultipartUploadRequest new];
+ abortReq.bucket = uploadTask.bucket;
+ abortReq.uploadId = uploadTask.uploadID;
+ abortReq.key = uploadTask.key;
+ return [self.s3 abortMultipartUpload:abortReq];
+}
+
- (AWSS3TransferUtilityUploadTask *)getUploadTask:(NSURLSessionUploadTask *)uploadTask {
if (![uploadTask isKindOfClass:[NSURLSessionUploadTask class]]) {
AWSDDLogError(@"uploadTask is not an instance of NSURLSessionUploadTask.");
@@ -689,7 +1409,10 @@ - (AWSS3TransferUtilityUploadTask *)getUploadTask:(NSURLSessionUploadTask *)uplo
AWSS3TransferUtilityUploadTask *transferUtilityUploadTask = [self.taskDictionary objectForKey:@(uploadTask.taskIdentifier)];
if (!transferUtilityUploadTask) {
+ AWSDDLogDebug(@"Unable to find TransferUtilityUploadTask for key %@ ", @(uploadTask.taskIdentifier));
+ AWSDDLogDebug(@"Creating new object");
transferUtilityUploadTask = [AWSS3TransferUtilityUploadTask new];
+ transferUtilityUploadTask.responseData = @"";
transferUtilityUploadTask.sessionTask = uploadTask;
[self.taskDictionary setObject:transferUtilityUploadTask
@@ -755,44 +1478,189 @@ - (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error {
- if (!error) {
- NSAssert([task.response isKindOfClass:[NSHTTPURLResponse class]], @"Expected response of type NSHTTPURLResponse");
+
+ NSHTTPURLResponse *HTTPResponse = nil;
+ NSMutableDictionary *userInfo = nil;
+
+ if (!error ) {
if (![task.response isKindOfClass:[NSHTTPURLResponse class]]) {
error = [NSError errorWithDomain:AWSS3TransferUtilityErrorDomain code:AWSS3TransferUtilityErrorUnknown userInfo:nil];
}
-
- NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse *)task.response;
- NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:[HTTPResponse allHeaderFields]];
- if (HTTPResponse.statusCode / 100 == 3
- && HTTPResponse.statusCode != 304) { // 304 Not Modified is a valid response.
- error = [NSError errorWithDomain:AWSS3TransferUtilityErrorDomain
- code:AWSS3TransferUtilityErrorRedirection
- userInfo:userInfo];
+ else {
+ HTTPResponse = (NSHTTPURLResponse *) task.response;
+ userInfo = [NSMutableDictionary dictionaryWithDictionary:[HTTPResponse allHeaderFields]];
}
-
- if (HTTPResponse.statusCode / 100 == 4) {
- error = [NSError errorWithDomain:AWSS3TransferUtilityErrorDomain
- code:AWSS3TransferUtilityErrorClientError
- userInfo:userInfo];
- }
-
- if (HTTPResponse.statusCode / 100 == 5) {
- error = [NSError errorWithDomain:AWSS3TransferUtilityErrorDomain
- code:AWSS3TransferUtilityErrorServerError
- userInfo:userInfo];
+
+ if (!error) {
+ if (HTTPResponse.statusCode / 100 == 3
+ && HTTPResponse.statusCode != 304) { // 304 Not Modified is a valid response.
+ error = [NSError errorWithDomain:AWSS3TransferUtilityErrorDomain
+ code:AWSS3TransferUtilityErrorRedirection
+ userInfo:userInfo];
+ }
+
+ if (HTTPResponse.statusCode / 100 == 4) {
+ error = [NSError errorWithDomain:AWSS3TransferUtilityErrorDomain
+ code:AWSS3TransferUtilityErrorClientError
+ userInfo:userInfo];
+ }
+
+ if (HTTPResponse.statusCode / 100 == 5) {
+ error = [NSError errorWithDomain:AWSS3TransferUtilityErrorDomain
+ code:AWSS3TransferUtilityErrorServerError
+ userInfo:userInfo];
+ }
}
}
- if ([task isKindOfClass:[NSURLSessionUploadTask class]]) {
- AWSS3TransferUtilityUploadTask *uploadTask = [self getUploadTask:(NSURLSessionUploadTask *)task];
- if (uploadTask.expression.completionHandler) {
- uploadTask.expression.completionHandler(uploadTask,
- error);
+
+ if( [task isKindOfClass:[NSURLSessionUploadTask class]]) {
+
+ AWSS3TransferUtilityTask *transferUtilityTask = [self.taskDictionary objectForKey:@(task.taskIdentifier)];
+
+ if ([transferUtilityTask isKindOfClass:[AWSS3TransferUtilityUploadTask class]]) {
+ AWSS3TransferUtilityUploadTask *uploadTask = [self getUploadTask:(NSURLSessionUploadTask *)task];
+
+ //Check if the task was cancelled.
+ if ( uploadTask.cancelled ) {
+ [self cleanupForUploadTask:uploadTask];
+ return;
+ }
+
+ if (error && HTTPResponse) {
+ if ([self isErrorRetriable:HTTPResponse.statusCode responseFromServer:uploadTask.responseData] ) {
+ AWSDDLogDebug(@"Received a 500, 503 or 400 error. Response Data is [%@]", uploadTask.responseData );
+ if (uploadTask.retryCount < self.transferUtilityConfiguration.retryLimit ) {
+ AWSDDLogDebug(@"Retry count is below limit and error is retriable. ");
+ [self retryUpload:uploadTask];
+ return;
+ }
+ }
+ uploadTask.error = error;
+ }
+
+ if(uploadTask.expression.completionHandler) {
+ uploadTask.expression.completionHandler(uploadTask,error);
+ }
+ [self cleanupForUploadTask:uploadTask];
+ return;
+ }
+ else if ([transferUtilityTask isKindOfClass:[AWSS3TransferUtilityMultiPartUploadTask class]]) {
+
+ //Get the multipart upload task
+ AWSS3TransferUtilityMultiPartUploadTask *transferUtilityMultiPartUploadTask = [self.taskDictionary objectForKey:@(task.taskIdentifier)];
+
+ //Check if the task was cancelled.
+ if ( transferUtilityMultiPartUploadTask.cancelled ) {
+ //Abort the request, so the server can clean up any partials.
+ [self callAbortMultiPartForUploadTask:transferUtilityMultiPartUploadTask];
+
+ //Clean up.
+ [self cleanupForMultiPartUploadTask:transferUtilityMultiPartUploadTask];
+ return;
+ }
+
+ //Check if there was an error.
+ if (error) {
+
+ AWSS3TransferUtilityUploadSubTask *subTask = [transferUtilityMultiPartUploadTask.inProgressPartsDictionary objectForKey:@(task.taskIdentifier)];
+
+ //Retrying if a 500, 503 or 400 RequestTimeout error occured.
+ if ([self isErrorRetriable:HTTPResponse.statusCode responseFromServer:subTask.responseData] ) {
+ AWSDDLogDebug(@"Received a 500, 503 or 400 error. Response Data is [%@]", subTask.responseData );
+ if (transferUtilityMultiPartUploadTask.retryCount < self.transferUtilityConfiguration.retryLimit ) {
+ AWSDDLogDebug(@"Retry count is below limit and error is retriable. ");
+ [self retryUploadSubTask:transferUtilityMultiPartUploadTask subTask:subTask];
+ return;
+ }
+ }
+
+ //Error is not retriable.
+ transferUtilityMultiPartUploadTask.error = error;
+
+ //Execute call back if provided.
+ if(transferUtilityMultiPartUploadTask.expression.completionHandler) {
+ transferUtilityMultiPartUploadTask.expression.completionHandler(transferUtilityMultiPartUploadTask,error);
+ }
+
+ //Make sure all the parts are canceled.
+ [transferUtilityMultiPartUploadTask cancel];
+
+ //Abort the request, so the server can clean up any partials.
+ [self callAbortMultiPartForUploadTask:transferUtilityMultiPartUploadTask];
+
+ //clean up.
+ [self cleanupForMultiPartUploadTask:transferUtilityMultiPartUploadTask];
+ return;
+ }
+
+ //Get multipart upload sub task
+ AWSS3TransferUtilityUploadSubTask *subTask = [transferUtilityMultiPartUploadTask.inProgressPartsDictionary objectForKey:@(task.taskIdentifier)];
+ NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse *) task.response;
+ subTask.eTag = (NSString *) HTTPResponse.allHeaderFields[@"ETAG"];
+
+ //Add it to completed parts and remove it from remaining parts.
+ [transferUtilityMultiPartUploadTask.completedPartsDictionary setObject:subTask forKey:@(subTask.taskIdentifier)];
+ [transferUtilityMultiPartUploadTask.inProgressPartsDictionary removeObjectForKey:@(subTask.taskIdentifier)];
+
+ //Delete the temporary upload file for this subTask
+ [self removeFile:subTask.file];
+
+ //Update Database
+ [self updateTransferRequestInDB:subTask.transferID taskIdentifier:subTask.taskIdentifier eTag:subTask.eTag status:AWSS3TransferUtilityCompletedStatus databaseQueue:_databaseQueue];
+
+ //If there are parts waiting to be uploaded, pick one from the list and move it to inProgress
+ if ([transferUtilityMultiPartUploadTask.waitingPartsDictionary count] != 0 ) {
+ //Get a part from the waitingList
+ AWSS3TransferUtilityUploadSubTask *nextSubTask = [[transferUtilityMultiPartUploadTask.waitingPartsDictionary allValues] objectAtIndex:0];
+
+ //Move it inProgress List
+ [transferUtilityMultiPartUploadTask.waitingPartsDictionary removeObjectForKey:@(nextSubTask.taskIdentifier)];
+ [transferUtilityMultiPartUploadTask.inProgressPartsDictionary setObject:nextSubTask forKey:@(nextSubTask.taskIdentifier)];
+ AWSDDLogDebug(@"Upload started %lu", (unsigned long)nextSubTask.taskIdentifier);
+ //Start the transfer
+ [nextSubTask.sessionTask resume];
+ }
+ //If there are no more inProgress parts, then we are done.
+ else if ( [transferUtilityMultiPartUploadTask.inProgressPartsDictionary count] == 0 ) {
+ //Call the Multipart completion step here.
+ [[ self callFinishMultiPartForUploadTask:transferUtilityMultiPartUploadTask] continueWithBlock:^id (AWSTask *task) {
+ if (task.error) {
+ AWSDDLogError(@"Error finishing up MultiPartForUpload Task[%@]", task.error);
+ transferUtilityMultiPartUploadTask.error = error;
+ }
+ //Call the callback function is specified.
+ if(transferUtilityMultiPartUploadTask.expression.completionHandler) {
+ transferUtilityMultiPartUploadTask.expression.completionHandler(transferUtilityMultiPartUploadTask,error);
+ }
+
+ AWSDDLogInfo(@"Completed Multipart Transfer: %@", transferUtilityMultiPartUploadTask.uploadID);
+ [self.taskDictionary removeObjectForKey:@(subTask.taskIdentifier)];
+ [self deleteTransferRequestFromDB:transferUtilityMultiPartUploadTask.transferID databaseQueue:_databaseQueue];
+ return nil;
+ }];
+ }
}
}
+
if ([task isKindOfClass:[NSURLSessionDownloadTask class]]) {
AWSS3TransferUtilityDownloadTask *downloadTask = [self getDownloadTask:(NSURLSessionDownloadTask *)task];
- if (error) {
+
+ //Check if the task was cancelled.
+ if ( downloadTask.cancelled ) {
+ [self.taskDictionary removeObjectForKey:@(downloadTask.sessionTask.taskIdentifier)];
+ [self deleteTransferRequestFromDB:downloadTask.transferID databaseQueue:_databaseQueue];
+ return;
+ }
+
+ if (error && HTTPResponse ) {
+ if ( [self isErrorRetriable:HTTPResponse.statusCode responseFromServer:downloadTask.responseData] ) {
+ if (downloadTask.retryCount < self.transferUtilityConfiguration.retryLimit) {
+ AWSDDLogDebug(@"Retry count is below limit and error is retriable. ");
+ [self retryDownload:downloadTask];
+ return;
+ }
+ }
downloadTask.error = error;
}
if (downloadTask.expression.completionHandler) {
@@ -801,27 +1669,104 @@ - (void)URLSession:(NSURLSession *)session
downloadTask.data,
downloadTask.error);
}
+ [self.taskDictionary removeObjectForKey:@(downloadTask.sessionTask.taskIdentifier)];
+ [self deleteTransferRequestFromDB:downloadTask.transferID databaseQueue:_databaseQueue];
}
+}
+
+- (void) cleanupForMultiPartUploadTask: (AWSS3TransferUtilityMultiPartUploadTask *) task {
+ //Remove data from the Database.
+ [self deleteTransferRequestFromDB:task.transferID databaseQueue:_databaseQueue];
- [self.taskDictionary removeObjectForKey:@(task.taskIdentifier)];
+ //Remove all temporary files and entries from taskDictionary.
+ for ( AWSS3TransferUtilityUploadSubTask *subTask in [task.inProgressPartsDictionary allValues] ) {
+ [self.taskDictionary removeObjectForKey:@(subTask.taskIdentifier)];
+ [self removeFile:subTask.file];
+ }
+ for ( AWSS3TransferUtilityUploadSubTask *subTask in [task.waitingPartsDictionary allValues] ) {
+ [self.taskDictionary removeObjectForKey:@(subTask.taskIdentifier)];
+ [self removeFile:subTask.file];
+ }
}
+- (void) cleanupForUploadTask: (AWSS3TransferUtilityUploadTask *) uploadTask {
+ [self.taskDictionary removeObjectForKey:@(uploadTask.taskIdentifier)];
+ if ( uploadTask.temporaryFileCreated) {
+ [self removeFile:uploadTask.file];
+ }
+ [self deleteTransferRequestFromDB:uploadTask.transferID databaseQueue:_databaseQueue];
+}
+
+- (BOOL) isErrorRetriable:(NSInteger) HTTPStatusCode
+ responseFromServer:(NSString *) responseFromServer {
+
+ // See https://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html for S3 error responses
+
+ //500 and 503 are retriable.
+ if (HTTPStatusCode == 500 || HTTPStatusCode == 503 ) {
+ return YES;
+ }
+ //If not 5XX or 400, error is not retriable.
+ if (HTTPStatusCode != 400 ) {
+ return NO;
+ }
+
+ //If we didn't get any more info from the server, error is retriable
+ if (!responseFromServer ||[responseFromServer isEqualToString:@""] ) {
+ return YES;
+ }
+
+ if ([responseFromServer containsString:@"RequestTimeout"] ||
+ [responseFromServer containsString:@"ExpiredToken" ] ||
+ [responseFromServer containsString:@"TokenRefreshRequired" ]) {
+ return YES;
+ }
+ return NO;
+}
+
+
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didSendBodyData:(int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend {
- if ([task isKindOfClass:[NSURLSessionUploadTask class]]) {
- AWSS3TransferUtilityUploadTask *transferUtilityUploadTask = [self getUploadTask:(NSURLSessionUploadTask *)task];
+
+ //Check if the task is an uploadTask.
+ if (![task isKindOfClass:[NSURLSessionUploadTask class]]) {
+ return;
+ }
+
+ //Handle the update differently based on whether it is a single part or multipart upload.
+ AWSS3TransferUtilityTask *transferUtilityTask = [self.taskDictionary objectForKey:@(task.taskIdentifier)];
+ if( [transferUtilityTask isKindOfClass:[AWSS3TransferUtilityUploadTask class]] ) {
+ AWSS3TransferUtilityUploadTask *transferUtilityUploadTask = [self.taskDictionary objectForKey:@(task.taskIdentifier)];
if (transferUtilityUploadTask.progress.totalUnitCount != totalBytesExpectedToSend) {
transferUtilityUploadTask.progress.totalUnitCount = totalBytesExpectedToSend;
}
- if (transferUtilityUploadTask.progress.completedUnitCount != totalBytesSent) {
+
+ if (transferUtilityUploadTask.progress.completedUnitCount < totalBytesSent) {
transferUtilityUploadTask.progress.completedUnitCount = totalBytesSent;
+
+ if (transferUtilityUploadTask.expression.progressBlock) {
+ transferUtilityUploadTask.expression.progressBlock(transferUtilityUploadTask, transferUtilityUploadTask.progress);
+ }
}
+ }
+ else if( [transferUtilityTask isKindOfClass:[AWSS3TransferUtilityMultiPartUploadTask class]]) {
+ //Get the multipart upload task
+ AWSS3TransferUtilityMultiPartUploadTask *transferUtilityMultiPartUploadTask = [self.taskDictionary objectForKey:@(task.taskIdentifier)];
+ //Get multipart upload sub task
+ AWSS3TransferUtilityUploadSubTask *subTask = [transferUtilityMultiPartUploadTask.inProgressPartsDictionary objectForKey:@(task.taskIdentifier)];
- if (transferUtilityUploadTask.expression.progressBlock) {
- transferUtilityUploadTask.expression.progressBlock(transferUtilityUploadTask, transferUtilityUploadTask.progress);
+ if (subTask.totalBytesSent < totalBytesSent) {
+ //Calculate and update the running total
+ transferUtilityMultiPartUploadTask.progress.completedUnitCount = transferUtilityMultiPartUploadTask.progress.completedUnitCount - subTask.totalBytesSent + totalBytesSent;
+ subTask.totalBytesSent = totalBytesSent;
+
+ //execute the callback to the progressblock if present.
+ if (transferUtilityMultiPartUploadTask.expression.progressBlock) {
+ transferUtilityMultiPartUploadTask.expression.progressBlock(transferUtilityMultiPartUploadTask, transferUtilityMultiPartUploadTask.progress);
+ }
}
}
}
@@ -860,12 +1805,404 @@ - (void)URLSession:(NSURLSession *)session
if (transferUtilityDownloadTask.progress.totalUnitCount != totalBytesExpectedToWrite) {
transferUtilityDownloadTask.progress.totalUnitCount = totalBytesExpectedToWrite;
}
- if (transferUtilityDownloadTask.progress.completedUnitCount != totalBytesWritten) {
+ if (transferUtilityDownloadTask.progress.completedUnitCount < totalBytesWritten) {
transferUtilityDownloadTask.progress.completedUnitCount = totalBytesWritten;
+
+ if (transferUtilityDownloadTask.expression.progressBlock) {
+ transferUtilityDownloadTask.expression.progressBlock(transferUtilityDownloadTask, transferUtilityDownloadTask.progress);
+ }
+ }
+}
+
+#pragma mark - NSURLSessionDataDelegate
+- (void)URLSession:(NSURLSession *)session
+ dataTask:(NSURLSessionDataTask *)dataTask
+ didReceiveData:(NSData *)data {
+
+ if (data && [data length] != 0 ) {
+ //Get the response into a string
+ NSString *response = [NSString stringWithUTF8String:[data bytes]];
+ AWSDDLogDebug(@"Got response from Server: %@", response);
+ if (!response ) {
+ //If response is null, no more work to do. Return
+ return;
+ }
+ //Put it into the responseData property of the transferUtilityUploadTask or the transferUtilityUploadSubTask
+ AWSS3TransferUtilityTask *transferUtilityTask = [self.taskDictionary objectForKey:@(dataTask.taskIdentifier)];
+ if ([transferUtilityTask isKindOfClass:[AWSS3TransferUtilityUploadTask class]]) {
+ AWSS3TransferUtilityUploadTask *uploadTask = [self.taskDictionary objectForKey:@(dataTask.taskIdentifier)];
+ uploadTask.responseData = [uploadTask.responseData stringByAppendingString:response];
+ }
+ else if ([transferUtilityTask isKindOfClass:[AWSS3TransferUtilityMultiPartUploadTask class]]) {
+ //Get the multipart upload task
+ AWSS3TransferUtilityMultiPartUploadTask *transferUtilityMultiPartUploadTask = [self.taskDictionary objectForKey:@(dataTask.taskIdentifier)];
+ AWSS3TransferUtilityUploadSubTask *subTask = [transferUtilityMultiPartUploadTask.inProgressPartsDictionary objectForKey:@(dataTask.taskIdentifier)];
+ subTask.responseData = [subTask.responseData stringByAppendingString:response];
+ }
+ else if ([transferUtilityTask isKindOfClass:[AWSS3TransferUtilityDownloadTask class]] ) {
+ AWSS3TransferUtilityDownloadTask *downloadTask = [self.taskDictionary objectForKey:@(dataTask.taskIdentifier)];
+ downloadTask.responseData = [downloadTask.responseData stringByAppendingString:response];
+ }
+ }
+}
+
+#pragma mark - Database Methods
+//Database Layer Constants
+NSString *const AWSS3TransferUtilityCreateAWSTransfer = @"CREATE TABLE IF NOT EXISTS awstransfer ("
+@"transfer_id TEXT NOT NULL,"
+@"ns_url_session_id TEXT NOT NULL,"
+@"session_task_id INTEGER NOT NULL,"
+@"transfer_type TEXT NOT NULL, "
+@"bucket_name TEXT NOT NULL, "
+@"key TEXT NOT NULL, "
+@"part_number INTEGER, "
+@"multi_part_id TEXT, "
+@"etag TEXT, "
+@"file TEXT NOT NULL,"
+@"temporary_file_created INTEGER, "
+@"content_length INTEGER,"
+@"status TEXT NOT NULL,"
+@"retry_count INTEGER NOT NULL,"
+@"request_headers TEXT,"
+@"request_parameters TEXT)";
+
+
+NSString *const AWSS3TransferUtilityQueryAWSTransfer = @"Select transfer_id, session_task_id, "
+@"transfer_type, bucket_name, key, part_number, multi_part_id, etag, file, temporary_file_created, content_length, "
+@"status, retry_count, request_headers, request_parameters "
+@"From awstransfer "
+@"Where ns_url_session_id=:ns_url_session_id order by transfer_id, part_number";
+
+
+NSString *const AWSS3TransferUtiltyInsertIntoAWSTransfer = @"INSERT INTO awstransfer ("
+@"transfer_id,ns_url_session_id, session_task_id, transfer_type, bucket_name, key, part_number, multi_part_id, etag, file, "
+@"temporary_file_created, content_length, status, retry_count, request_headers, request_parameters"
+@") VALUES ("
+@":transfer_id,:ns_url_session_id, :session_task_id, :transfer_type, :bucket_name, :key, :part_number, :multi_part_id, :etag, :file, :temporary_file_created, :content_length, "
+@":status, :retry_count, :request_headers, :request_parameters"
+@")";
+
+
+NSString *const AWSS3TransferUtilityDeleteATask = @"DELETE FROM awstransfer "
+@"WHERE transfer_id=:transfer_id and "
+@" session_task_id=:session_task_id ";
+
+NSString *const AWSS3TransferUtilityDeleteTransfer = @"DELETE FROM awstransfer "
+@"WHERE transfer_id=:transfer_id";
+
+NSString *const AWSS3TransferUtilityUpdateTransferUtilityStatus = @"UPDATE awstransfer "
+@"SET status=:status "
+@"WHERE transfer_id=:transfer_id and "
+@" session_task_id=:session_task_id ";
+
+NSString *const AWSS3TransferUtilityUpdateTransferUtilityStatusAndETag = @"UPDATE awstransfer "
+@"SET status=:status, etag = :etag "
+@"WHERE transfer_id=:transfer_id and "
+@" session_task_id=:session_task_id ";
+
+NSString *const AWSS3TransferUtilityDatabaseDirectory = @"/com/amazonaws/AWSS3TransferUtility/";
+NSString *const AWSS3TransferUtilityDatabaseName = @"transfer_utility_database";
+NSString *const AWSS3TransferUtilityInProgressStatus = @"IN_PROGRESS";
+NSString *const AWSS3TransferUtilityPausedStatus = @"PAUSED";
+NSString *const AWSS3TransferUtilityCompletedStatus = @"COMPLETED";
+NSString *const AWSS3TransferUtilityWaitingStatus = @"WAITING";
+
+
+- (AWSFMDatabaseQueue *) createDatabase {
+ //Create temporary Dir to hold DB
+ NSString *dbDirPath = [self.cacheDirectoryPath stringByAppendingString:AWSS3TransferUtilityDatabaseDirectory];
+ BOOL fileExistsAtPath = [[NSFileManager defaultManager] fileExistsAtPath:dbDirPath];
+ if (!fileExistsAtPath) {
+ NSError *error = nil;
+ BOOL success = [[NSFileManager defaultManager] createDirectoryAtPath:dbDirPath withIntermediateDirectories:YES attributes:nil error:&error];
+ if (!success) {
+ AWSDDLogError(@"Failed to create a directory for the transfer utility database. [%@]", error);
+ AWSDDLogError(@"Will proceed without using database");
+ return nil;
+ }
+ }
+
+ NSString * databasePath = [dbDirPath stringByAppendingString:AWSS3TransferUtilityDatabaseName];
+ //Open the database if the directory exists
+ AWSDDLogInfo(@"Transfer Utility Database Path: [%@]", databasePath);
+ AWSFMDatabaseQueue *databaseQueue = [AWSFMDatabaseQueue databaseQueueWithPath: databasePath];
+
+ if (!databaseQueue ) {
+ AWSDDLogError(@"Unable to create Database Queue for [%@]", databasePath);
+ return nil;
+ }
+
+ [databaseQueue inDatabase:^(AWSFMDatabase *db) {
+ if (! [db executeUpdate: AWSS3TransferUtilityCreateAWSTransfer]) {
+ AWSDDLogError(@"Failed to create awstransfer Database table. [%@]", db.lastError);
+ }
+ }];
+ return databaseQueue;
+}
+
+
+- (void) insertUploadTransferRequestInDB:(AWSS3TransferUtilityUploadTask *) task
+ databaseQueue: (AWSFMDatabaseQueue *) databaseQueue {
+
+ [self insertTransferRequestInDB:task.transferID
+ nsURLSessionID:task.nsURLSessionID
+ taskIdentifier:@(task.sessionTask.taskIdentifier)
+ transferType:@"UPLOAD"
+ bucket:task.bucket
+ key:task.key
+ partNumber:@0
+ multiPartID:@""
+ eTag:@""
+ file:task.file
+ temporaryFileCreated:task.temporaryFileCreated
+ contentLength:@0
+ status:AWSS3TransferUtilityInProgressStatus
+ retryCount:@(task.retryCount)
+ requestHeadersJSON:[self getJSONRepresentation:task.expression.requestHeaders]
+ requestParametersJSON:[self getJSONRepresentation:task.expression.requestParameters]
+ databaseQueue:databaseQueue];
+}
+
+- (NSString *) getJSONRepresentation: (NSDictionary *) dict {
+ NSError *error = nil;
+ NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:kNilOptions error:&error];
+ if (error) {
+ AWSDDLogError(@"Error converting dictionary to JSON:%@", error);
+ return @"{}";
+ }
+ return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
+}
+
+- (NSDictionary*) getDictionaryFromJson: (NSString *)json {
+ NSError *error = nil;
+ NSData *data = [json dataUsingEncoding:NSUTF8StringEncoding];
+ NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
+ if (error) {
+ AWSDDLogError(@"Error converting JSON to Dictionary:%@", error);
+ return [NSDictionary new];
+ }
+ return dict;
+}
+
+
+- (void) insertDownloadTransferRequestInDB:(AWSS3TransferUtilityDownloadTask *) task
+ databaseQueue: (AWSFMDatabaseQueue *) databaseQueue {
+ NSString *file = task.file;
+ if(!file) {
+ file = @"";
+ }
+ [self insertTransferRequestInDB:task.transferID
+ nsURLSessionID:task.nsURLSessionID
+ taskIdentifier:@(task.sessionTask.taskIdentifier)
+ transferType:@"DOWNLOAD"
+ bucket:task.bucket
+ key:task.key
+ partNumber:@0
+ multiPartID:@""
+ eTag:@""
+ file:file
+ temporaryFileCreated: NO
+ contentLength:@0
+ status:AWSS3TransferUtilityInProgressStatus
+ retryCount:@(task.retryCount)
+ requestHeadersJSON:[self getJSONRepresentation:task.expression.requestHeaders]
+ requestParametersJSON:[self getJSONRepresentation:task.expression.requestParameters]
+ databaseQueue:databaseQueue];
+}
+
+- (void) insertMultiPartUploadRequestInDB:(AWSS3TransferUtilityMultiPartUploadTask *) task
+ databaseQueue: (AWSFMDatabaseQueue *) databaseQueue {
+ [self insertTransferRequestInDB:task.transferID
+ nsURLSessionID:task.nsURLSessionID
+ taskIdentifier:@0
+ transferType:@"MULTI_PART_UPLOAD"
+ bucket:task.bucket
+ key:task.key
+ partNumber:@0
+ multiPartID:task.uploadID
+ eTag:@""
+ file:task.file
+ temporaryFileCreated: task.temporaryFileCreated
+ contentLength:task.contentLength
+ status:AWSS3TransferUtilityInProgressStatus
+ retryCount:@(task.retryCount)
+ requestHeadersJSON:[self getJSONRepresentation:task.expression.requestHeaders]
+ requestParametersJSON:[self getJSONRepresentation:task.expression.requestParameters]
+ databaseQueue:databaseQueue];
+}
+
+- (void) insertMultiPartUploadRequestSubTaskInDB:(AWSS3TransferUtilityMultiPartUploadTask *) task
+ subTask:(AWSS3TransferUtilityUploadSubTask *) subTask
+ databaseQueue: (AWSFMDatabaseQueue *) databaseQueue {
+ [self insertTransferRequestInDB:task.transferID
+ nsURLSessionID:task.nsURLSessionID
+ taskIdentifier:@(subTask.taskIdentifier)
+ transferType:@"MULTI_PART_UPLOAD_SUB_TASK"
+ bucket:task.bucket
+ key:task.key
+ partNumber:subTask.partNumber
+ multiPartID:task.uploadID
+ eTag:@""
+ file:subTask.file
+ temporaryFileCreated: YES
+ contentLength:@(subTask.totalBytesExpectedToSend)
+ status:subTask.status
+ retryCount:@(0)
+ requestHeadersJSON:[self getJSONRepresentation:task.expression.requestHeaders]
+ requestParametersJSON:[self getJSONRepresentation:task.expression.requestParameters]
+ databaseQueue:databaseQueue];
+}
+
+
+- (void) insertTransferRequestInDB: (NSString *) transferID
+ nsURLSessionID: (NSString *) nsURLSessionID
+ taskIdentifier: (NSNumber *) taskIdentifier
+ transferType: (NSString *) transferType
+ bucket: (NSString *) bucket
+ key: (NSString *) key
+ partNumber: (NSNumber *) partNumber
+ multiPartID: (NSString *) multiPartID
+ eTag: (NSString *) eTag
+ file: (NSString *) file
+ temporaryFileCreated: (BOOL) temporaryFileCreated
+ contentLength: (NSNumber *) contentLength
+ status: (NSString *) status
+ retryCount: (NSNumber *) retryCount
+ requestHeadersJSON: (NSString *) requestHeadersJSON
+ requestParametersJSON: (NSString *) requestParametersJSON
+ databaseQueue: (AWSFMDatabaseQueue *) databaseQueue {
+ NSNumber *tempFileCreated = [NSNumber numberWithInt:0];
+ if (temporaryFileCreated ) {
+ tempFileCreated = [NSNumber numberWithInt:1];
}
- if (transferUtilityDownloadTask.expression.progressBlock) {
- transferUtilityDownloadTask.expression.progressBlock(transferUtilityDownloadTask, transferUtilityDownloadTask.progress);
+ [databaseQueue inDatabase:^(AWSFMDatabase *db) {
+ BOOL result = [db executeUpdate: AWSS3TransferUtiltyInsertIntoAWSTransfer
+ withParameterDictionary:@{
+ @"transfer_id": transferID,
+ @"ns_url_session_id": nsURLSessionID,
+ @"session_task_id":taskIdentifier,
+ @"transfer_type": transferType,
+ @"bucket_name": bucket,
+ @"key": key,
+ @"part_number": partNumber,
+ @"multi_part_id": multiPartID,
+ @"etag": eTag,
+ @"file": file,
+ @"temporary_file_created": tempFileCreated,
+ @"content_length": contentLength,
+ @"status": status,
+ @"request_headers": requestHeadersJSON,
+ @"request_parameters": requestParametersJSON,
+ @"retry_count": retryCount
+ }];
+
+ if (!result ) {
+ AWSDDLogError(@"Failed to save Transfer [%@] in awstransfer database table. [%@]", transferID, db.lastError);
+ }
+ }];
+}
+
+
+- (void) deleteTransferRequestFromDB:(NSString *) transferID
+ databaseQueue: (AWSFMDatabaseQueue *) databaseQueue {
+ [databaseQueue inDatabase:^(AWSFMDatabase *db) {
+ BOOL result = [db executeUpdate: AWSS3TransferUtilityDeleteTransfer
+ withParameterDictionary:@{
+ @"transfer_id": transferID
+ }];
+
+ if (!result ) {
+ AWSDDLogError(@"Failed to delete transfer_request [%@] in Database. [%@]", transferID,
+ db.lastError);
+ return;
+ }
+
+ }];
+}
+
+- (void) deleteTransferRequestFromDB:(NSString *) transferID
+ taskIdentifier: (NSUInteger) taskIdentifier
+ databaseQueue: (AWSFMDatabaseQueue *) databaseQueue {
+ [databaseQueue inDatabase:^(AWSFMDatabase *db) {
+ BOOL result = [db executeUpdate:AWSS3TransferUtilityDeleteATask
+ withParameterDictionary:@{
+ @"transfer_id": transferID,
+ @"session_task_id": @(taskIdentifier)
+ }];
+ if (!result ) {
+ AWSDDLogError(@"Failed to delete transfer_request [%@] in Database. [%@]", transferID,
+ db.lastError);
+ }
+ }];
+}
+
+- (void) updateTransferRequestInDB: (NSString *) transferID
+ taskIdentifier: (NSUInteger) taskIdentifier
+ eTag: (NSString *) eTag
+ status: (NSString *) status
+ databaseQueue: (AWSFMDatabaseQueue *) databaseQueue {
+ [databaseQueue inDatabase:^(AWSFMDatabase *db) {
+ BOOL result = [db executeUpdate: AWSS3TransferUtilityUpdateTransferUtilityStatusAndETag
+ withParameterDictionary:@{
+ @"transfer_id": transferID,
+ @"session_task_id": @(taskIdentifier),
+ @"etag": eTag,
+ @"status": status
+ }];
+
+ if (!result ) {
+ AWSDDLogError(@"Failed to update transfer_request [%@] in Database. [%@]", transferID,
+ db.lastError);
+ }
+ }];
+}
+
+- (NSMutableArray *) getTransferTaskDataFromDB:(NSString *)nsURLSessionID {
+
+ NSMutableArray *tasks = [NSMutableArray new];
+ //Read from DB
+ [_databaseQueue inDatabase:^(AWSFMDatabase *db) {
+ //Get all AWSTransferRecords
+ AWSFMResultSet *rs = [db executeQuery:AWSS3TransferUtilityQueryAWSTransfer
+ withParameterDictionary:@{
+ @"ns_url_session_id": nsURLSessionID
+ }];
+ while( [rs next]) {
+ NSMutableDictionary *transfer = [NSMutableDictionary new];
+ [transfer setObject:[rs stringForColumn:@"transfer_id"] forKey:@"transfer_id"];
+ [transfer setObject:@([rs intForColumn:@"session_task_id"]) forKey:@"session_task_id"];
+ [transfer setObject:[rs stringForColumn:@"transfer_type"] forKey:@"transfer_type"];
+ [transfer setObject:[rs stringForColumn:@"bucket_name"] forKey:@"bucket_name"];
+ [transfer setObject:[rs stringForColumn:@"key"] forKey:@"key"];
+ [transfer setObject:@([rs intForColumn:@"part_number"]) forKey:@"part_number"];
+ [transfer setObject:[rs stringForColumn:@"multi_part_id"] forKey:@"multi_part_id"];
+ [transfer setObject:[rs stringForColumn:@"etag"] forKey:@"etag"];
+ [transfer setObject:[rs stringForColumn:@"file"] forKey:@"file"];
+ [transfer setObject:@([rs intForColumn:@"temporary_file_created"]) forKey:@"temporary_file_created"];
+ [transfer setObject:@([rs intForColumn:@"content_length"]) forKey:@"content_length"];
+ [transfer setObject:[rs stringForColumn:@"status"] forKey:@"status"];
+ [transfer setObject:@([rs intForColumn:@"retry_count"]) forKey:@"retry_count"];
+ [transfer setObject:[rs stringForColumn:@"request_headers"] forKey:@"request_headers"];
+ [transfer setObject:[rs stringForColumn:@"request_parameters"] forKey:@"request_parameters"];
+ [tasks addObject:transfer];
+ }
+ rs = nil;
+ }];
+ return tasks;
+}
+
+
+- (void) removeFile: (NSString *) absolutePath
+{
+ if (!absolutePath || ![[NSFileManager defaultManager ] fileExistsAtPath:absolutePath] ) {
+ return;
+ }
+
+ NSError *error = nil;
+ [[NSFileManager defaultManager] removeItemAtPath:absolutePath error:&error];
+ if (error) {
+ AWSDDLogError(@"Error deleting file[%@]: [%@]", absolutePath, error);
}
}
@@ -877,15 +2214,20 @@ @implementation AWSS3TransferUtilityConfiguration
- (instancetype)init {
if (self = [super init]) {
+ //set defaults.
_accelerateModeEnabled = NO;
+ _retryLimit = 0;
+ _multiPartConcurrencyLimit = @(AWSS3TransferUtilityMultiPartDefaultConcurrencyLimit);
}
-
return self;
}
- (id)copyWithZone:(NSZone *)zone {
AWSS3TransferUtilityConfiguration *configuration = [[[self class] allocWithZone:zone] init];
configuration.accelerateModeEnabled = self.isAccelerateModeEnabled;
+ configuration.bucket = self.bucket;
+ configuration.retryLimit = self.retryLimit;
+ configuration.multiPartConcurrencyLimit = self.multiPartConcurrencyLimit;
return configuration;
}
@@ -909,15 +2251,17 @@ - (NSUInteger)taskIdentifier {
}
- (void)cancel {
- [self.sessionTask cancel];
}
- (void)resume {
[self.sessionTask resume];
+ [self updateStatus:self.transferID taskIdentifier:self.taskIdentifier status:AWSS3TransferUtilityInProgressStatus databaseQueue:self.databaseQueue];
+
}
- (void)suspend {
[self.sessionTask suspend];
+ [self updateStatus:self.transferID taskIdentifier:self.sessionTask.taskIdentifier status:AWSS3TransferUtilityPausedStatus databaseQueue:self.databaseQueue];
}
- (NSURLRequest *)request {
@@ -928,10 +2272,26 @@ - (NSHTTPURLResponse *)response {
if ([self.sessionTask.response isKindOfClass:[NSHTTPURLResponse class]]) {
return (NSHTTPURLResponse *)self.sessionTask.response;
}
-
return nil;
}
+- (void) updateStatus: (NSString *) transferID
+ taskIdentifier: (NSUInteger) taskIdentifier
+ status: (NSString *) status
+ databaseQueue: (AWSFMDatabaseQueue *) databaseQueue {
+ [databaseQueue inDatabase:^(AWSFMDatabase *db) {
+ BOOL result = [db executeUpdate: AWSS3TransferUtilityUpdateTransferUtilityStatus
+ withParameterDictionary:@{
+ @"transfer_id": transferID,
+ @"session_task_id": @(taskIdentifier),
+ @"status": status
+ }];
+ if (!result ) {
+ AWSDDLogError(@"Failed to update transfer_request [%@] in Database. [%@]", transferID,
+ db.lastError);
+ }
+ }];
+}
@end
@implementation AWSS3TransferUtilityUploadTask
@@ -943,6 +2303,81 @@ - (AWSS3TransferUtilityUploadExpression *)expression {
return _expression;
}
+-(void) cancel {
+ self.cancelled = YES;
+ [self.sessionTask cancel];
+}
+
+@end
+
+@implementation AWSS3TransferUtilityMultiPartUploadTask
+
+- (void) updateStatus: (NSString *) transferID
+ taskIdentifier: (NSUInteger) taskIdentifier
+ status: (NSString *) status
+ databaseQueue: (AWSFMDatabaseQueue *) databaseQueue {
+ [databaseQueue inDatabase:^(AWSFMDatabase *db) {
+ BOOL result = [db executeUpdate: AWSS3TransferUtilityUpdateTransferUtilityStatus
+ withParameterDictionary:@{
+ @"transfer_id": transferID,
+ @"session_task_id": @(taskIdentifier),
+ @"status": status
+ }];
+
+ if (!result ) {
+ AWSDDLogError(@"Failed to update transfer_request [%@] in Database. [%@]", transferID,
+ db.lastError);
+ }
+ }];
+}
+
+- (instancetype)init {
+ if (self = [super init]) {
+ _progress = [NSProgress new];
+ _waitingPartsDictionary = [NSMutableDictionary new];
+ _inProgressPartsDictionary = [NSMutableDictionary new];
+ _completedPartsDictionary = [NSMutableDictionary new];
+ }
+ return self;
+}
+
+- (AWSS3TransferUtilityMultiPartUploadExpression *)expression {
+ if (!_expression) {
+ _expression = [AWSS3TransferUtilityMultiPartUploadExpression new];
+ }
+ return _expression;
+}
+
+- (void)cancel {
+ self.cancelled = YES;
+ for (NSNumber *key in [self.inProgressPartsDictionary allKeys]) {
+ AWSS3TransferUtilityUploadSubTask *subTask = [self.inProgressPartsDictionary objectForKey:key];
+ [subTask.sessionTask cancel];
+ }
+ for (NSNumber *key in [self.waitingPartsDictionary allKeys]) {
+ AWSS3TransferUtilityUploadSubTask *subTask = [self.waitingPartsDictionary objectForKey:key];
+ [subTask.sessionTask cancel];
+ }
+}
+
+- (void)resume {
+ for (NSNumber *key in [self.inProgressPartsDictionary allKeys]) {
+ AWSS3TransferUtilityUploadSubTask *subTask = [self.inProgressPartsDictionary objectForKey:key];
+ subTask.status = AWSS3TransferUtilityInProgressStatus;
+ [self updateStatus:subTask.transferID taskIdentifier:subTask.taskIdentifier status:AWSS3TransferUtilityInProgressStatus databaseQueue:self.databaseQueue];
+ [subTask.sessionTask resume];
+ }
+}
+
+- (void)suspend {
+ for (NSNumber *key in [self.inProgressPartsDictionary allKeys]) {
+ AWSS3TransferUtilityUploadSubTask *subTask = [self.inProgressPartsDictionary objectForKey:key];
+ [subTask.sessionTask suspend];
+ subTask.status = AWSS3TransferUtilityPausedStatus;
+ [self updateStatus:subTask.transferID taskIdentifier:subTask.taskIdentifier status:AWSS3TransferUtilityPausedStatus databaseQueue:self.databaseQueue];
+ }
+}
+
@end
@implementation AWSS3TransferUtilityDownloadTask
@@ -954,6 +2389,11 @@ - (AWSS3TransferUtilityDownloadExpression *)expression {
return _expression;
}
+-(void) cancel {
+ self.cancelled = YES;
+ [self.sessionTask cancel];
+}
+
@end
#pragma mark - AWSS3TransferUtilityExpressions
@@ -1002,7 +2442,6 @@ - (void)assignRequestParameters:(AWSS3GetPreSignedURLRequest *)getPreSignedURLRe
@end
@implementation AWSS3TransferUtilityUploadExpression
-
- (NSString *)contentMD5 {
return [self.internalRequestHeaders valueForKey:@"Content-MD5"];
}
@@ -1010,9 +2449,54 @@ - (NSString *)contentMD5 {
- (void)setContentMD5:(NSString *)contentMD5 {
[self setValue:contentMD5 forRequestHeader:@"Content-MD5"];
}
+@end
+
+@implementation AWSS3TransferUtilityMultiPartUploadExpression
+
+- (instancetype)init {
+ if (self = [super init]) {
+ _internalRequestHeaders = [NSMutableDictionary new];
+ _internalRequestParameters = [NSMutableDictionary new];
+ }
+ return self;
+}
+
+- (NSDictionary *)requestHeaders {
+ return [NSDictionary dictionaryWithDictionary:self.internalRequestHeaders];
+}
+
+- (NSDictionary *)requestParameters {
+ return [NSDictionary dictionaryWithDictionary:self.internalRequestParameters];
+}
+
+- (void)setValue:(NSString *)value forRequestHeader:(NSString *)requestHeader {
+ [self.internalRequestHeaders setValue:value forKey:requestHeader];
+}
+
+- (void)setValue:(NSString *)value forRequestParameter:(NSString *)requestParameter {
+ [self.internalRequestParameters setValue:value forKey:requestParameter];
+}
+
+- (void)assignRequestHeaders:(AWSS3GetPreSignedURLRequest *)getPreSignedURLRequest {
+ for (NSString *key in self.internalRequestHeaders) {
+ [getPreSignedURLRequest setValue:self.internalRequestHeaders[key]
+ forRequestHeader:key];
+ }
+}
+
+- (void)assignRequestParameters:(AWSS3GetPreSignedURLRequest *)getPreSignedURLRequest {
+ for (NSString *key in self.internalRequestParameters) {
+ [getPreSignedURLRequest setValue:self.internalRequestParameters[key]
+ forRequestParameter:key];
+ }
+}
@end
@implementation AWSS3TransferUtilityDownloadExpression
+@end
+@implementation AWSS3TransferUtilityUploadSubTask
@end
+
+
diff --git a/AWSS3/Info.plist b/AWSS3/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSS3/Info.plist
+++ b/AWSS3/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSS3Tests/AWSS3TransferUtilityTests.swift b/AWSS3Tests/AWSS3TransferUtilityTests.swift
index 5ab76903ba2..e2ec06b17f6 100644
--- a/AWSS3Tests/AWSS3TransferUtilityTests.swift
+++ b/AWSS3Tests/AWSS3TransferUtilityTests.swift
@@ -23,6 +23,10 @@ class AWSS3TransferUtilityTests: XCTestCase {
override class func setUp() {
super.setUp()
+ //Setup Log level
+ AWSDDLog.sharedInstance.logLevel = .verbose
+ AWSDDLog.add(AWSDDTTYLogger.sharedInstance)
+
AWSTestUtility.setupCognitoCredentialsProvider()
let serviceConfiguration = AWSServiceConfiguration(
@@ -38,7 +42,19 @@ class AWSS3TransferUtilityTests: XCTestCase {
transferUtilityConfiguration: transferUtilityConfiguration,
forKey: "transfer-acceleration"
)
-
+
+ let serviceConfiguration2 = AWSServiceManager.default().defaultServiceConfiguration
+ let transferUtilityConfigurationWithRetry = AWSS3TransferUtilityConfiguration()
+ transferUtilityConfigurationWithRetry.isAccelerateModeEnabled = false
+ transferUtilityConfigurationWithRetry.retryLimit = 3
+ transferUtilityConfigurationWithRetry.multiPartConcurrencyLimit = 2
+
+ AWSS3TransferUtility.register(
+ with: serviceConfiguration2!,
+ transferUtilityConfiguration: transferUtilityConfigurationWithRetry,
+ forKey: "with-retry"
+ )
+
var dataString = "1234567890"
for _ in 1...5 {
dataString += dataString
@@ -61,12 +77,15 @@ class AWSS3TransferUtilityTests: XCTestCase {
let password = "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="
let passwordMD5 = "dnF5x6K/8ZZRzpfSlMMM+w=="
- let transferUtility = AWSS3TransferUtility.default()
+ let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "with-retry")
let uploadExpression = AWSS3TransferUtilityUploadExpression()
uploadExpression.setValue("AES256", forRequestHeader: "x-amz-server-side-encryption-customer-algorithm")
uploadExpression.setValue(password, forRequestHeader: "x-amz-server-side-encryption-customer-key")
uploadExpression.setValue(passwordMD5, forRequestHeader: "x-amz-server-side-encryption-customer-key-MD5")
-
+ uploadExpression.progressBlock = {(task, progress) in
+ print("Upload progress: ", progress.fractionCompleted)
+ }
+
let uploadCompletionHandler = { (task: AWSS3TransferUtilityUploadTask, error: Error?) -> Void in
XCTAssertNil(error)
if let HTTPResponse = task.response {
@@ -76,7 +95,9 @@ class AWSS3TransferUtilityTests: XCTestCase {
downloadExpression.setValue("AES256", forRequestHeader: "x-amz-server-side-encryption-customer-algorithm")
downloadExpression.setValue(password, forRequestHeader: "x-amz-server-side-encryption-customer-key")
downloadExpression.setValue(passwordMD5, forRequestHeader: "x-amz-server-side-encryption-customer-key-MD5")
-
+ downloadExpression.progressBlock = {(task, progress) in
+ print("Download progress: ", progress.fractionCompleted)
+ }
let downloadCompletionHandler = { (task: AWSS3TransferUtilityDownloadTask, URL: Foundation.URL?, data: Data?, error: Error?) in
if let HTTPResponse = task.response {
XCTAssertEqual(HTTPResponse.statusCode, 200)
@@ -120,13 +141,354 @@ class AWSS3TransferUtilityTests: XCTestCase {
}
}
+ func testUploadAndDownloadLargeData() {
+ let expectation = self.expectation(description: "The completion handler called.")
+ var dataString = "Test1234"
+ for _ in 1...21 {
+ dataString = dataString + dataString;
+ }
+
+ // the test key is 1234567890123456
+ let password = "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="
+ let passwordMD5 = "dnF5x6K/8ZZRzpfSlMMM+w=="
+
+ let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "with-retry")
+ let uploadExpression = AWSS3TransferUtilityUploadExpression()
+ uploadExpression.setValue("AES256", forRequestHeader: "x-amz-server-side-encryption-customer-algorithm")
+ uploadExpression.setValue(password, forRequestHeader: "x-amz-server-side-encryption-customer-key")
+ uploadExpression.setValue(passwordMD5, forRequestHeader: "x-amz-server-side-encryption-customer-key-MD5")
+ uploadExpression.progressBlock = {(task, progress) in
+ print("Upload progress: ", progress.fractionCompleted)
+ }
+
+ let uploadCompletionHandler = { (task: AWSS3TransferUtilityUploadTask, error: Error?) -> Void in
+ XCTAssertNil(error)
+ if let HTTPResponse = task.response {
+ XCTAssertEqual(HTTPResponse.statusCode, 200)
+
+ let downloadExpression = AWSS3TransferUtilityDownloadExpression()
+ downloadExpression.setValue("AES256", forRequestHeader: "x-amz-server-side-encryption-customer-algorithm")
+ downloadExpression.setValue(password, forRequestHeader: "x-amz-server-side-encryption-customer-key")
+ downloadExpression.setValue(passwordMD5, forRequestHeader: "x-amz-server-side-encryption-customer-key-MD5")
+ downloadExpression.progressBlock = {(task, progress) in
+ print("Download progress: ", progress.fractionCompleted)
+ }
+ let downloadCompletionHandler = { (task: AWSS3TransferUtilityDownloadTask, URL: Foundation.URL?, data: Data?, error: Error?) in
+ if let HTTPResponse = task.response {
+ XCTAssertEqual(HTTPResponse.statusCode, 200)
+ XCTAssertEqual(data, dataString.data(using: String.Encoding.utf8)!)
+ XCTAssertEqual(HTTPResponse.allHeaderFields["Content-Type"] as? String, "text/plain")
+ } else {
+ XCTFail()
+ }
+
+ expectation.fulfill()
+ }
+
+ transferUtility.downloadData(
+ fromBucket: "ios-v2-s3.periods",
+ key: "testUploadAndDownloadLargeData.txt",
+ expression: downloadExpression,
+ completionHandler: downloadCompletionHandler).continueWith(block: { (task) -> Any? in
+ XCTAssertNil(task.error)
+ return nil
+ })
+ } else {
+ XCTFail()
+ }
+ }
+
+ transferUtility.uploadData(
+ dataString.data(using: String.Encoding.utf8)!,
+ bucket: "ios-v2-s3.periods",
+ key: "testUploadAndDownloadLargeData.txt",
+ contentType: "text/plain",
+ expression: uploadExpression,
+ completionHandler: uploadCompletionHandler
+ ).continueWith (block: { (task) -> AnyObject? in
+ XCTAssertNil(task.error)
+
+ return nil
+ })
+
+ waitForExpectations(timeout: 30) { (error) in
+ XCTAssertNil(error)
+ }
+ }
+
+ func testSuspendResumeUploadAndDownloadLargeData() {
+ let expectation = self.expectation(description: "The completion handler called.")
+ var dataString = "Test1234"
+ for _ in 1...22 {
+ dataString = dataString + dataString;
+ }
+
+ // the test key is 1234567890123456
+ let password = "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="
+ let passwordMD5 = "dnF5x6K/8ZZRzpfSlMMM+w=="
+
+ let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "with-retry")
+ let uploadExpression = AWSS3TransferUtilityUploadExpression()
+ uploadExpression.setValue("AES256", forRequestHeader: "x-amz-server-side-encryption-customer-algorithm")
+ uploadExpression.setValue(password, forRequestHeader: "x-amz-server-side-encryption-customer-key")
+ uploadExpression.setValue(passwordMD5, forRequestHeader: "x-amz-server-side-encryption-customer-key-MD5")
+ uploadExpression.progressBlock = {(task, progress) in
+ print("Upload progress: ", progress.fractionCompleted)
+ }
+
+ let uploadCompletionHandler = { (task: AWSS3TransferUtilityUploadTask, error: Error?) -> Void in
+ XCTAssertNil(error)
+ if let HTTPResponse = task.response {
+ XCTAssertEqual(HTTPResponse.statusCode, 200)
+
+ let downloadExpression = AWSS3TransferUtilityDownloadExpression()
+ downloadExpression.setValue("AES256", forRequestHeader: "x-amz-server-side-encryption-customer-algorithm")
+ downloadExpression.setValue(password, forRequestHeader: "x-amz-server-side-encryption-customer-key")
+ downloadExpression.setValue(passwordMD5, forRequestHeader: "x-amz-server-side-encryption-customer-key-MD5")
+ downloadExpression.progressBlock = {(task, progress) in
+ print("Download progress: ", progress.fractionCompleted)
+ }
+ let downloadCompletionHandler = { (task: AWSS3TransferUtilityDownloadTask, URL: Foundation.URL?, data: Data?, error: Error?) in
+ if let HTTPResponse = task.response {
+ XCTAssertEqual(HTTPResponse.statusCode, 200)
+ XCTAssertEqual(data, dataString.data(using: String.Encoding.utf8)!)
+ XCTAssertEqual(HTTPResponse.allHeaderFields["Content-Type"] as? String, "text/plain")
+ } else {
+ XCTFail()
+ }
+
+ expectation.fulfill()
+ }
+
+ var refDownloadTask:AWSS3TransferUtilityDownloadTask?
+
+ transferUtility.downloadData(
+ fromBucket: "ios-v2-s3.periods",
+ key: "testSuspendResumeUploadAndDownloadLargeData.txt",
+ expression: downloadExpression,
+ completionHandler: downloadCompletionHandler).continueWith(block: { (task) -> Any? in
+ XCTAssertNil(task.error)
+ refDownloadTask = task.result
+ return nil
+ })
+
+ sleep(2)
+ refDownloadTask?.suspend()
+ sleep(5)
+ refDownloadTask?.resume()
+
+ } else {
+ XCTFail()
+ }
+ }
+
+ var refUploadTask: AWSS3TransferUtilityUploadTask?
+
+ transferUtility.uploadData(
+ dataString.data(using: String.Encoding.utf8)!,
+ bucket: "ios-v2-s3.periods",
+ key: "testSuspendResumeUploadAndDownloadLargeData.txt",
+ contentType: "text/plain",
+ expression: uploadExpression,
+ completionHandler: uploadCompletionHandler
+ ).continueWith (block: { (task) -> AnyObject? in
+ XCTAssertNil(task.error)
+ refUploadTask = task.result
+ return nil
+ })
+
+ sleep(2)
+ refUploadTask?.suspend()
+ sleep(5)
+ refUploadTask?.resume()
+
+ waitForExpectations(timeout: 90) { (error) in
+ XCTAssertNil(error)
+ }
+ }
+
+
+ func testCancelDownload() {
+ let expectation = self.expectation(description: "Cancel called.")
+ var dataString = "Test1234"
+ for _ in 1...20 {
+ dataString = dataString + dataString;
+ }
+
+ // the test key is 1234567890123456
+ let password = "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="
+ let passwordMD5 = "dnF5x6K/8ZZRzpfSlMMM+w=="
+
+ let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "with-retry")
+ let uploadExpression = AWSS3TransferUtilityUploadExpression()
+ uploadExpression.setValue("AES256", forRequestHeader: "x-amz-server-side-encryption-customer-algorithm")
+ uploadExpression.setValue(password, forRequestHeader: "x-amz-server-side-encryption-customer-key")
+ uploadExpression.setValue(passwordMD5, forRequestHeader: "x-amz-server-side-encryption-customer-key-MD5")
+ uploadExpression.progressBlock = {(task, progress) in
+ print("Upload progress: ", progress.fractionCompleted)
+ }
+
+ let uploadCompletionHandler = { (task: AWSS3TransferUtilityUploadTask, error: Error?) -> Void in
+ XCTAssertNil(error)
+ if let HTTPResponse = task.response {
+ XCTAssertEqual(HTTPResponse.statusCode, 200)
+
+ let downloadExpression = AWSS3TransferUtilityDownloadExpression()
+ downloadExpression.setValue("AES256", forRequestHeader: "x-amz-server-side-encryption-customer-algorithm")
+ downloadExpression.setValue(password, forRequestHeader: "x-amz-server-side-encryption-customer-key")
+ downloadExpression.setValue(passwordMD5, forRequestHeader: "x-amz-server-side-encryption-customer-key-MD5")
+ downloadExpression.progressBlock = {(task, progress) in
+ print("Download progress: ", progress.fractionCompleted)
+ }
+ let downloadCompletionHandler = { (task: AWSS3TransferUtilityDownloadTask, URL: Foundation.URL?, data: Data?, error: Error?) in
+ if let HTTPResponse = task.response {
+ XCTAssertEqual(HTTPResponse.statusCode, 200)
+ XCTAssertEqual(data, dataString.data(using: String.Encoding.utf8)!)
+ XCTAssertEqual(HTTPResponse.allHeaderFields["Content-Type"] as? String, "text/plain")
+ } else {
+ XCTFail()
+ }
+
+ }
+
+ var refDownloadTask:AWSS3TransferUtilityDownloadTask?
+
+ transferUtility.downloadData(
+ fromBucket: "ios-v2-s3.periods",
+ key: "testCancelDownload.txt",
+ expression: downloadExpression,
+ completionHandler: downloadCompletionHandler).continueWith(block: { (task) -> Any? in
+ XCTAssertNil(task.error)
+ refDownloadTask = task.result
+ return nil
+ })
+
+ sleep(2)
+ refDownloadTask?.cancel()
+ expectation.fulfill()
+
+ } else {
+ XCTFail()
+ }
+ }
+
+
+ transferUtility.uploadData(
+ dataString.data(using: String.Encoding.utf8)!,
+ bucket: "ios-v2-s3.periods",
+ key: "testCancelDownload.txt",
+ contentType: "text/plain",
+ expression: uploadExpression,
+ completionHandler: uploadCompletionHandler
+ ).continueWith (block: { (task) -> AnyObject? in
+ XCTAssertNil(task.error)
+ return nil
+ })
+ waitForExpectations(timeout: 60) { (error) in
+ XCTAssertNil(error)
+ }
+ }
+
+ func testSuspendResumeUploadAndDownloadLargeDataLongDelay() {
+ let expectation = self.expectation(description: "The completion handler called.")
+ var dataString = "Test12"
+ for _ in 1...22 {
+ dataString = dataString + dataString;
+ }
+
+ // the test key is 1234567890123456
+ let password = "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="
+ let passwordMD5 = "dnF5x6K/8ZZRzpfSlMMM+w=="
+
+ let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "with-retry")
+ let uploadExpression = AWSS3TransferUtilityUploadExpression()
+ uploadExpression.setValue("AES256", forRequestHeader: "x-amz-server-side-encryption-customer-algorithm")
+ uploadExpression.setValue(password, forRequestHeader: "x-amz-server-side-encryption-customer-key")
+ uploadExpression.setValue(passwordMD5, forRequestHeader: "x-amz-server-side-encryption-customer-key-MD5")
+ uploadExpression.progressBlock = {(task, progress) in
+ print("Upload progress: ", progress.fractionCompleted)
+ }
+
+ let uploadCompletionHandler = { (task: AWSS3TransferUtilityUploadTask, error: Error?) -> Void in
+ XCTAssertNil(error)
+ if let HTTPResponse = task.response {
+ XCTAssertEqual(HTTPResponse.statusCode, 200)
+
+ let downloadExpression = AWSS3TransferUtilityDownloadExpression()
+ downloadExpression.setValue("AES256", forRequestHeader: "x-amz-server-side-encryption-customer-algorithm")
+ downloadExpression.setValue(password, forRequestHeader: "x-amz-server-side-encryption-customer-key")
+ downloadExpression.setValue(passwordMD5, forRequestHeader: "x-amz-server-side-encryption-customer-key-MD5")
+ downloadExpression.progressBlock = {(task, progress) in
+ print("Download progress: ", progress.fractionCompleted)
+ }
+ let downloadCompletionHandler = { (task: AWSS3TransferUtilityDownloadTask, URL: Foundation.URL?, data: Data?, error: Error?) in
+ if let HTTPResponse = task.response {
+ XCTAssertEqual(HTTPResponse.statusCode, 200)
+ XCTAssertEqual(data, dataString.data(using: String.Encoding.utf8)!)
+ XCTAssertEqual(HTTPResponse.allHeaderFields["Content-Type"] as? String, "text/plain")
+ } else {
+ XCTFail()
+ }
+
+ expectation.fulfill()
+ }
+
+ var refDownloadTask:AWSS3TransferUtilityDownloadTask?
+
+ transferUtility.downloadData(
+ fromBucket: "ios-v2-s3.periods",
+ key: "testSuspendResumeUploadAndDownloadLargeDataLongDelay.txt",
+ expression: downloadExpression,
+ completionHandler: downloadCompletionHandler).continueWith(block: { (task) -> Any? in
+ XCTAssertNil(task.error)
+ refDownloadTask = task.result
+ return nil
+ })
+
+ sleep(2)
+ refDownloadTask?.suspend()
+ print("Sleeping for 30 seconds to allow server to Timeout on the download")
+ sleep(30)
+ refDownloadTask?.resume()
+
+ } else {
+ XCTFail()
+ }
+ }
+
+ var refUploadTask: AWSS3TransferUtilityUploadTask?
+
+ transferUtility.uploadData(
+ dataString.data(using: String.Encoding.utf8)!,
+ bucket: "ios-v2-s3.periods",
+ key: "testSuspendResumeUploadAndDownloadLargeDataLongDelay.txt",
+ contentType: "text/plain",
+ expression: uploadExpression,
+ completionHandler: uploadCompletionHandler
+ ).continueWith (block: { (task) -> AnyObject? in
+ XCTAssertNil(task.error)
+ refUploadTask = task.result
+ return nil
+ })
+
+ sleep(2)
+ refUploadTask?.suspend()
+ print("Sleeping for 30 seconds to allow server to Timeout on the upload")
+ sleep(30)
+ refUploadTask?.resume()
+
+ waitForExpectations(timeout: 300) { (error) in
+ XCTAssertNil(error)
+ }
+ }
+
func testUploadFailure() {
let expectation = self.expectation(description: "The completion handler called.")
let password = "InvalidPassword"
let passwordMD5 = "InvalidPasswordMD5"
- let transferUtility = AWSS3TransferUtility.default()
+ let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "with-retry")
let uploadExpression = AWSS3TransferUtilityUploadExpression()
uploadExpression.setValue("AES256", forRequestHeader: "x-amz-server-side-encryption-customer-algorithm")
uploadExpression.setValue(password, forRequestHeader: "x-amz-server-side-encryption-customer-key")
@@ -145,7 +507,7 @@ class AWSS3TransferUtilityTests: XCTestCase {
expectation .fulfill()
}
-
+
transferUtility.uploadData(
testData,
bucket: "ios-v2-s3.periods",
@@ -232,6 +594,7 @@ class AWSS3TransferUtilityTests: XCTestCase {
expression: nil,
completionHandler: nil
).continueWith (block: { (task) -> AnyObject? in
+ sleep(2)
XCTAssertNotNil(task.error)
XCTAssertEqual(task.error?._domain, AWSS3PresignedURLErrorDomain)
XCTAssertEqual(task.error?._code, AWSS3PresignedURLErrorType.presignedURLErrorInvalidBucketNameForAccelerateModeEnabled.rawValue)
@@ -299,7 +662,7 @@ class AWSS3TransferUtilityTests: XCTestCase {
}
func testBadFilePathUpload() {
- let transferUtility = AWSS3TransferUtility.default()
+ let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "with-retry")
transferUtility.uploadFile(URL(fileURLWithPath: "~/abc.txt"),
bucket: "ios-v2-s3.periods",
@@ -318,17 +681,22 @@ class AWSS3TransferUtilityTests: XCTestCase {
}
func testGoodFilePathUpload() {
- let transferUtility = AWSS3TransferUtility.default()
+ let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "with-retry")
let filePath = NSTemporaryDirectory() + "testGoodFilePathUpload.tmp"
let fileURL = URL(fileURLWithPath: filePath)
- FileManager.default.createFile(atPath: filePath, contents: "Test".data(using: .utf8), attributes: nil)
+ FileManager.default.createFile(atPath: filePath, contents: "Test1234".data(using: .utf8), attributes: nil)
+
+ let expression = AWSS3TransferUtilityUploadExpression()
+ expression.progressBlock = {(task, progress) in
+ print("Upload progress: ", progress.fractionCompleted)
+ }
transferUtility.uploadFile(fileURL,
bucket: "ios-v2-s3.periods",
key: "testGoodFilePathUpload.txt",
contentType: "text/plain",
- expression: nil,
+ expression: expression,
completionHandler: nil)
.continueWith { (task: AWSTask) -> Any? in
XCTAssertNil(task.error)
@@ -337,8 +705,182 @@ class AWSS3TransferUtilityTests: XCTestCase {
}.waitUntilFinished()
}
+ func testLargeFileUpload() {
+ let expectation = self.expectation(description: "The completion handler called.")
+ let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "with-retry")
+ //Create a large temp file;
+ let filePath = NSTemporaryDirectory() + "testLargeFileUpload.tmp"
+ var testData = "Test1234"
+ for _ in 1...20 {
+ testData = testData + testData;
+ }
+ let fileURL = URL(fileURLWithPath: filePath)
+ FileManager.default.createFile(atPath: filePath, contents: testData.data(using: .utf8), attributes: nil)
+
+ let expression = AWSS3TransferUtilityUploadExpression()
+ expression.progressBlock = {(task, progress) in
+ print("Upload progress: ", progress.fractionCompleted)
+ }
+
+ //Create Completion Handler
+ let uploadCompletionHandler = { (task: AWSS3TransferUtilityUploadTask, error: Error?) -> Void in
+ XCTAssertNil(error)
+ expectation.fulfill()
+ }
+
+ transferUtility.uploadFile(fileURL,
+ bucket: "ios-v2-s3.periods",
+ key: "testLargeFileUpload.txt",
+ contentType: "text/plain",
+ expression: expression,
+ completionHandler: uploadCompletionHandler)
+ .continueWith { (task: AWSTask) -> Any? in
+ XCTAssertNil(task.error)
+ XCTAssertNotNil(task.result)
+ return nil
+ }.waitUntilFinished()
+
+ waitForExpectations(timeout: 30) { (error) in
+ XCTAssertNil(error)
+ }
+ }
+
+ func testLargeFileUploadCancel() {
+ let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "with-retry")
+ //Create a large temp file;
+ let filePath = NSTemporaryDirectory() + "testLargeFileUploadCancel.tmp"
+ var testData = "Test1234"
+ for _ in 1...20 {
+ testData = testData + testData;
+ }
+ let fileURL = URL(fileURLWithPath: filePath)
+ FileManager.default.createFile(atPath: filePath, contents: testData.data(using: .utf8), attributes: nil)
+
+ let expression = AWSS3TransferUtilityUploadExpression()
+ expression.progressBlock = {(task, progress) in
+ print("Upload progress: ", progress.fractionCompleted)
+ }
+
+ //Create Completion Handler
+ let uploadCompletionHandler = { (task: AWSS3TransferUtilityUploadTask, error: Error?) -> Void in
+ XCTAssertNil(error)
+ }
+
+ var refUploadTask: AWSS3TransferUtilityUploadTask?
+
+ transferUtility.uploadFile(fileURL,
+ bucket: "ios-v2-s3.periods",
+ key: "testLargeFileUploadCancel.txt",
+ contentType: "text/plain",
+ expression: expression,
+ completionHandler: uploadCompletionHandler)
+ .continueWith { (task: AWSTask) -> Any? in
+ XCTAssertNil(task.error)
+ XCTAssertNotNil(task.result)
+ refUploadTask = task.result
+ return nil
+ }.waitUntilFinished()
+ sleep(2)
+ refUploadTask?.cancel()
+ }
+
+ func testSuspendResumeLargeFileUpload() {
+ let expectation = self.expectation(description: "The completion handler called.")
+ let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "with-retry")
+ //Create a large temp file;
+ let filePath = NSTemporaryDirectory() + "testSuspendResumeLargeFileUpload.tmp"
+ var testData = "Test1234"
+ for _ in 1...20 {
+ testData = testData + testData;
+ }
+ let fileURL = URL(fileURLWithPath: filePath)
+ FileManager.default.createFile(atPath: filePath, contents: testData.data(using: .utf8), attributes: nil)
+
+ let expression = AWSS3TransferUtilityUploadExpression()
+ expression.progressBlock = {(task, progress) in
+ print("Upload progress: ", progress.fractionCompleted)
+ }
+
+ //Create Completion Handler
+ let uploadCompletionHandler = { (task: AWSS3TransferUtilityUploadTask, error: Error?) -> Void in
+ XCTAssertNil(error)
+ expectation.fulfill()
+ }
+ var refUploadTask: AWSS3TransferUtilityUploadTask?
+
+ transferUtility.uploadFile(fileURL,
+ bucket: "ios-v2-s3.periods",
+ key: "testSuspendResumeLargeFileUpload.txt",
+ contentType: "text/plain",
+ expression: expression,
+ completionHandler: uploadCompletionHandler)
+ .continueWith { (task: AWSTask) -> Any? in
+ XCTAssertNil(task.error)
+ XCTAssertNotNil(task.result)
+ refUploadTask = task.result
+ return nil
+ }.waitUntilFinished()
+
+ sleep(2)
+ refUploadTask?.suspend()
+
+ sleep(5)
+ refUploadTask?.resume()
+ waitForExpectations(timeout: 30) { (error) in
+ XCTAssertNil(error)
+ }
+ }
+
+ func testSuspendResumeLargeFileUploadLongDelay() {
+ let expectation = self.expectation(description: "The completion handler called.")
+
+ let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "with-retry")
+ //Create a large temp file;
+ let filePath = NSTemporaryDirectory() + "testSuspendResumeLargeFileUploadLongDelay.tmp"
+ var testData = "Test1234"
+ for _ in 1...21 {
+ testData = testData + testData;
+ }
+ let fileURL = URL(fileURLWithPath: filePath)
+ FileManager.default.createFile(atPath: filePath, contents: testData.data(using: .utf8), attributes: nil)
+
+ let expression = AWSS3TransferUtilityUploadExpression()
+ expression.progressBlock = {(task, progress) in
+ print("Upload progress: ", progress.fractionCompleted)
+ }
+
+ //Create Completion Handler
+ let uploadCompletionHandler = { (task: AWSS3TransferUtilityUploadTask, error: Error?) -> Void in
+ XCTAssertNil(error)
+ expectation.fulfill()
+ }
+ var refUploadTask: AWSS3TransferUtilityUploadTask?
+
+ transferUtility.uploadFile(fileURL,
+ bucket: "ios-v2-s3.periods",
+ key: "testSuspendResumeLargeFileUploadLongDelay.txt",
+ contentType: "text/plain",
+ expression: expression,
+ completionHandler: uploadCompletionHandler)
+ .continueWith { (task: AWSTask) -> Any? in
+ XCTAssertNil(task.error)
+ XCTAssertNotNil(task.result)
+ refUploadTask = task.result
+ return nil
+ }.waitUntilFinished()
+
+ sleep(2)
+ refUploadTask?.suspend()
+ print("Sleeping for 30 seconds to allow server to Timeout")
+ sleep(30)
+ refUploadTask?.resume()
+ waitForExpectations(timeout: 300) { (error) in
+ XCTAssertNil(error)
+ }
+ }
+
func testGoodFilePathUploadDefaultBucket() {
- let transferUtility = AWSS3TransferUtility.default()
+ let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "with-retry")
let transferUtilityCongiguration = AWSS3TransferUtilityConfiguration()
@@ -365,7 +907,7 @@ class AWSS3TransferUtilityTests: XCTestCase {
}
func testGoodFilePathWithSpacesUpload() {
- let transferUtility = AWSS3TransferUtility.default()
+ let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "with-retry")
let filePath = NSTemporaryDirectory() + "test Good File Path Upload.tmp"
let fileURL = URL(fileURLWithPath: filePath)
@@ -383,4 +925,327 @@ class AWSS3TransferUtilityTests: XCTestCase {
return nil
}.waitUntilFinished()
}
+
+ func testMultiPartUploadSmallFile() {
+ let expectation = self.expectation(description: "The completion handler called.")
+ let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "with-retry")
+
+ let filePath = NSTemporaryDirectory() + "testMultiPartUploadSmallFile.tmp"
+ let fileURL = URL(fileURLWithPath: filePath)
+ FileManager.default.createFile(atPath: filePath, contents: "This is a test".data(using: .utf8), attributes: nil)
+
+ let uploadCompletionHandler = { (task: AWSS3TransferUtilityMultiPartUploadTask, error: Error?) -> Void in
+ XCTAssertNil(error)
+ expectation.fulfill()
+ }
+
+ transferUtility.uploadUsingMultiPart(fileURL:fileURL,
+ bucket: "ios-v2-s3.periods",
+ key: "testMultiPartUploadSmallFile.txt",
+ contentType: "text/plain",
+ expression: nil,
+ completionHandler: uploadCompletionHandler)
+ .continueWith { (task: AWSTask) -> Any? in
+ XCTAssertNil(task.error)
+ XCTAssertNotNil(task.result)
+ return nil
+ }.waitUntilFinished()
+
+ waitForExpectations(timeout: 30) { (error) in
+ XCTAssertNil(error)
+ }
+ }
+
+ func testMultiPartUploadLargeFile() {
+ //Create a large temp file;
+ let filePath = NSTemporaryDirectory() + "testMultiPartUploadLargeFile.tmp"
+ var testData = "Test123456789"
+ for _ in 1...21 {
+ testData = testData + testData;
+ }
+ let fileURL = URL(fileURLWithPath: filePath)
+ FileManager.default.createFile(atPath: filePath, contents: testData.data(using: .utf8), attributes: nil)
+
+ let expectation = self.expectation(description: "The completion handler called.")
+
+ //Create Completion Handler
+ let uploadCompletionHandler = { (task: AWSS3TransferUtilityMultiPartUploadTask, error: Error?) -> Void in
+ XCTAssertNil(error)
+ expectation.fulfill()
+ }
+
+ let expression = AWSS3TransferUtilityMultiPartUploadExpression()
+ expression.progressBlock = {(task, progress) in
+ print("Upload progress: ", progress.fractionCompleted)
+ }
+
+ let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "with-retry")
+ transferUtility.uploadUsingMultiPart(fileURL: fileURL, bucket: "ios-v2-s3.periods",
+ key: "testMultiPartUploadLargeFile.txt",
+ contentType: "text/plain",
+ expression: expression,
+ completionHandler: uploadCompletionHandler)
+ .continueWith { (task: AWSTask) -> Any? in
+ XCTAssertNil(task.error)
+ XCTAssertNotNil(task.result)
+ return nil
+ }.waitUntilFinished()
+
+ waitForExpectations(timeout: 240) { (error) in
+ XCTAssertNil(error)
+ }
+ }
+
+ func testMultiPartSinglePartEdgeCase() {
+ //Create a large temp file that is exactly 5 MB;
+ let filePath = NSTemporaryDirectory() + "testMultiPartSinglePartEdgeCase.tmp"
+ var testData = "Test1"
+ for _ in 1...20 {
+ testData = testData + testData;
+ }
+ let fileURL = URL(fileURLWithPath: filePath)
+ FileManager.default.createFile(atPath: filePath, contents: testData.data(using: .utf8), attributes: nil)
+
+ let expectation = self.expectation(description: "The completion handler called.")
+
+ //Create Completion Handler
+ let uploadCompletionHandler = { (task: AWSS3TransferUtilityMultiPartUploadTask, error: Error?) -> Void in
+ XCTAssertNil(error)
+ expectation.fulfill()
+ }
+
+ let expression = AWSS3TransferUtilityMultiPartUploadExpression()
+ expression.progressBlock = {(task, progress) in
+ print("Upload progress: ", progress.fractionCompleted)
+ }
+
+ let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "with-retry")
+ transferUtility.uploadUsingMultiPart(fileURL:fileURL, bucket: "ios-v2-s3.periods",
+ key: "testMultiPartSinglePartEdgeCase.txt",
+ contentType: "text/plain",
+ expression: expression,
+ completionHandler: uploadCompletionHandler)
+ .continueWith { (task: AWSTask) -> Any? in
+ XCTAssertNil(task.error)
+ XCTAssertNotNil(task.result)
+ return nil
+ }.waitUntilFinished()
+
+ waitForExpectations(timeout: 120) { (error) in
+ XCTAssertNil(error)
+ }
+ }
+
+ func testMultiPartEdgeCase() {
+ //Create a large temp file that is exactly 10 MB;
+ let filePath = NSTemporaryDirectory() + "testMultiPartEdgeCase.tmp"
+ var testData = "Test1"
+ for _ in 1...21 {
+ testData = testData + testData;
+ }
+ let fileURL = URL(fileURLWithPath: filePath)
+ FileManager.default.createFile(atPath: filePath, contents: testData.data(using: .utf8), attributes: nil)
+
+ let expectation = self.expectation(description: "The completion handler called.")
+
+ //Create Completion Handler
+ let uploadCompletionHandler = { (task: AWSS3TransferUtilityMultiPartUploadTask, error: Error?) -> Void in
+ XCTAssertNil(error)
+ expectation.fulfill()
+ }
+
+ let expression = AWSS3TransferUtilityMultiPartUploadExpression()
+ expression.progressBlock = {(task, progress) in
+ print("Upload progress: ", progress.fractionCompleted)
+ }
+
+ let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "with-retry")
+ transferUtility.uploadUsingMultiPart(fileURL: fileURL, bucket: "ios-v2-s3.periods",
+ key: "testMultiPartEdgeCase.txt",
+ contentType: "text/plain",
+ expression: expression,
+ completionHandler: uploadCompletionHandler)
+ .continueWith { (task: AWSTask) -> Any? in
+ XCTAssertNil(task.error)
+ XCTAssertNotNil(task.result)
+ return nil
+ }.waitUntilFinished()
+
+ waitForExpectations(timeout: 120) { (error) in
+ XCTAssertNil(error)
+ }
+ }
+
+ func testCancelMultipartUpload() {
+ //Create a large temp file;
+ let filePath = NSTemporaryDirectory() + "testCancelMultipartUpload.tmp"
+ var testData = "CancelT"
+ for _ in 1...24 {
+ testData = testData + testData;
+ }
+ let fileURL = URL(fileURLWithPath: filePath)
+ FileManager.default.createFile(atPath: filePath, contents: testData.data(using: .utf8), attributes: nil)
+
+
+ let expression = AWSS3TransferUtilityMultiPartUploadExpression()
+ expression.progressBlock = {(task, progress) in
+ print("Upload progress: ", progress.fractionCompleted)
+ }
+
+ var refUploadTask: AWSS3TransferUtilityMultiPartUploadTask?
+
+ let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "with-retry")
+ transferUtility.uploadUsingMultiPart(fileURL: fileURL, bucket: "ios-v2-s3.periods",
+ key: "testCancelMultipartUpload.txt",
+ contentType: "text/plain",
+ expression: expression,
+ completionHandler: nil)
+ .continueWith { (task: AWSTask) -> Any? in
+ XCTAssertNil(task.error)
+ XCTAssertNotNil(task.result)
+ refUploadTask = task.result
+ return nil;
+ }
+ sleep(2)
+ refUploadTask?.cancel()
+
+ }
+
+ func testSuspendResumeMultipartUpload() {
+ //Create a large temp file;
+ let filePath = NSTemporaryDirectory() + "testSuspendResumeMultipartUpload.tmp"
+ var testData = "PauseResumeTest"
+ for _ in 1...20 {
+ testData = testData + testData;
+ }
+ let fileURL = URL(fileURLWithPath: filePath)
+ FileManager.default.createFile(atPath: filePath, contents: testData.data(using: .utf8), attributes: nil)
+
+ let expectation = self.expectation(description: "The completion handler called.")
+
+ //Create Completion Handler
+ let uploadCompletionHandler = { (task: AWSS3TransferUtilityMultiPartUploadTask, error: Error?) -> Void in
+ XCTAssertNil(error)
+ expectation.fulfill()
+ }
+
+ let expression = AWSS3TransferUtilityMultiPartUploadExpression()
+ expression.progressBlock = {(task, progress) in
+ print("Upload progress: ", progress.fractionCompleted)
+ }
+
+ var refUploadTask: AWSS3TransferUtilityMultiPartUploadTask?
+
+ let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "with-retry")
+ transferUtility.uploadUsingMultiPart(fileURL: fileURL, bucket: "ios-v2-s3.periods",
+ key: "testSuspendResumeMultipartUpload.txt",
+ contentType: "text/plain",
+ expression: expression,
+ completionHandler: uploadCompletionHandler)
+ .continueWith { (task: AWSTask) -> Any? in
+ XCTAssertNil(task.error)
+ XCTAssertNotNil(task.result)
+ refUploadTask = task.result
+ return nil;
+ }
+ sleep(2)
+ refUploadTask?.suspend()
+ sleep(5)
+ refUploadTask?.resume()
+
+ waitForExpectations(timeout: 120) { (error) in
+ XCTAssertNil(error)
+ }
+ }
+
+ func testSuspendResumeMultipartUploadLongDelay() {
+ //Create a large temp file;
+ let filePath = NSTemporaryDirectory() + "testPauseResumeUploadLongDelay.tmp"
+ var testData = "PauseResumeTest"
+ for _ in 1...22 {
+ testData = testData + testData;
+ }
+ let fileURL = URL(fileURLWithPath: filePath)
+ FileManager.default.createFile(atPath: filePath, contents: testData.data(using: .utf8), attributes: nil)
+
+ let expectation = self.expectation(description: "The completion handler called.")
+
+ //Create Completion Handler
+ let uploadCompletionHandler = { (task: AWSS3TransferUtilityMultiPartUploadTask, error: Error?) -> Void in
+ XCTAssertNil(error)
+ expectation.fulfill()
+ }
+
+ let expression = AWSS3TransferUtilityMultiPartUploadExpression()
+ expression.progressBlock = {(task, progress) in
+ print("Upload progress: ", progress.fractionCompleted)
+ }
+
+ var refUploadTask: AWSS3TransferUtilityMultiPartUploadTask?
+
+ let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "with-retry")
+
+ transferUtility.uploadUsingMultiPart(fileURL: fileURL, bucket: "ios-v2-s3.periods",
+ key: "testPauseResumeUploadLongDelay.txt",
+ contentType: "text/plain",
+ expression: expression,
+ completionHandler: uploadCompletionHandler)
+ .continueWith { (task: AWSTask) -> Any? in
+ XCTAssertNil(task.error)
+ XCTAssertNotNil(task.result)
+ refUploadTask = task.result
+ return nil;
+ }
+ sleep(2)
+ refUploadTask?.suspend()
+ print("Sleeping for 30 seconds to allow server to Timeout")
+ sleep(30)
+ refUploadTask?.resume()
+
+ waitForExpectations(timeout: 300) { (error) in
+ XCTAssertNil(error)
+ }
+ }
+
+ func testMultiPartUploadTransferAcceleration() {
+ //Create a large temp file;
+ let filePath = NSTemporaryDirectory() + "testMultiPartUploadTransferAcceleration.tmp"
+ var testData = "Test1234"
+ for _ in 1...22 {
+ testData = testData + testData;
+ }
+ let fileURL = URL(fileURLWithPath: filePath)
+ FileManager.default.createFile(atPath: filePath, contents: testData.data(using: .utf8), attributes: nil)
+
+ let expectation = self.expectation(description: "The completion handler called.")
+
+ //Create Completion Handler
+ let uploadCompletionHandler = { (task: AWSS3TransferUtilityMultiPartUploadTask, error: Error?) -> Void in
+ XCTAssertNil(error)
+ expectation.fulfill()
+ }
+
+ let expression = AWSS3TransferUtilityMultiPartUploadExpression()
+ expression.progressBlock = {(task, progress) in
+ print("Upload progress: ", progress.fractionCompleted)
+ }
+
+ let transferUtility = AWSS3TransferUtility.s3TransferUtility(forKey: "transfer-acceleration")
+
+ transferUtility.uploadUsingMultiPart(fileURL:fileURL, bucket: "ios-v2-s3-transfer-acceleration",
+ key: "testMultiPartUploadTransferAcceleration.txt",
+ contentType: "text/plain",
+ expression: expression,
+ completionHandler: uploadCompletionHandler)
+ .continueWith { (task: AWSTask) -> Any? in
+ XCTAssertNil(task.error)
+ XCTAssertNotNil(task.result)
+ return nil
+ }.waitUntilFinished()
+
+ waitForExpectations(timeout: 120) { (error) in
+ XCTAssertNil(error)
+ }
+ }
+
}
diff --git a/AWSSES.podspec b/AWSSES.podspec
index f5d02e03cae..73550906196 100644
--- a/AWSSES.podspec
+++ b/AWSSES.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSSES'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,6 +12,6 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSSES/*.{h,m}'
end
diff --git a/AWSSES/AWSSESService.m b/AWSSES/AWSSESService.m
index 4ff2ed56420..60fdef062de 100644
--- a/AWSSES/AWSSESService.m
+++ b/AWSSES/AWSSESService.m
@@ -26,7 +26,7 @@
#import "AWSSESResources.h"
static NSString *const AWSInfoSES = @"SES";
-static NSString *const AWSSESSDKVersion = @"2.6.12";
+static NSString *const AWSSESSDKVersion = @"2.6.13";
@interface AWSSESResponseSerializer : AWSXMLResponseSerializer
diff --git a/AWSSES/Info.plist b/AWSSES/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSSES/Info.plist
+++ b/AWSSES/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSSNS.podspec b/AWSSNS.podspec
index 9079b2a6a84..fcd3015eb32 100644
--- a/AWSSNS.podspec
+++ b/AWSSNS.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSSNS'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,6 +12,6 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSSNS/*.{h,m}'
end
diff --git a/AWSSNS/AWSSNSService.m b/AWSSNS/AWSSNSService.m
index d776e99121a..6be9dc7ba42 100644
--- a/AWSSNS/AWSSNSService.m
+++ b/AWSSNS/AWSSNSService.m
@@ -26,7 +26,7 @@
#import "AWSSNSResources.h"
static NSString *const AWSInfoSNS = @"SNS";
-static NSString *const AWSSNSSDKVersion = @"2.6.12";
+static NSString *const AWSSNSSDKVersion = @"2.6.13";
@interface AWSSNSResponseSerializer : AWSXMLResponseSerializer
diff --git a/AWSSNS/Info.plist b/AWSSNS/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSSNS/Info.plist
+++ b/AWSSNS/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSSQS.podspec b/AWSSQS.podspec
index 3acf55139ab..de476a5a591 100644
--- a/AWSSQS.podspec
+++ b/AWSSQS.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSSQS'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,6 +12,6 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSSQS/*.{h,m}'
end
diff --git a/AWSSQS/AWSSQSService.m b/AWSSQS/AWSSQSService.m
index 1e05fea2823..6d42a6ab5d9 100644
--- a/AWSSQS/AWSSQSService.m
+++ b/AWSSQS/AWSSQSService.m
@@ -26,7 +26,7 @@
#import "AWSSQSResources.h"
static NSString *const AWSInfoSQS = @"SQS";
-static NSString *const AWSSQSSDKVersion = @"2.6.12";
+static NSString *const AWSSQSSDKVersion = @"2.6.13";
@interface AWSSQSResponseSerializer : AWSXMLResponseSerializer
diff --git a/AWSSQS/Info.plist b/AWSSQS/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSSQS/Info.plist
+++ b/AWSSQS/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSSimpleDB.podspec b/AWSSimpleDB.podspec
index 0e6428979bc..871e3426e1e 100644
--- a/AWSSimpleDB.podspec
+++ b/AWSSimpleDB.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSSimpleDB'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,6 +12,6 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCore', '2.6.12'
+ s.dependency 'AWSCore', '2.6.13'
s.source_files = 'AWSSimpleDB/*.{h,m}'
end
diff --git a/AWSSimpleDB/AWSSimpleDBService.m b/AWSSimpleDB/AWSSimpleDBService.m
index fbcc4f11962..a14ea1b307c 100644
--- a/AWSSimpleDB/AWSSimpleDBService.m
+++ b/AWSSimpleDB/AWSSimpleDBService.m
@@ -26,7 +26,7 @@
#import "AWSSimpleDBResources.h"
static NSString *const AWSInfoSimpleDB = @"SimpleDB";
-static NSString *const AWSSimpleDBSDKVersion = @"2.6.12";
+static NSString *const AWSSimpleDBSDKVersion = @"2.6.13";
@interface AWSSimpleDBResponseSerializer : AWSXMLResponseSerializer
diff --git a/AWSSimpleDB/Info.plist b/AWSSimpleDB/Info.plist
index 4b42c10313a..f33ea515c35 100644
--- a/AWSSimpleDB/Info.plist
+++ b/AWSSimpleDB/Info.plist
@@ -15,7 +15,7 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 2.6.12
+ 2.6.13
CFBundleSignature
????
CFBundleVersion
diff --git a/AWSUserPoolsSignIn.podspec b/AWSUserPoolsSignIn.podspec
index d35b8848b33..ebc8cacc450 100644
--- a/AWSUserPoolsSignIn.podspec
+++ b/AWSUserPoolsSignIn.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'AWSUserPoolsSignIn'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -12,8 +12,8 @@ Pod::Spec.new do |s|
s.source = { :git => 'https://github.com/aws/aws-sdk-ios.git',
:tag => s.version}
s.requires_arc = true
- s.dependency 'AWSCognitoIdentityProvider', '2.6.12'
- s.dependency 'AWSAuthCore', '2.6.12'
+ s.dependency 'AWSCognitoIdentityProvider', '2.6.13'
+ s.dependency 'AWSAuthCore', '2.6.13'
s.source_files = 'AWSAuthSDK/Sources/AWSUserPoolsSignIn/**/*.{h,m}'
s.public_header_files = 'AWSAuthSDK/Sources/AWSUserPoolsSignIn/*.{h}'
s.private_header_files = 'AWSAuthSDK/Sources/AWSUserPoolsSignIn/UserPoolsUI/*.{h}'
diff --git a/AWSiOSSDKv2.podspec b/AWSiOSSDKv2.podspec
index dec8ddaac47..eebf7a8fdf4 100644
--- a/AWSiOSSDKv2.podspec
+++ b/AWSiOSSDKv2.podspec
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = 'AWSiOSSDKv2'
- s.version = '2.6.12'
+ s.version = '2.6.13'
s.summary = 'Amazon Web Services SDK for iOS.'
s.description = 'The AWS SDK for iOS provides a library, code samples, and documentation for developers to build connected mobile applications using AWS.'
@@ -15,103 +15,103 @@ Pod::Spec.new do |s|
s.requires_arc = true
s.subspec 'AWSCore' do |aws|
- aws.dependency 'AWSCore', '2.6.12'
+ aws.dependency 'AWSCore', '2.6.13'
end
s.subspec 'AWSAPIGateway' do |apigateway|
- apigateway.dependency 'AWSAPIGateway', '2.6.12'
+ apigateway.dependency 'AWSAPIGateway', '2.6.13'
end
s.subspec 'AutoScaling' do |autoscaling|
- autoscaling.dependency 'AWSAutoScaling', '2.6.12'
+ autoscaling.dependency 'AWSAutoScaling', '2.6.13'
end
s.subspec 'CloudWatch' do |cloudwatch|
- cloudwatch.dependency 'AWSCloudWatch', '2.6.12'
+ cloudwatch.dependency 'AWSCloudWatch', '2.6.13'
end
s.subspec 'Pinpoint' do |pp|
- pp.dependency 'AWSPinpoint', '2.6.12'
+ pp.dependency 'AWSPinpoint', '2.6.13'
end
s.subspec 'AWSCognito' do |cognito|
- cognito.dependency 'AWSCognito', '2.6.12'
+ cognito.dependency 'AWSCognito', '2.6.13'
end
s.subspec 'AWSCognitoIdentityProvider' do |cognitoidentityprovider|
- cognitoidentityprovider.dependency 'AWSCognitoIdentityProvider', '2.6.12'
+ cognitoidentityprovider.dependency 'AWSCognitoIdentityProvider', '2.6.13'
end
s.subspec 'DynamoDB' do |ddb|
- ddb.dependency 'AWSDynamoDB', '2.6.12'
+ ddb.dependency 'AWSDynamoDB', '2.6.13'
end
s.subspec 'EC2' do |ec2|
- ec2.dependency 'AWSEC2', '2.6.12'
+ ec2.dependency 'AWSEC2', '2.6.13'
end
s.subspec 'ElasticLoadBalancing' do |elasticloadbalancing|
- elasticloadbalancing.dependency 'AWSElasticLoadBalancing', '2.6.12'
+ elasticloadbalancing.dependency 'AWSElasticLoadBalancing', '2.6.13'
end
s.subspec 'AWSIoT' do |iot|
- iot.dependency 'AWSIoT', '2.6.12'
+ iot.dependency 'AWSIoT', '2.6.13'
end
s.subspec 'Kinesis' do |kinesis|
- kinesis.dependency 'AWSKinesis', '2.6.12'
+ kinesis.dependency 'AWSKinesis', '2.6.13'
end
s.subspec 'AWSKMS' do |kms|
- kms.dependency 'AWSKMS', '2.6.12'
+ kms.dependency 'AWSKMS', '2.6.13'
end
s.subspec 'AWSLambda' do |lambda|
- lambda.dependency 'AWSLambda', '2.6.12'
+ lambda.dependency 'AWSLambda', '2.6.13'
end
s.subspec 'AWSLex' do |lex|
- lex.dependency 'AWSLex', '2.6.12'
+ lex.dependency 'AWSLex', '2.6.13'
end
s.subspec 'AWSLogs' do |log|
- log.dependency 'AWSLogs', '2.6.12'
+ log.dependency 'AWSLogs', '2.6.13'
end
s.subspec 'AWSMachineLearning' do |machinelearning|
- machinelearning.dependency 'AWSMachineLearning', '2.6.12'
+ machinelearning.dependency 'AWSMachineLearning', '2.6.13'
end
s.subspec 'AWSPolly' do |polly|
- polly.dependency 'AWSPolly', '2.6.12'
+ polly.dependency 'AWSPolly', '2.6.13'
end
s.subspec 'MobileAnalytics' do |mobileanalytics|
- mobileanalytics.dependency 'AWSMobileAnalytics', '2.6.12'
+ mobileanalytics.dependency 'AWSMobileAnalytics', '2.6.13'
end
s.subspec 'AWSRekognition' do |rekognition|
- rekognition.dependency 'AWSRekognition', '2.6.12'
+ rekognition.dependency 'AWSRekognition', '2.6.13'
end
s.subspec 'AWSS3' do |s3|
- s3.dependency 'AWSS3', '2.6.12'
+ s3.dependency 'AWSS3', '2.6.13'
end
s.subspec 'AWSSES' do |ses|
- ses.dependency 'AWSSES', '2.6.12'
+ ses.dependency 'AWSSES', '2.6.13'
end
s.subspec 'AWSSimpleDB' do |simpledb|
- simpledb.dependency 'AWSSimpleDB', '2.6.12'
+ simpledb.dependency 'AWSSimpleDB', '2.6.13'
end
s.subspec 'AWSSNS' do |sns|
- sns.dependency 'AWSSNS', '2.6.12'
+ sns.dependency 'AWSSNS', '2.6.13'
end
s.subspec 'AWSSQS' do |sqs|
- sqs.dependency 'AWSSQS', '2.6.12'
+ sqs.dependency 'AWSSQS', '2.6.13'
end
end
diff --git a/AWSiOSSDKv2.xcodeproj/project.pbxproj b/AWSiOSSDKv2.xcodeproj/project.pbxproj
index cd96f0ab0b6..4ff55ec1637 100644
--- a/AWSiOSSDKv2.xcodeproj/project.pbxproj
+++ b/AWSiOSSDKv2.xcodeproj/project.pbxproj
@@ -266,6 +266,8 @@
18F938D21DE5148E00034221 /* AWSLexVoiceButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 18F938C11DE5148E00034221 /* AWSLexVoiceButton.m */; };
18F938D41DE5193F00034221 /* AWSGeneralLexTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 18F938D31DE5193F00034221 /* AWSGeneralLexTests.m */; };
18F938D71DE520C500034221 /* AWSLexClientTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 18F938D61DE520C500034221 /* AWSLexClientTests.m */; };
+ 9A293CF0203885A300A12241 /* AWSS3TransferUtility+Validation.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A293CEE203885A300A12241 /* AWSS3TransferUtility+Validation.h */; };
+ 9A293CF1203885A300A12241 /* AWSS3TransferUtility+Validation.m in Sources */ = {isa = PBXBuildFile; fileRef = 9A293CEF203885A300A12241 /* AWSS3TransferUtility+Validation.m */; };
B52FBE921F17414A000F0C00 /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B52FBE911F17414A000F0C00 /* Media.xcassets */; };
CE0D41701C6A66E5006B91B5 /* AWSCore.h in Headers */ = {isa = PBXBuildFile; fileRef = CE0D416F1C6A66E5006B91B5 /* AWSCore.h */; settings = {ATTRIBUTES = (Public, ); }; };
CE0D417C1C6A66E5006B91B5 /* AWSCoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CE0D417B1C6A66E5006B91B5 /* AWSCoreTests.m */; };
@@ -2710,6 +2712,8 @@
18F938C11DE5148E00034221 /* AWSLexVoiceButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AWSLexVoiceButton.m; sourceTree = ""; };
18F938D31DE5193F00034221 /* AWSGeneralLexTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AWSGeneralLexTests.m; sourceTree = ""; };
18F938D61DE520C500034221 /* AWSLexClientTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AWSLexClientTests.m; sourceTree = ""; };
+ 9A293CEE203885A300A12241 /* AWSS3TransferUtility+Validation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AWSS3TransferUtility+Validation.h"; sourceTree = ""; };
+ 9A293CEF203885A300A12241 /* AWSS3TransferUtility+Validation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "AWSS3TransferUtility+Validation.m"; sourceTree = ""; };
B52FBE911F17414A000F0C00 /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = ""; };
CE0D416D1C6A66E5006B91B5 /* AWSCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AWSCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
CE0D416F1C6A66E5006B91B5 /* AWSCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AWSCore.h; sourceTree = ""; };
@@ -5571,6 +5575,8 @@
18CDFB261D66561F0021B1DE /* AWSS3Serializer.h */,
18CDFB271D66561F0021B1DE /* AWSS3Serializer.m */,
CE9DE9C11C6A7C2E0060793F /* Info.plist */,
+ 9A293CEE203885A300A12241 /* AWSS3TransferUtility+Validation.h */,
+ 9A293CEF203885A300A12241 /* AWSS3TransferUtility+Validation.m */,
);
path = AWSS3;
sourceTree = "";
@@ -6520,6 +6526,7 @@
buildActionMask = 2147483647;
files = (
CE9DE9E31C6A7C5E0060793F /* AWSS3PreSignedURL.h in Headers */,
+ 9A293CF0203885A300A12241 /* AWSS3TransferUtility+Validation.h in Headers */,
CE9DE9E91C6A7C5E0060793F /* AWSS3TransferManager.h in Headers */,
18CDFB281D66561F0021B1DE /* AWSS3Serializer.h in Headers */,
CE9DE9E11C6A7C5E0060793F /* AWSS3Model.h in Headers */,
@@ -9905,6 +9912,7 @@
18DF08E61D349137004C7D19 /* AWSS3RequestRetryHandler.m in Sources */,
CE9DE9E81C6A7C5E0060793F /* AWSS3Service.m in Sources */,
CE9DE9EC1C6A7C5E0060793F /* AWSS3TransferUtility.m in Sources */,
+ 9A293CF1203885A300A12241 /* AWSS3TransferUtility+Validation.m in Sources */,
18CDFB291D66561F0021B1DE /* AWSS3Serializer.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -13059,6 +13067,8 @@
isa = XCBuildConfiguration;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
+ CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
+ "CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES[arch=*]" = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -13078,6 +13088,7 @@
isa = XCBuildConfiguration;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
+ CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bdf421e41ff..5fb0bff6938 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# AWS Mobile SDK for iOS CHANGELOG
+## 2.6.13
+
+### New Features
+
+* **Amazon S3**
+ * Added support for MultiPart uploads in Transfer Utility
+ * Included error retry logic for Transfer Utility
+
## 2.6.12
### New Features
diff --git a/Scripts/GenerateAppleDocs.sh b/Scripts/GenerateAppleDocs.sh
index b4d868809ff..ee0414f0d5e 100644
--- a/Scripts/GenerateAppleDocs.sh
+++ b/Scripts/GenerateAppleDocs.sh
@@ -9,7 +9,7 @@ function cleanup
}
-VERSION="2.6.12"
+VERSION="2.6.13"
if [ -n $1 ] && [ "$1" == "clean" ];
then
cleanup