Skip to content

Instantly share code, notes, and snippets.

@Skorch
Last active January 18, 2021 09:13
Show Gist options
  • Save Skorch/90ab38295b959c979363a35fe6ba8665 to your computer and use it in GitHub Desktop.
Save Skorch/90ab38295b959c979363a35fe6ba8665 to your computer and use it in GitHub Desktop.
Uploading from iOS to S3 as a background task. Pro Tip: You can embed REST parameters in the header, and have the server execute a Lambda on upload.
//:
import Foundation
import AWSS3
//A sample struct to define the parameters to encode and send to the
//server to be executed along with your upload
struct FileUploadParameters{
//label for the encoded name
private static let EncodeParam1 = "param_1"
private static let EncodeParam2 = "param_2"
let param1: String
let param2: String
//Perform a simple serialization of the parameters
func encode() -> [String: AnyObject]{
return [
FileUploadParameters.EncodeParam1: param1,
FileUploadParameters.EncodeParam2: param2
]
}
}
//Handles the creation of the AWS S3 upload with the encoding of the Lambda request embedded
private func uploadFile(uploadParameters: FileUploadParameters, localFileURL: NSURL, remoteBucketName: String, remoteFileName: String, fileMimeType: String) -> AWSTask?{
do{
//serialize the parameters to be included in the upload payload - will be executed on the server after upload is complete
let jsonParameters = try NSJSONSerialization.dataWithJSONObject(uploadParameters.encode(), options: NSJSONWritingOptions(rawValue: 0))
//Need to encode string so it fits properly into the header
//Ensure UTF8 string enoding
//convert to URL percent encoding
guard
let jsonParametersToken = NSString(data: jsonParameters, encoding: NSUTF8StringEncoding),
let encodedParameters = jsonParametersToken.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLHostAllowedCharacterSet())
else{
LogWarn("Burst parameters could not be serialized")
return nil
}
//Store the encoded string into the custom metadata
//All custom metadata values begin with x-amz-meta-
//Could alternatively create one string-encoded parameter per value, instead of JSON serialization
let customMeta = [
"x-amz-meta-uploadParameters": encodedParameters,
]
//AWS Transfer Utility manages the NSURLSession and background data task for you
let transferUtility = AWSS3TransferUtility.defaultS3TransferUtility()
//In order to customize the header information, we use the AWSS3TransferUtilityUploadExpression class
let expression = AWSS3TransferUtilityUploadExpression()
//We want our file to be publicly available by default
expression.setValue("public-read", forRequestParameter: "x-amz-acl")
//Copy the custom Meta information into the expression
for (key, metaValue) in customMeta{
expression.setValue(metaValue, forRequestParameter: key)
}
//build and return the AWSTask
return transferUtility.uploadFile(localFileURL, bucket: remotBucketName, key: remoteFileName, contentType: fileMimeType, expression: expression, completionHander: { [remoteFileName, localFileURL] (task, error) -> Void in
if let e = error{
print("upload of file \(remoteFileName) failed.\n\(e)")
return
}
//Clean up {localFileURL}?
})
} catch let e{
print("could not encode upload parameters \(e)")
}
return nil
}
@Skorch
Copy link
Author

Skorch commented Aug 8, 2016

The goal is to have your app upload a file to S3 without needing the app to be active. Typically one would perform a subsequent API call after the file has been successfully uploaded. If your app is in the background, this won't happen until the app is activated.

By using Amazon's AWSS3TransferUtility, their framework deals with the whole NSURLSession lifecycle and management.
By using a technique where we're associating custom upload headers with a custom JSON payload, the exact information needed to process the subsequent API call is embedded in the upload itself.

On the server, we create a Lambda event on the S3 container. Now when the upload completes, a Lambda is executed, where we can now fetch the header information, parse the JSON payload, then perform the API call needed.

The end result is a background upload task that can complete while the app is in the background, and the API call is still executed once the upload is complete - fire and forget!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment