Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save drawcode/d40e0fcf2632ed11c2512388bbafc4dd to your computer and use it in GitHub Desktop.
Save drawcode/d40e0fcf2632ed11c2512388bbafc4dd to your computer and use it in GitHub Desktop.
Way to get the game-center verification signature with Unity.

Even tho Unity has implemented a game-center api (Social api) it doesn't have a way to generate a verification signature, which you need to use game-center as a authentication method.

Luckily the objective-c code required is pretty straight forward. (Apple documentation: generateIdentityVerificationSignature)

#import <Foundation/Foundation.h>
#import <GameKit/GameKit.h>

typedef void (*IdentityVerificationSignatureCallback)(const char * publicKeyUrl, const char * signature, int signatureLength, const char * salt, int saltLength, const uint64_t timestamp, const char * error);

extern void generateIdentityVerificationSignature(IdentityVerificationSignatureCallback callback) {
    
    GKLocalPlayer * localPlayer = [GKLocalPlayer localPlayer];

    NSLog(@"LocalPlayer: %@", localPlayer.playerID);
    [localPlayer generateIdentityVerificationSignatureWithCompletionHandler:^(NSURL * publicKeyUrl, NSData * signature, NSData * salt, uint64_t timestamp, NSError * error) {

        NSLog(@"Received 'generateIdentityVerificationSignature' callback, error: %@", error.description);

        // Create a pool for releasing the resources we create
        @autoreleasepool {
            
            // PublicKeyUrl
            const char * publicKeyUrlCharPointer = NULL;
            if (publicKeyUrl != NULL)
            {
                const NSString * publicKeyUrlString = [[NSString alloc] initWithString:[publicKeyUrl absoluteString]];
                publicKeyUrlCharPointer = [publicKeyUrlString UTF8String];
            }

            // Signature
            const char * signatureBytes = [signature bytes];
            int signatureLength = (int)[signature length];

            // Salt
            const char * saltBytes = [salt bytes];
            int saltLength = (int)[salt length];

            // Error
            const NSString * errorString = error.description;
            const char * errorStringPointer = [errorString UTF8String];

            // Callback
            callback(publicKeyUrlCharPointer, signatureBytes, signatureLength, saltBytes, saltLength, timestamp, errorStringPointer);
        }
    }];
}

Strings are marshalled automatically for us and for the byte[]'s we pass a pointer to the obj-c memory, that way we can copy the data into managed arrays.

// Add a using for: AOT
// Add a using for: System.Runtime.InteropServices

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void IdentityVerificationSignatureCallback(
    string publicKeyUrl, 
    IntPtr signaturePointer, int signatureLength,
    IntPtr saltPointer, int saltLength,
    ulong timestamp,
    string error);

[DllImport("__Internal")]
private static extern void generateIdentityVerificationSignature(
    [MarshalAs(UnmanagedType.FunctionPtr)]IdentityVerificationSignatureCallback callback);

// Note: This callback has to be static because Unity's il2Cpp doesn't support marshalling instance methods.
[MonoPInvokeCallback(typeof(IdentityVerificationSignatureCallback))]
private static void OnIdentityVerificationSignatureGenerated(
    string publicKeyUrl, 
    IntPtr signaturePointer, int signatureLength,
    IntPtr saltPointer, int saltLength,
    ulong timestamp,
    string error)
{
    // Create a managed array for the signature
    var signature = new byte[signatureLength];
    Marshal.Copy(signaturePointer, signature, 0, signatureLength);

    // Create a managed array for the salt
    var salt = new byte[saltLength];
    Marshal.Copy(saltPointer, salt, 0, saltLength);

    UnityEngine.Debug.Log($"publicKeyUrl: {publicKeyUrl}");
    UnityEngine.Debug.Log($"signature: {signature.Length}");
    UnityEngine.Debug.Log($"salt: {salt.Length}");
    UnityEngine.Debug.Log($"timestamp: {timestamp}");
    UnityEngine.Debug.Log($"error: {error}");
}

You can then call generateIdentityVerificationSignature and give it OnIdentityVerificationSignatureGenerated as a callback, you can then in the callback save the results to a static variable for example.

Note: this assumes that the user is already logged-in to game-center, luckily Unity has already implemented the api to login:

Social.localUser.Authenticate(success =>
{

});

For info on where to place the objective-c code Unity has a manual page: PluginsForIOS

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