Last active
April 18, 2022 07:38
-
-
Save gamefish/7c1341de94bbcbfb2bbb9bcc5a03e966 to your computer and use it in GitHub Desktop.
[Sign in with Apple Unity official example] Showcase how to sign in with Apple account in unity
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public SignInWithApple signInWithApple; | |
signInWithApple.Login((args) => | |
{ | |
if(!string.IsNullOrEmpty(args.error)) | |
{ | |
LogMessage("Apple -> sign in error:" + args.error); | |
return; | |
} | |
string idToken = args.userInfo.idToken; | |
LogMessage("Apple --> Identity Token:\n"+idToken); | |
// use idToken to login to PlayFab or any other web services | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Concurrent; | |
using System.Runtime.InteropServices; | |
using AOT; | |
using UnityEngine.Events; | |
namespace UnityEngine.SignInWithApple | |
{ | |
public enum UserDetectionStatus | |
{ | |
LikelyReal, | |
Unknown, | |
Unsupported | |
} | |
public enum UserCredentialState | |
{ | |
Revoked, | |
Authorized, | |
NotFound | |
} | |
public struct UserInfo | |
{ | |
public string userId; | |
public string email; | |
public string displayName; | |
public string idToken; | |
public string error; | |
public UserDetectionStatus userDetectionStatus; | |
} | |
[System.Serializable] | |
public class SignInWithAppleEvent : UnityEvent<SignInWithApple.CallbackArgs> | |
{ | |
} | |
public class SignInWithApple : MonoBehaviour | |
{ | |
private static Callback s_LoginCompletedCallback; | |
private static Callback s_CredentialStateCallback; | |
private static readonly ConcurrentQueue<Action> s_EventQueue = new ConcurrentQueue<Action>(); | |
public struct CallbackArgs | |
{ | |
/// <summary> | |
/// The state of the user's authorization. | |
/// </summary> | |
public UserCredentialState credentialState; | |
/// <summary> | |
/// The logged in user info after the call is done. | |
/// </summary> | |
public UserInfo userInfo; | |
/// <summary> | |
/// Whether the call ends up with an error. | |
/// </summary> | |
public string error; | |
} | |
public delegate void Callback(CallbackArgs args); | |
private delegate void LoginCompleted(int result, UserInfo info); | |
[MonoPInvokeCallback(typeof(LoginCompleted))] | |
private static void LoginCompletedCallback(int result, [MarshalAs(UnmanagedType.Struct)]UserInfo info) | |
{ | |
var args = new CallbackArgs(); | |
if (result != 0) | |
{ | |
args.userInfo = new UserInfo | |
{ | |
idToken = info.idToken, | |
displayName = info.displayName, | |
email = info.email, | |
userId = info.userId, | |
userDetectionStatus = info.userDetectionStatus | |
}; | |
} | |
else | |
{ | |
args.error = info.error; | |
} | |
s_LoginCompletedCallback(args); | |
s_LoginCompletedCallback = null; | |
} | |
private delegate void GetCredentialStateCompleted(UserCredentialState state); | |
[MonoPInvokeCallback(typeof(GetCredentialStateCompleted))] | |
private static void GetCredentialStateCallback([MarshalAs(UnmanagedType.SysInt)]UserCredentialState state) | |
{ | |
var args = new CallbackArgs | |
{ | |
credentialState = state | |
}; | |
s_CredentialStateCallback(args); | |
s_CredentialStateCallback = null; | |
} | |
#region events | |
[Header("Event fired when login is complete.")] | |
public SignInWithAppleEvent onLogin; | |
[Header("Event fired when the users credential state has been retrieved.")] | |
public SignInWithAppleEvent onCredentialState; | |
[Header("Event fired when there is an error.")] | |
public SignInWithAppleEvent onError; | |
#endregion | |
/// <summary> | |
/// Get credential state and trigger onCredentialState or onError event when action is completed. | |
/// </summary> | |
/// <param name="userID">The user id to query the credential state on.</param> | |
public void GetCredentialState(string userID) | |
{ | |
GetCredentialState(userID, TriggerCredentialStateEvent); | |
} | |
/// <summary> | |
/// Invoke login and provide a custom callback when action is completed. | |
/// When a custom trigger is used, the onCredentialState or onError unity event won't trigger. | |
/// </summary> | |
/// <param name="userID">The user id to query the credential state on.</param> | |
/// <param name="callback">The custom callback to trigger when action is completed.</param> | |
public void GetCredentialState(string userID, Callback callback) | |
{ | |
if (s_CredentialStateCallback != null) | |
throw new InvalidOperationException("Credential state fetch called while another request is in progress"); | |
s_CredentialStateCallback = callback; | |
GetCredentialStateInternal(userID); | |
} | |
private void GetCredentialStateInternal(string userID) | |
{ | |
#if (UNITY_IOS || UNITY_TVOS) && !UNITY_EDITOR | |
IntPtr cback = IntPtr.Zero; | |
GetCredentialStateCompleted d = GetCredentialStateCallback; | |
cback = Marshal.GetFunctionPointerForDelegate(d); | |
UnitySignInWithApple_GetCredentialState(userID, cback); | |
#endif | |
} | |
/// <summary> | |
/// Invoke login and trigger onLogin or onError event when login is completed. | |
/// </summary> | |
public void Login() | |
{ | |
Login(TriggerOnLoginEvent); | |
} | |
/// <summary> | |
/// Invoke login and provide a custom callback when login is completed. | |
/// When a custom trigger is used, the onLogin or onError unity event won't trigger. | |
/// </summary> | |
/// <param name="callback">The custom callback to trigger when login is completed.</param> | |
public void Login(Callback callback) | |
{ | |
Debug.Log("siwa Login"); | |
if (s_LoginCompletedCallback != null) | |
throw new InvalidOperationException("Login called while another login is in progress"); | |
s_LoginCompletedCallback = callback; | |
LoginInternal(); | |
} | |
private void LoginInternal() | |
{ | |
Debug.Log("siwa LoginInternal"); | |
#if (UNITY_IOS || UNITY_TVOS) && !UNITY_EDITOR | |
IntPtr cback = IntPtr.Zero; | |
LoginCompleted d = LoginCompletedCallback; | |
cback = Marshal.GetFunctionPointerForDelegate(d); | |
UnitySignInWithApple_Login(cback); | |
#endif | |
} | |
private void TriggerOnLoginEvent(CallbackArgs args) | |
{ | |
if (args.error != null) | |
{ | |
TriggerOnErrorEvent(args); | |
return; | |
} | |
s_EventQueue.Enqueue(delegate () | |
{ | |
if (onLogin != null) | |
{ | |
onLogin.Invoke(args); | |
} | |
}); | |
} | |
private void TriggerCredentialStateEvent(CallbackArgs args) | |
{ | |
if (args.error != null) | |
{ | |
TriggerOnErrorEvent(args); | |
return; | |
} | |
s_EventQueue.Enqueue(delegate () | |
{ | |
if (onCredentialState != null) | |
{ | |
onCredentialState.Invoke(args); | |
} | |
}); | |
} | |
private void TriggerOnErrorEvent(CallbackArgs args) | |
{ | |
s_EventQueue.Enqueue(delegate () | |
{ | |
if (onError != null) | |
{ | |
onError.Invoke(args); | |
} | |
}); | |
} | |
public void Update() | |
{ | |
Action action; | |
while (s_EventQueue.TryDequeue(out action)) | |
{ | |
action.Invoke(); | |
} | |
} | |
#region native hooks | |
#if (UNITY_IOS || UNITY_TVOS) && !UNITY_EDITOR | |
[DllImport("__Internal")] | |
private static extern void UnitySignInWithApple_Login(IntPtr callback); | |
[DllImport("__Internal")] | |
private static extern void UnitySignInWithApple_GetCredentialState(string userID, IntPtr callback); | |
#endif | |
#endregion | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <Foundation/Foundation.h> | |
#import <AuthenticationServices/AuthenticationServices.h> | |
#import "UnityAppController.h" | |
struct UserInfo | |
{ | |
const char * userId; | |
const char * email; | |
const char * displayName; | |
const char * idToken; | |
const char * error; | |
ASUserDetectionStatus userDetectionStatus; | |
}; | |
typedef void (*SignInWithAppleCallback)(int result, struct UserInfo s1); | |
API_AVAILABLE(ios(13.0), tvos(13.0)) | |
typedef void (*CredentialStateCallback)(ASAuthorizationAppleIDProviderCredentialState state); | |
API_AVAILABLE(ios(13.0), tvos(13.0)) | |
@interface UnitySignInWithApple : NSObject<ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding> | |
@property (nonatomic) SignInWithAppleCallback loginCallback; | |
@property (nonatomic) CredentialStateCallback credentialStateCallback; | |
@end | |
API_AVAILABLE(ios(13.0), tvos(13.0)) | |
static UnitySignInWithApple* _unitySignInWithAppleInstance; | |
@implementation UnitySignInWithApple | |
{ | |
ASAuthorizationAppleIDRequest* request; | |
} | |
+(UnitySignInWithApple*)instance | |
{ | |
if (_unitySignInWithAppleInstance == nil) | |
_unitySignInWithAppleInstance = [[UnitySignInWithApple alloc] init]; | |
return _unitySignInWithAppleInstance; | |
} | |
-(void)startRequest | |
{ | |
if (@available(iOS 13.0, tvOS 13.0, *)) { | |
ASAuthorizationAppleIDProvider* provider = [[ASAuthorizationAppleIDProvider alloc] init]; | |
request = [provider createRequest]; | |
[request setRequestedScopes: @[ASAuthorizationScopeEmail, ASAuthorizationScopeFullName]]; | |
ASAuthorizationController* controller = [[ASAuthorizationController alloc] initWithAuthorizationRequests:@[request]]; | |
controller.delegate = self; | |
controller.presentationContextProvider = self; | |
[controller performRequests]; | |
} else { | |
// Fallback on earlier versions | |
} | |
} | |
- (void)getCredentialState:(NSString *)userID | |
{ | |
ASAuthorizationAppleIDProvider* provider = [[ASAuthorizationAppleIDProvider alloc] init]; | |
[provider getCredentialStateForUserID:userID | |
completion:^(ASAuthorizationAppleIDProviderCredentialState credentialState, NSError * _Nullable error) { | |
self.credentialStateCallback(credentialState); | |
}]; | |
} | |
-(ASPresentationAnchor)presentationAnchorForAuthorizationController:(ASAuthorizationController *)controller | |
{ | |
return _UnityAppController.window; | |
} | |
-(void)authorizationController:(ASAuthorizationController *)controller didCompleteWithAuthorization:(ASAuthorization *)authorization | |
{ | |
if (self.loginCallback) | |
{ | |
struct UserInfo data; | |
if (@available(iOS 13.0, tvOS 13.0, *)) { | |
ASAuthorizationAppleIDCredential* credential = (ASAuthorizationAppleIDCredential*)authorization.credential; | |
NSString* idToken = [[NSString alloc] initWithData:credential.identityToken encoding:NSUTF8StringEncoding]; | |
NSPersonNameComponents* name = credential.fullName; | |
data.idToken = [idToken UTF8String]; | |
data.displayName = [[NSPersonNameComponentsFormatter localizedStringFromPersonNameComponents:name | |
style:NSPersonNameComponentsFormatterStyleDefault | |
options:0] UTF8String]; | |
data.email = [credential.email UTF8String]; | |
data.userId = [credential.user UTF8String]; | |
data.userDetectionStatus = credential.realUserStatus; | |
data.error = ""; | |
self.loginCallback(1, data); | |
} else { | |
// Fallback on earlier versions | |
} | |
} | |
} | |
-(void)authorizationController:(ASAuthorizationController *)controller didCompleteWithError:(NSError *)error | |
{ | |
if (self.loginCallback) | |
{ | |
// All members need to be set to a non-null value. | |
struct UserInfo data; | |
data.idToken = ""; | |
data.displayName = ""; | |
data.email = ""; | |
data.userId = ""; | |
data.userDetectionStatus = 1; | |
data.error = [error.localizedDescription UTF8String]; | |
self.loginCallback(0, data); | |
} | |
} | |
@end | |
void UnitySignInWithApple_Login(SignInWithAppleCallback callback) | |
{ | |
if (@available(iOS 13.0, tvOS 13.0, *)) { | |
UnitySignInWithApple* login = [UnitySignInWithApple instance]; | |
login.loginCallback = callback; | |
[login startRequest]; | |
} else { | |
// Fallback on earlier versions | |
} | |
} | |
API_AVAILABLE(ios(13.0), tvos(13.0)) | |
void UnitySignInWithApple_GetCredentialState(const char *userID, CredentialStateCallback callback) | |
{ | |
if (@available(iOS 13.0, tvOS 13.0, *)) { | |
UnitySignInWithApple* login = [UnitySignInWithApple instance]; | |
login.credentialStateCallback = callback; | |
[login getCredentialState: [NSString stringWithUTF8String: userID]]; | |
} else { | |
// Fallback on earlier versions | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment