I have an app which is currently uploading images to amazon S3. I have been trying to switch it from using NSURLConnection to NSURLSession so that the uploads can continue w
The answers here are slightly outdated, spent a great deal of my day trying to get this work in Swift and the new AWS SDK. So here's how to do it in Swift by using the new AWSS3PreSignedURLBuilder
(available in version 2.0.7+):
class S3BackgroundUpload : NSObject {
// Swift doesn't support static properties yet, so have to use structs to achieve the same thing.
struct Static {
static var session : NSURLSession?
}
override init() {
super.init()
// Note: There are probably safer ways to store the AWS credentials.
let configPath = NSBundle.mainBundle().pathForResource("appconfig", ofType: "plist")
let config = NSDictionary(contentsOfFile: configPath!)
let accessKey = config.objectForKey("awsAccessKeyId") as String?
let secretKey = config.objectForKey("awsSecretAccessKey") as String?
let credentialsProvider = AWSStaticCredentialsProvider .credentialsWithAccessKey(accessKey!, secretKey: secretKey!)
// AWSRegionType.USEast1 is the default S3 endpoint (use it if you don't need specific endpoints such as s3-us-west-2.amazonaws.com)
let configuration = AWSServiceConfiguration(region: AWSRegionType.USEast1, credentialsProvider: credentialsProvider)
// This is setting the configuration for all AWS services, you can also pass in this configuration to the AWSS3PreSignedURLBuilder directly.
AWSServiceManager.defaultServiceManager().setDefaultServiceConfiguration(configuration)
if Static.session == nil {
let configIdentifier = "com.example.s3-background-upload"
var config : NSURLSessionConfiguration
if NSURLSessionConfiguration.respondsToSelector("backgroundSessionConfigurationWithIdentifier:") {
// iOS8
config = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier(configIdentifier)
} else {
// iOS7
config = NSURLSessionConfiguration.backgroundSessionConfiguration(configIdentifier)
}
// NSURLSession background sessions *need* to have a delegate.
Static.session = NSURLSession(configuration: config, delegate: self, delegateQueue: nil)
}
}
func upload() {
let s3path = "/some/path/some_file.jpg"
let filePath = "/var/etc/etc/some_file.jpg"
// Check if the file actually exists to prevent weird uncaught obj-c exceptions.
if NSFileManager.defaultManager().fileExistsAtPath(filePath) == false {
NSLog("file does not exist at %@", filePath)
return
}
// NSURLSession needs the filepath in a "file://" NSURL format.
let fileUrl = NSURL(string: "file://\(filePath)")
let preSignedReq = AWSS3GetPreSignedURLRequest()
preSignedReq.bucket = "bucket-name"
preSignedReq.key = s3path
preSignedReq.HTTPMethod = AWSHTTPMethod.PUT // required
preSignedReq.contentType = "image/jpeg" // required
preSignedReq.expires = NSDate(timeIntervalSinceNow: 60*60) // required
// The defaultS3PreSignedURLBuilder uses the global config, as specified in the init method.
let urlBuilder = AWSS3PreSignedURLBuilder.defaultS3PreSignedURLBuilder()
// The new AWS SDK uses BFTasks to chain requests together:
urlBuilder.getPreSignedURL(preSignedReq).continueWithBlock { (task) -> AnyObject! in
if task.error != nil {
NSLog("getPreSignedURL error: %@", task.error)
return nil
}
var preSignedUrl = task.result as NSURL
NSLog("preSignedUrl: %@", preSignedUrl)
var request = NSMutableURLRequest(URL: preSignedUrl)
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData
// Make sure the content-type and http method are the same as in preSignedReq
request.HTTPMethod = "PUT"
request.setValue(preSignedReq.contentType, forHTTPHeaderField: "Content-Type")
// NSURLSession background session does *not* support completionHandler, so don't set it.
let uploadTask = Static.session?.uploadTaskWithRequest(request, fromFile: fileUrl)
// Start the upload task:
uploadTask?.resume()
return nil
}
}
}
extension S3BackgroundUpload : NSURLSessionDelegate {
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
NSLog("did receive data: %@", NSString(data: data, encoding: NSUTF8StringEncoding))
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
NSLog("session did complete")
if error != nil {
NSLog("error: %@", error!.localizedDescription)
}
// Finish up your post-upload tasks.
}
}