All Products
Search
Document Center

ApsaraVideo VOD:Upload files using the iOS SDK

Last Updated:Sep 04, 2026

The ApsaraVideo VOD iOS upload SDK enables your app to upload video and image files directly to VOD storage. Your app obtains upload credentials from a backend authorization service, and the SDK uses those credentials to upload files to OSS without routing media through your server.

How it works

The upload process involves three parties: your iOS app, your backend service, and ApsaraVideo VOD (which stores files in OSS). The workflow is as follows:

  1. Your app calls the SDK to start an upload. The SDK triggers the getAuth callback.

  2. In the callback, your app requests upload credentials from your backend service, which calls a VOD OpenAPI operation (such as CreateUploadVideo) and returns the response.

  3. Your app passes the OpenAPI response to the SDK. The SDK extracts UploadAuth and UploadAddress from the response and uploads the file directly to OSS.

  4. If credentials expire during a large upload, the SDK triggers the getAuth callback again with a refresh kind to obtain new credentials.

Prerequisites

ApsaraVideo VOD quick start

Item

Requirement

Minimum iOS version

iOS 12.0

Development language

Objective-C (Swift projects use a Bridging Header for bridging)

Alibaba Cloud account

ApsaraVideo VOD is activated

Authorization service

A backend service that can issue upload credentials (UploadAuth) is prepared

Integrate the SDK

Integration method

Way 1: Pod integration

Add the following content to your Podfile:

platform :ios, '12.0'
target 'YourApp' do
  pod 'VODUpload', '~> 2.0'
end

Run the following commands:

pod repo update
pod install

The SDK automatically pulls the underlying AliyunOSSiOS dependency.

Way 2: Swift Package Manager(SPM) integration

  1. Xcode menu File→Add Package Dependencies, enter the warehouse address: https://github.com/aliyunvideo/VODUpload.git.

  2. Select Up to Next Major Version for the version rule and fill in 2.0.1 for the initial version.

  3. Add product VODUpload to your App target.

Or declare it in Package.swift:

1.package(url: "https://github.com/aliyunvideo/VODUpload.git", from: "2.0.1")

The underlying OSS dependencies are automatically parsed along with the main package. You do not need to manually add them or configure them. After the integration is completed, the import VODUpload is sufficient. The API usage is the same as that of CocoaPods integration.

Project configuration

The podspec declares the following system libraries, which are automatically linked when you use CocoaPods for integration:

SystemConfiguration.framework
MobileCoreServices.framework
CoreMedia.framework
AVFoundation.framework
CoreTelephony.framework
libresolv.tbd

The SDK includes a PrivacyInfo.xcprivacy privacy manifest that is automatically injected to VODUpload.bundle/PrivacyInfo.xcprivacy during pod integration. No additional configuration is required.

Import the required header files:

#import <VODUpload/VODUploadV2Client.h>
#import <VODUpload/VODUploadConfig.h>
#import <VODUpload/VODAuthContext.h>
#import <VODUpload/VODGetAuthCallback.h>
#import <VODUpload/VODUploadOptions.h>
#import <VODUpload/VODUploadResult.h>
#import <VODUpload/VODUploadError.h>
#import <VODUpload/VODVideoMeta.h>
#import <VODUpload/VODImageMeta.h>
#import <VODUpload/VODUploadTask.h>

Upload workflow

Initialize an upload instance

VODUploadConfig uses properties for direct configuration. Call the +uploaderWithConfig: factory method to create an instance:

VODUploadConfig *config = [[VODUploadConfig alloc] init];
config.getAuth = getAuth;                  // Required: authorization callback. For more information, see "Handle authorization callbacks".

VODUploadV2Client *uploader = [VODUploadV2Client uploaderWithConfig:config];

The uploader instance must be retained as a property. If it is a local variable, ARC releases it, which causes callback loss. You can reuse the same instance to upload multiple files concurrently. Call [uploader dispose] to release resources when the lifecycle ends.

Upload files

Call uploadFile:options:callback: to start an upload. The method returns a VODUploadTask handle that you can use for cancellation:

VODVideoMeta *meta = [[VODVideoMeta alloc] init];
meta.title = @"My Video";
meta.tags = @"demo";
meta.cateId = @(1000);

VODUploadOptions *options = [[VODUploadOptions alloc] init];
options.videoMeta = meta;
options.onProgress = ^(float percent, int64_t uploaded, int64_t total) {
    NSLog(@"%.1f%%", percent * 100);
};

VODUploadTask *task = [uploader uploadFile:filePath
                                   options:options
                                  callback:^(VODUploadResult *r, VODUploadError *e) {
    if (e) {
        NSLog(@"%@: %@", e.errorCode, e.errorMessage);
        return;
    }
    NSLog(@"videoId=%@ uploadTaskId=%@ etag=%@ requestId=%@ durationMs=%lld",
          r.videoId, r.uploadTaskId, r.etag, r.requestId, r.durationMs);
}];

// Cancel during upload. Resumable upload is enabled by default. The next uploadFile call with the same file automatically resumes the upload.
[task cancel];
// task.uploadTaskId can be used to correlate logs and integrate with tracking.

Image upload: The SDK automatically uses the image upload process based on the file extension (jpg / jpeg / png / gif / bmp / webp / heic) or if options.imageMeta != nil is explicitly set. In the success callback, imageId and imageUrl have values:

VODImageMeta *imgMeta = [[VODImageMeta alloc] init];
imgMeta.imageType = @"cover";
imgMeta.title = @"Cover";

VODUploadOptions *opts = [[VODUploadOptions alloc] init];
opts.imageMeta = imgMeta;

[uploader uploadFile:imagePath options:opts callback:cb];
Note

The description field of VODVideoMeta is desc (not description, to avoid conflicts with the Objective-C reserved method).

Handle authorization callbacks

In v2, the VODGetAuthCallback block requests upload credentials from your application. The SDK calls getAuth at specific points and uses VODAuthContext.kind to indicate the required credential type:

Kind

Trigger

OpenAPI to call

VODAuthKindCreateVideo

First upload of a video file

CreateUploadVideo

VODAuthKindRefreshVideo

Resumable upload / credential refresh

RefreshUploadVideo

VODAuthKindCreateImage

Image upload

CreateUploadImage

Your application passes the raw JSON response from the backend OpenAPI call directly to completion. The SDK reads the following keys from the OpenAPI response by field name (case-sensitive):

Kind

Required fields

VODAuthKindCreateVideo / VODAuthKindRefreshVideo

UploadAuth, UploadAddress, VideoId

VODAuthKindCreateImage

UploadAuth, UploadAddress, ImageId, ImageURL (passed through to result.imageUrl)

Field names are case-sensitive. Pass through the OpenAPI response directly. Manually renaming fields to videoId, imageUrl, or image_url causes the SDK to parse them as nil.

VODGetAuthCallback getAuth = ^(VODAuthContext *ctx,
                               void (^completion)(NSDictionary *result, NSError *error)) {
    switch (ctx.kind) {
        case VODAuthKindCreateVideo:
            [YourBackend createUploadVideo:ctx.fileName
                                  fileSize:ctx.fileSize
                                completion:^(NSDictionary *json, NSError *err) {
                completion(json, err);
            }];
            break;
        case VODAuthKindRefreshVideo:
            [YourBackend refreshUploadVideo:ctx.videoId
                                 completion:^(NSDictionary *json, NSError *err) {
                completion(json, err);
            }];
            break;
        case VODAuthKindCreateImage:
            [YourBackend createUploadImage:ctx.fileName
                                completion:^(NSDictionary *json, NSError *err) {
                completion(json, err);
            }];
            break;
    }
};

Advanced settings

Upload acceleration

Set VODVideoMeta.userData to the following JSON. The VOD server automatically returns the OSS global transfer acceleration endpoint (oss-accelerate.aliyuncs.com) when issuing the UploadAddress. The SDK then directly uploads to the acceleration endpoint:

VODVideoMeta *meta = [[VODVideoMeta alloc] init];
meta.title = @"My Video";
meta.userData = @"{\"Type\":\"oss\",\"Domain\":\"oss-accelerate.aliyuncs.com\"}";

Upload acceleration relies on OSS global transfer acceleration. You must enable transfer acceleration for the bucket in the OSS console. For more information, see Access OSS using transfer acceleration.

Upload and transcode

Specify a transcoding template group or workflow by using VODVideoMeta:

VODVideoMeta *meta = [[VODVideoMeta alloc] init];
meta.title = @"My Video";
meta.desc = @"Video description";                     // Note: use desc, not description
meta.coverUrl = @"https://example.com/cover.jpg";
meta.cateId = @(1000);
meta.tags = @"tag1,tag2";
meta.storageLocation = @"<Optional: custom storage region>";
meta.templateGroupId = @"<Your template group ID>";    // Transcoding template group
meta.workflowId = @"<Your workflow ID>";          // Workflow (optional)
meta.appId = @"<Optional: application ID>";

Supported fields of VODVideoMeta: title, desc, coverUrl, cateId, tags, userData, storageLocation, templateGroupId, workflowId, and appId. All fields are optional and are passed through to CreateUploadVideo.

After the upload is complete, the VOD server transcodes the source video based on the template group. If you do not want to trigger transcoding, pass a "no transcoding" template group when calling CreateUploadVideo from your backend.

Timeout and retry

VODUploadConfig *config = [[VODUploadConfig alloc] init];
config.getAuth = getAuth;
config.timeout = 60;            // Unit: seconds. Default value: 60
config.maxRetryCount = 2;       // Default value: 2

timeout controls the connection and read/write timeout (NSTimeInterval, in seconds) for a single OSS request. maxRetryCount controls the number of retries for OSS requests upon network errors.

Important

The timeout unit is milliseconds on Android and seconds on iOS. Pay attention to this difference when integrating across platforms.

Signature version

config.signature = @"v4";        // Default value: @"v4". Valid values: @"v1"

OSS V4 signature is the recommended version. Starting from September 1, 2025, V4 signature is mandatory for newly created buckets. Existing customers are also recommended to migrate as soon as possible. Set @"v1" explicitly only when the bucket used by your application still supports only V1 signatures.

Resumable upload

Resumable upload is enabled by default. The SDK uses the (lastModified, fileName, fileSize) triplet as the resumable upload key and automatically persists it to the local Caches directory:

Scenario

SDK behavior

[task cancel] is called during upload

The resumable upload record is retained. The next uploadFile: call with the same file automatically resumes the upload.

App crashes or is terminated in the background

The resumable upload record is retained. The next cold start uploadFile: call with the same file automatically resumes the upload.

Any change in the file fingerprint

The file is treated as a new file and is uploaded from scratch.

OSS uploadId expires

The SDK automatically clears the old record and starts a fresh upload through VODAuthKindCreateVideo.

To disable resumable upload:

config.checkpoint = NO;

Multipart upload

Use partSize to control the part size and parallel to control the number of concurrent parts:

// Global configuration
config.partSize = 1024 * 1024;     // Default value: 1 MB
config.parallel = 4;               // Default value: 4

// Per-task override
VODUploadOptions *options = [[VODUploadOptions alloc] init];
options.videoMeta = meta;
options.partSize = 2 * 1024 * 1024;
options.parallel = 6;

Set the VOD service region

config.region = @"cn-shanghai";    // Default value: cn-shanghai

For the list of supported regions, see ApsaraVideo VOD region IDs.

Data reporting

The SDK enables upload link tracking by default for product quality monitoring. Only runtime metrics of the upload process are collected, and file content is not included. To disable data reporting:

config.reportEnabled = NO;       // Default value: YES

Error handling

VODUploadError inherits from NSError. Error codes use the UPLOAD.{LAYER}.{TYPE} three-segment format:

LAYER

Error code

Description

AUTH

UPLOAD.AUTH.GET_AUTH_FAILED

The getAuth callback failed.

AUTH

UPLOAD.AUTH.DECODE_FAILED

Failed to decode UploadAuth.

AUTH

UPLOAD.AUTH.EXPIRED

The credential has expired.

OSS

UPLOAD.OSS.ACCESS_DENIED

OSS access denied (permission/signature issue).

OSS

UPLOAD.OSS.NO_SUCH_BUCKET

The bucket does not exist.

OSS

UPLOAD.OSS.NO_SUCH_UPLOAD

The uploadId has expired. The SDK automatically falls back.

OSS

UPLOAD.OSS.UPLOAD_FAILED

OSS upload failed.

OSS

UPLOAD.OSS.MERGE_FAILED

OSS multipart merge failed.

NETWORK

UPLOAD.NETWORK.TIMEOUT

Network timeout.

NETWORK

UPLOAD.NETWORK.UNREACHABLE

Network unreachable.

FILE

UPLOAD.FILE.NOT_FOUND

The file does not exist.

FILE

UPLOAD.FILE.EMPTY

The file is empty.

CANCEL

UPLOAD.CANCEL.USER_CANCELLED

The user cancelled the upload.

INTERNAL

UPLOAD.INTERNAL.DISPOSED

The instance has been disposed.

INTERNAL

UPLOAD.INTERNAL.INVALID_CONFIG

The configuration is invalid.

Public properties:

  • NSString *errorCode — Error code

  • NSString *errorMessage — Error message

  • NSError *cause — Underlying OSS exception

  • NSString *uploadTaskId — Upload task ID

  • NSString *suggestion — Built-in fix suggestion from the SDK

if ([error.errorCode hasPrefix:@"UPLOAD.AUTH."]) {
    // Prompt the user to check the backend authorization
} else if ([error.errorCode isEqualToString:VODErrorCodeOssUploadFailed]) {
    // Retry or prompt the user to check the network
}
NSLog(@"%@ : %@ (cause=%@, suggestion=%@)",
      error.errorCode, error.errorMessage, error.cause, error.suggestion);

FAQ

Can I initiate multiple uploadFile calls simultaneously?

Yes. Each task is independent. Control the number of concurrent uploads based on network conditions to avoid excessive upstream bandwidth pressure caused by the combination with parallel.

Resumable upload does not work after cancellation and re-upload

Troubleshoot the issue by performing the following steps:

  • Check whether the file path is the same.

  • Check whether the lastModified or size of the file has changed.

  • Check whether config.checkpoint is set to YES.

  • Check whether the uploadId has expired. When the NoSuchUpload response is received, the SDK automatically falls back to a fresh upload. This is expected behavior.

App Store Connect review rejected with "missing privacy manifest"

The SDK includes a PrivacyInfo.xcprivacy file that is automatically injected during pod integration. Make sure that your app also declares the Required Reason API that your app uses.

Callbacks are not triggered after creating the uploader

Make sure that uploader is not a local variable. The VODUploadV2Client instance must be retained as a property of self. Otherwise, ARC releases it prematurely.