Objective-C Signing SDK Integration Guide

Overview

The Objective-C signing SDK generates the X-Sdk-Date and Authorization headers required by Meitu Open Platform APIs and returns an NSURLRequest that can be sent directly with NSURLSession. It uses the SDK-HMAC-SHA256 algorithm to sign requests with an AK/SK pair.

Store the SK on the server and never embed it in a published iOS, macOS, or other client application.

Requirements

  • Apple Objective-C project
  • Foundation
  • CommonCrypto
  • NSURLSession
  • ARC

The SDK does not declare a minimum OS version. Use the deployment target and compiler settings required by your application.

Download and integration

SDK version: 1.0.0 Download the Objective-C SDK and example

The archive contains:

AIGCP-API-ObjectiveC-sdk-1.0.0/
├── MTSigner.h
├── MTSigner.m
└── MTDemo.m

Add MTSigner.h and MTSigner.m to the application target, then import the header:

#import "MTSigner.h"

API reference

Initialize the signer

- (instancetype)initWithKey:(NSString *)key
                      secret:(NSString *)secret;
ParameterDescription
keyAccess Key (AK)
secretSecret Key (SK)

Create a signed request

- (NSURLRequest *)signRequest:(NSURL *)url
                       method:(NSString *)method
                      headers:(NSDictionary<NSString *, NSString *> *)headers
                         body:(NSString *)body
                        error:(NSError **)error;
ParameterDescription
urlFinal request URL, including encoded and sorted query parameters
methodHTTP method, such as GET or POST
headersHeaders to sign; must include Host
bodyUTF-8 request body that will be sent; pass @"" for GET
errorError output parameter

The return value is an NSURLRequest containing the request method, headers, and body. Send this object directly; do not rebuild or modify the request after signing.

Header constants

ConstantHeader/value
kHeaderHostHost
kHeaderXDateX-Sdk-Date
kHeaderAuthorizationAuthorization
kHeaderContentSha256X-Sdk-Content-Sha256
kAlgorithmSDK-HMAC-SHA256

If X-Sdk-Date is not supplied, the SDK generates a UTC timestamp. The SDK adds Authorization after signing.

Complete POST example

The following example calls the production synchronous task endpoint at https://openapi.meitu.com/api/v1/sdk/sync/push. The request body contains five top-level fields: task, task_type, init_images, params passed as a JSON string, and sync_timeout. Replace the task, business parameters, image, and timeout values according to the target capability's API documentation.

#import <Foundation/Foundation.h>
#import "MTSigner.h"

static void SendSignedPOST(void) {
    NSDictionary *environment = [NSProcessInfo processInfo].environment;
    NSString *accessKey = environment[@"MEITU_OPENAPI_AK"];
    NSString *secretKey = environment[@"MEITU_OPENAPI_SK"];
    if (accessKey.length == 0 || secretKey.length == 0) {
        NSLog(@"Missing MEITU_OPENAPI_AK or MEITU_OPENAPI_SK");
        return;
    }

    NSURL *url = [NSURL URLWithString:
        @"https://openapi.meitu.com/api/v1/sdk/sync/push"];
    NSDictionary *payload = @{
        @"task": @"/v1/replace-with-product-task",
        @"task_type": @"formula",
        @"init_images": @[
            @{
                @"url": @"https://example.com/input.jpg",
                @"profile": @{
                    @"media_profiles": @{ @"media_data_type": @"url" },
                    @"version": @"v1"
                }
            }
        ],
        @"params": @"{\"parameter\":{\"rsp_media_type\":\"url\"}}",
        @"sync_timeout": @30
    };

    NSError *jsonError = nil;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:payload
                                                       options:0
                                                         error:&jsonError];
    if (jsonData == nil) {
        NSLog(@"Failed to serialize request JSON: %@", jsonError);
        return;
    }

    NSString *body = [[NSString alloc] initWithData:jsonData
                                           encoding:NSUTF8StringEncoding];
    NSDictionary<NSString *, NSString *> *headers = @{
        kHeaderHost: @"openapi.meitu.com",
        @"Content-Type": @"application/json; charset=UTF-8",
        @"Accept": @"application/json"
    };

    MTSigner *signer = [[MTSigner alloc] initWithKey:accessKey secret:secretKey];
    NSError *signError = nil;
    NSURLRequest *request = [signer signRequest:url
                                        method:@"POST"
                                       headers:headers
                                          body:body
                                         error:&signError];
    if (request == nil || signError != nil) {
        NSLog(@"Failed to sign request: %@", signError);
        return;
    }

    NSURLSessionDataTask *task =
        [[NSURLSession sharedSession] dataTaskWithRequest:request
                                       completionHandler:^(NSData *data,
                                                           NSURLResponse *response,
                                                           NSError *error) {
        if (error != nil) {
            NSLog(@"Request failed: %@", error);
            return;
        }
        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
        NSString *responseBody = [[NSString alloc] initWithData:data
                                                       encoding:NSUTF8StringEncoding];
        NSLog(@"HTTP %ld", (long)httpResponse.statusCode);
        NSLog(@"%@", responseBody);
    }];
    [task resume];
}

Set the environment variables before running the example, then replace task, params, the image information, and sync_timeout with values required by the target API. body is serialized once. The SDK signs the same string's UTF-8 bytes and uses them to create HTTPBody, after which the returned NSURLRequest is sent directly.

GET example

GET requests normally have no body. Percent-encode and sort all query parameters before signing, then place them in the final URL.

NSURL *url = [NSURL URLWithString:
    @"https://openapi.meitu.com/api/v1/sdk/status?task_id=replace-with-task-id"];
NSDictionary<NSString *, NSString *> *headers = @{
    kHeaderHost: @"openapi.meitu.com",
    @"Accept": @"application/json"
};

MTSigner *signer = [[MTSigner alloc] initWithKey:accessKey secret:secretKey];
NSError *signError = nil;
NSURLRequest *request = [signer signRequest:url
                                    method:@"GET"
                                   headers:headers
                                      body:@""
                                     error:&signError];

if (request != nil && signError == nil) {
    NSURLSessionDataTask *task =
        [[NSURLSession sharedSession] dataTaskWithRequest:request
                                       completionHandler:^(NSData *data,
                                                           NSURLResponse *response,
                                                           NSError *error) {
        if (error != nil) {
            NSLog(@"Request failed: %@", error);
            return;
        }
        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
        NSString *body = [[NSString alloc] initWithData:data
                                               encoding:NSUTF8StringEncoding];
        NSLog(@"HTTP %ld", (long)httpResponse.statusCode);
        NSLog(@"%@", body);
    }];
    [task resume];
}

Notes

  • Use HTTPS and ensure that Host matches the URL host. Use openapi.meitu.com for this domain.
  • method must match the request and use uppercase form, such as GET or POST.
  • Finalize the URL, query, signed headers, and body before signing. Do not modify them afterward.
  • Apply RFC 3986 percent encoding and protocol-defined sorting to query parameters before signing. The SDK does not sort the query string.
  • POST signing and sending must use identical UTF-8 body bytes. Send the NSURLRequest returned by the SDK directly.
  • Pass @"" as the GET body.
  • Keep the server clock accurate. The generated X-Sdk-Date uses UTC.
  • By default, the body's SHA-256 digest is signed. If the target API supports excluding the body digest, set this before signing:
headers[kHeaderContentSha256] = @"UNSIGNED-PAYLOAD";

Common errors

SymptomResolution
401, 403, or invalid signatureCheck the AK/SK, server time, Host, HTTP method, and whether the URL, headers, or body changed after signing
POST signature verification failsSerialize JSON once and send the request returned by the signer directly; verify Content-Type and the actual UTF-8 body
GET signature verification failsEncode and sort query parameters before signing, and pass @"" as the body
UNSIGNED-PAYLOAD has no effectUse kHeaderContentSha256, verify the exact value, and confirm that the target API supports it
Request times out or cannot connectCheck HTTPS, DNS, network proxy settings, and NSURLSession timeouts; inspect the completion-handler network error
API reports invalid business parametersCheck task, task_type, init_images, params, and sync_timeout against the target capability's API documentation