iOS Carica un'immagine o un video su S3 utilizzando AWS SDK

Esempio

Prima di iniziare con l'esempio, consiglierei di creare un Singleton con un membro della classe delegato in modo da poter ottenere un caso di utilizzo del caricamento di un file in background e consentire all'utente di continuare a utilizzare l'app mentre i file vengono caricati anche quando l'app è lo sfondo.

Iniziamo, innanzitutto, dovremmo creare un enum che rappresenti la configurazione S3:

enum S3Configuration : String
{
    case IDENTITY_POOL_ID   = "YourIdentityPoolId"
    case BUCKET_NAME        = "YourBucketName"
    case CALLBACK_KEY       = "YourCustomStringForCallBackWhenUploadingInTheBackground"
    case CONTENT_TYPE_IMAGE = "image/png"
    case CONTENT_TYPE_VIDEO = "video/mp4"
}

Ora, dovremmo impostare le credenziali quando la tua app didFinishLaunchingWithOptions per la prima volta, quindi dovremmo impostarle all'interno di AppDelegate nel metodo didFinishLaunchingWithOptions (fai attenzione che dovresti impostare la tua regione sul parametro regionType ):

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
{
  let credentialProvider = AWSCognitoCredentialsProvider(regionType: .EUWest1, identityPoolId: S3Configuration.IDENTITY_POOL_ID.rawValue)
  let configuration = AWSServiceConfiguration(region: .EUWest1, credentialsProvider: credentialProvider)
  AWSS3TransferUtility.registerS3TransferUtilityWithConfiguration(configuration, forKey: S3Configuration.CALLBACK_KEY.rawValue)
}

Poiché siamo già all'interno di AppDelegate, dovremmo implementare la richiamata in background gestita dall'SDK AWS:

func application(application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: () -> Void)
{
    //  Will print the identifer you have set at the enum: .CALLBACK_KEY
    print("Identifier: " + identifier)
    //  Stores the completion handler.
    AWSS3TransferUtility.interceptApplication(application,
                                              handleEventsForBackgroundURLSession: identifier,
                                              completionHandler: completionHandler)
}

Ora, quando l'utente sposta l'app sullo sfondo, il caricamento continuerà il caricamento effettivo.

Per caricare il file utilizzando l'SDK AWS dovremo scrivere il file sul dispositivo e fornire all'SDK il percorso effettivo. Per fare un esempio, immagina di avere un UIImage (potrebbe anche essere un video ...) e lo scriveremo in una cartella temporanea:

// Some image....
let image = UIImage()
let fileURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent(fileName)
let filePath = fileURL.path!
let imageData = UIImageJPEGRepresentation(image, 1.0)
imageData!.writeToFile(filePath, atomically: true)

FileURL e fileName verranno utilizzati per il caricamento effettivo in seguito.

Ci sono 2 chiusure che dovremo definire fornite dall'SDK di AWS,

  1. AWSS3TransferUtilityUploadCompletionHandlerBlock - Una chiusura che avvisa quando il caricamento è terminato (o meno)
  2. AWSS3TransferUtilityUploadProgressBlock - Una chiusura che notifica ogni byte inviato

Se si prevede di avere un Singleton, è necessario definire tali tipi come membri della classe. L'implementazione dovrebbe assomigliare a questa:

var completionHandler : AWSS3TransferUtilityUploadCompletionHandlerBlock? =
    { (task, error) -> Void in

        if ((error) != nil)
        {
          print("Upload failed")
        }
        else
        {
          print("File uploaded successfully")
        }
    }

var progressBlock : AWSS3TransferUtilityUploadProgressBlock? = 
    { [unowned self] (task, bytesSent:Int64, totalBytesSent:Int64,  totalBytesExpectedToSend:Int64) -> Void in

     let progressInPercentage = Float(Double(totalBytesSent) / Double(totalBytesExpectedToSend)) * 100
     print(progressInPercentage)
    }

NOTA: Se si utilizza un Singleton, è possibile che si desideri definire un delegato che lo segnalerà all'avanzamento o al termine del file. Se non si utilizza un Singleton, è possibile creare un metodo statico con i tipi rilevanti:

    static func uploadImageToS3(fileURL : NSURL,
                               fileName : String,
                progressFunctionUpdater : Float -> Void,
                            resultBlock : (NSError?) -> Void)
{
 //    Actual implementation .....
 //    ...
 //    ...
}
  1. progressFunctionUpdater : riporterà a una funzione in corso.
  2. resultBlock - Se si restituisce nil, il caricamento ha avuto esito positivo, si invia l'oggetto error

Signore e signori, il caricamento effettivo:

        let fileData = NSData(contentsOfFile: fileURL.relativePath!)

        let expression = AWSS3TransferUtilityUploadExpression()
        expression.uploadProgress = progressBlock
        
        let transferUtility = AWSS3TransferUtility.S3TransferUtilityForKey(S3Configuration.CALLBACK_KEY.rawValue)
        
        transferUtility?.uploadData(fileData!,
            bucket: S3Configuration.BUCKET_NAME.rawValue,
            key: fileName,
            contentType: S3Configuration.CONTENT_TYPE_IMAGE.rawData,
            expression: expression,
            completionHander: completionHandler).continueWithBlock
            { (task : AWSTask) -> AnyObject? in
                
                if let error = task.error
                {
                    print(error)
                }
                if let exception = task.exception
                {
                    print("Exception: " + exception.description)
                }
                if let uploadTask = task.result as? AWSS3TransferUtilityUploadTask
                {
                    print("Upload started...")
                }
                
                return nil
        }

Caricamento S3 felice :)