Uploading media

With Newzulu Platform SDK you can upload user-generated content and retrieve it into your dashboard in seconds.

The first thing you want to be sure before starting is that the user is authenticated with Newzulu Platform. Be sure you followed the Authentication documentation and that your user is authenticated before starting.

Preparing your upload

To upload a media (photo or video) into Newzulu Platform, you have to provide the SDK with 2 things:

  • A URL that points to your media.
  • The UploadInfo containing info about your upload.
let info = UploadInfo(title: "", description: "")
let upload = Upload(source: source, info: info)

The source URL may be:

  • a file URL (file: scheme),
  • a data URL (data: scheme),
  • an ALAsset URL from the AssetsLibrary framework (assets-library: scheme), or
  • constructed from a PHAsset local identifier using the URL.init(photosLocalIdentifier:) initializer in NewzuluCore.

For convenience, you may create an upload directly from a PHAsset object:

let upload = Upload(asset: asset, uploadInfo: uploadInfo)

Starting the upload

You are now ready to start your upload. Here's how to do it:

platformClient.startUpload(upload, onProgress: { uploadedBytes, totalBytes in
    // The progress changed.
}, onSuccess: {
    // The upload completed with success.
}, onError: { error, uploadState in
    // An error occurred.
})

Handling errors

If the upload fails, a NewzuluError is passed to the onError block, which contains information about what went wrong and the state of the current upload.

The reason

The error has a reason property that can be one of the following cases:

  • .networkFailure: A network error occurred and the SDK unable to reach the remote server.
  • .notAuthenticated: The client doesn't have credentials.
  • .wrongCredentials: The client used its credentials to authenticate prior to fetching the media but they're are not valid. See Authentication.
  • .serverFailure: The remote server encountered an error, you should try again later.
  • .backgroundTimeExpired: The application moved to the background and the upload needs to be suspended.
  • .invalidFile: The source supplied by the user cannot be read.
  • .invalidRepresentation: The response from the remote server cannot be understood.
  • .unknown: An unknown internal error occurred.

As NewzuluError.Reason is declared in the Newzulu Core framework, you need to import NewzuluCore in your source file when dealing with errors.

Retrying a failed upload

As the upload of a large file could take some times, it's likely to be interrupted if the device loses its network connection. You can retry a failed upload using the UploadState passed to onError.

platformClient.resumeUpload(with: uploadState, onProgress: { uploadedBytes, totalBytes in
    // The progress changed.
}, onSuccess: {
    // The upload completed with success.
}, onError: { error, uploadState in
    // An error occurred.
})

Note that this state is Representable so you can serialize it.

Background upload

Because the application is suspended when it moves to the background, an upload that takes too much time could fail. In order to allow long upload to persist in background, you need to use Background Transfer Services.

Most of the work is already implemented by the framework in the UploadManager class. An upload manager acts as an intermediary between one or several uploads and your application. You should create it once.

let uploadManager = UploadManager()

Then you can pass it to your uploads.

let info = UploadInfo(title: "", description: "")
let upload = Upload(source: source, info: info, manager: uploadManager)

In order for the upload manager to perform correctly, your application needs to do two things:

Handling Background Transfer events

First, your application must let the upload manager handle background transfer events. The application delegate must implement the application(:handleEventsForBackgroundURLSession:completionHandler:) method and invoke the handleEvents(forBackgroundURLSession:completion:) method on the upload manager when notified by the application that events related to a background URL session are waiting to be processed.

func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
    if uploadManager.handleEvents(forBackgroundURLSession: identifier, completion: completionHandler) {
        // Good
    } else {
        // The background session doesn't belong to the upload manager
    }
}

Persisting the uploads

Secondly, as the application may be terminated by the system while performing uploads in the background, you should save the state of the executing uploads so they can be resumed when the application is launched again. This is done by invoking the saveExecutingUploads(using:) method when the restorable state of the application is saved.

  • If your application implements the State Preservation Restoration feature, you may save the state of the uploads inside the archiver provided by UIKit when requested.
  • If your application does not implement the State Preservation Restoration feature, you should save the state of the upload persistently on the file system when the application enters the background and after the upload manager finishes handling events for a background URL session.

Here's how you may save the uploads:

func application(_ application: UIApplication, willEncodeRestorableStateWith coder: NSCoder) {
    uploadManager.saveExecutingUploads { (representation: Representation) in
        coder.encode(representation, forKey: "newzuluPlatformUploads")
    }
}

And here's how to resume them:

func application(_ application: UIApplication, didDecodeRestorableStateWith coder: NSCoder) {
    guard let representation = coder.decodeObject(forKey: "newzuluPlatformUploads") as? Representation else { return }
    do {
        let uploads = try uploadManager.restoreUploads(with: representation)
        for upload in uploads {
            platformClient.startUpload(upload, onProgress: { uploadedBytes, totalBytes in

            }, onSuccess: {

            }, onError: { error, uploadState in

            })
        }
    } catch let error as NewzuluError {
        // Decoding error
    } catch { }
}