Skip to content

Instantly share code, notes, and snippets.

@bu-tokumitsu
Last active June 2, 2021 09:17
Show Gist options
  • Save bu-tokumitsu/751b7b6054552b7271462872f36e9082 to your computer and use it in GitHub Desktop.
Save bu-tokumitsu/751b7b6054552b7271462872f36e9082 to your computer and use it in GitHub Desktop.
using UnityEngine;
using System;
using System.Security.Cryptography;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Text;
// Firebase
using Firebase;
using Firebase.Extensions;
using Firebase.Auth;
using Firebase.Firestore;
// Sign In with Appleで使用(iOS)
using AppleAuth;
using AppleAuth.Enums;
using AppleAuth.Interfaces;
using AppleAuth.Extensions;
using AppleAuth.Native;
#if UNITY_IPHONE
// GameCenter認証で使用(iOS)
using UnityEngine.SocialPlatforms.GameCenter;
#endif
// Google認証
using Google;
// Playgames認証で使用(Android)
using GooglePlayGames;
using GooglePlayGames.BasicApi;
using UnityEngine.SocialPlatforms;
public class FirebaseAuthDebugUI : MonoBehaviour {
// Firebaseの認証を行う
private Firebase.Auth.FirebaseAuth auth;
// 認証後のユーザー情報
private Dictionary<string, Firebase.Auth.FirebaseUser> userByAuth = new Dictionary<string, Firebase.Auth.FirebaseUser>();
// Android向け 最新のGooglePlay開発者サービスアプリがインストールされているかチェックに使用する
private Firebase.DependencyStatus dependencyStatus = Firebase.DependencyStatus.UnavailableOther;
// Sign In with Appleで使用する変数
private const string AppleUserIdKey = "AppleUserId";
private IAppleAuthManager appleAuthManager;
// Google認証で使用するクライアントID(プロジェクト毎に取得する)
private const string GoogleClientId = "{Web Client IDを設定}";
// トークン取得判定のフラグ
private bool fetchingToken = false;
// UI表示に使用する
public GUISkin fb_GUISkin;
private string logText = "";
private string displayName = "";
private string sendMessage = "";
private Vector2 controlsScrollViewVector = Vector2.zero;
private Vector2 scrollViewVector = Vector2.zero;
bool UIEnabled = true;
// ログ表示の最大サイズ
const int kMaxLogSize = 16382;
// ゲスト認証完了フラグ
private bool signedInGuest = false;
// ゲスト以外の認証完了フラグ
private bool signedInUser = false;
// Overridable function to allow additional controls to be added.
protected virtual void GUIDisplayCustomControls() { }
protected Task previousTask;
protected CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
public virtual void Start() {
// GooglePlay開発者サービスアプリがインストールされているかチェックしてInitializeFirebase()を呼ぶ
Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task => {
dependencyStatus = task.Result;
if(dependencyStatus == Firebase.DependencyStatus.Available)
{
InitializeFirebase();
}
else
{
Debug.LogError("Could not resolve all Firebase dependencies: " + dependencyStatus);
}
});
StartWithSIWA();
StartWithSignInGoogle();
StartWithPlayGames();
}
protected virtual void Update() {
if (Input.GetKeyDown(KeyCode.Escape))
{
Application.Quit();
}
UpdateWithSIWA();
}
void OnDestroy() {
// Firebase認証の状態監視を削除
if (auth != null)
{
auth.StateChanged -= AuthStateChanged;
auth.IdTokenChanged -= IdTokenChanged;
auth = null;
}
}
void DisableUI() {
UIEnabled = false;
}
void EnableUI() {
UIEnabled = true;
}
// UI下部に表示するログ情報
public void DebugLog(string s) {
Debug.Log(s);
logText += s + "\n";
while (logText.Length > kMaxLogSize) {
int index = logText.IndexOf("\n");
logText = logText.Substring(index + 1);
}
scrollViewVector.y = int.MaxValue;
}
// 認証ユーザー情報のログ表示
protected void DisplayUserInfo(Firebase.Auth.IUserInfo userInfo, int indentLevel) {
string indent = new String(' ', indentLevel * 2);
var userProperties = new Dictionary<string, string> {
{ "Display Name", userInfo.DisplayName },
{ "Email", userInfo.Email} ,
{ "Photo URL", userInfo.PhotoUrl != null ? userInfo.PhotoUrl.ToString() : null },
{ "Provider ID", userInfo.ProviderId },
{ "User ID", userInfo.UserId }
};
foreach (var property in userProperties) {
if (!String.IsNullOrEmpty(property.Value))
{
DebugLog(String.Format("{0}{1}: {2}", indent, property.Key, property.Value));
}
}
}
// 認証情報のログ表示
protected void DisplayDetailedUserInfo(Firebase.Auth.FirebaseUser user, int indentLevel) {
string indent = new String(' ', indentLevel * 2);
DisplayUserInfo(user, indentLevel);
DebugLog(String.Format("{0}Anonymous: {1}", indent, user.IsAnonymous));
DebugLog(String.Format("{0}Email Verified: {1}", indent, user.IsEmailVerified));
DebugLog(String.Format("{0}Phone Number: {1}", indent, user.PhoneNumber));
var providerDataList = new List<Firebase.Auth.IUserInfo>(user.ProviderData);
var numberOfProviders = providerDataList.Count;
if (numberOfProviders > 0)
{
for (int i = 0; i < numberOfProviders; ++i) {
DebugLog(String.Format("{0}Provider Data: {1}", indent, i));
DisplayUserInfo(providerDataList[i], indentLevel + 2);
}
}
}
// 実行Taskのログ出し
protected bool LogTaskCompletion(Task task, string operation) {
bool complete = false;
if (task.IsCanceled)
{
DebugLog(operation + " canceled.");
}
else if (task.IsFaulted)
{
DebugLog(operation + " encounted an error.");
foreach (Exception exception in task.Exception.Flatten().InnerExceptions) {
string authErrorCode = "";
Firebase.FirebaseException firebaseEx = exception as Firebase.FirebaseException;
if (firebaseEx != null)
{
authErrorCode = String.Format("AuthError.{0}: ", ((Firebase.Auth.AuthError)firebaseEx.ErrorCode).ToString());
}
DebugLog(authErrorCode + exception.ToString());
}
}
else if (task.IsCompleted)
{
DebugLog(operation + " completed");
complete = true;
}
return complete;
}
// GUIのレンダリング
void OnGUI() {
GUI.skin = fb_GUISkin;
if (dependencyStatus != Firebase.DependencyStatus.Available)
{
GUILayout.Label("One or more Firebase dependencies are not present.");
GUILayout.Label("Current dependency status: " + dependencyStatus.ToString());
return;
}
Rect logArea, controlArea;
// 画面表示が縦・横の時の割合表示
if (Screen.width < Screen.height)
{
// Portrait mode
controlArea = new Rect(0.0f, 0.0f, Screen.width, Screen.height * 0.7f);
logArea = new Rect(0.0f, Screen.height * 0.7f, Screen.width, Screen.height * 0.3f);
}
else
{
// Landscape mode
controlArea = new Rect(0.0f, 0.0f, Screen.width * 0.5f, Screen.height);
logArea = new Rect(Screen.width * 0.5f, 0.0f, Screen.width * 0.5f, Screen.height);
}
GUILayout.BeginArea(logArea);
GUIDisplayLog();
GUILayout.EndArea();
GUILayout.BeginArea(controlArea);
GUIDisplayControls();
GUILayout.EndArea();
}
private class ScopedGuiEnabledModifier : IDisposable {
private bool wasEnabled;
public ScopedGuiEnabledModifier(bool newValue) {
wasEnabled = GUI.enabled;
GUI.enabled = newValue;
}
public void Dispose() {
GUI.enabled = wasEnabled;
}
}
// 上部のUI表示(認証ボタンの表示など)
void GUIDisplayControls() {
if (UIEnabled) {
controlsScrollViewVector = GUILayout.BeginScrollView(controlsScrollViewVector);
GUILayout.BeginVertical();
GUIStyle titleLabelStyle = new GUIStyle();
titleLabelStyle.fontSize = 96;
titleLabelStyle.normal.textColor = Color.white;
GUIStyle btnLabelStyle = GUI.skin.GetStyle("Button");
btnLabelStyle.fontSize = 54;
btnLabelStyle.normal.textColor = Color.white;
GUIStyle textFieldStyle = GUI.skin.GetStyle("TextField");
textFieldStyle.fontSize = 54;
textFieldStyle.normal.textColor = Color.white;
GUILayout.Space(162);
GUILayout.BeginHorizontal();
{
GUILayout.FlexibleSpace();
GUILayout.Label("Firebase Auth", titleLabelStyle);
GUILayout.FlexibleSpace();
}
GUILayout.EndHorizontal();
GUILayout.Space(96);
GUILayout.BeginHorizontal();
{
GUILayout.FlexibleSpace();
using (new ScopedGuiEnabledModifier(!signedInGuest && !signedInUser))
{
if (GUILayout.Button("Guest", btnLabelStyle, GUILayout.Width(Screen.width * 0.7f), GUILayout.Height(126)))
{
SigninAnonymouslyAsync();
}
}
GUILayout.FlexibleSpace();
}
GUILayout.EndHorizontal();
GUILayout.Space(48);
#if UNITY_IPHONE
GUILayout.BeginHorizontal();
{
GUILayout.FlexibleSpace();
using (new ScopedGuiEnabledModifier(!signedInUser))
{
if (GUILayout.Button("Sign In with Apple", btnLabelStyle, GUILayout.Width(Screen.width * 0.7f), GUILayout.Height(126)))
{
SignInWithApple();
}
}
GUILayout.FlexibleSpace();
}
GUILayout.EndHorizontal();
GUILayout.Space(48);
GUILayout.BeginHorizontal();
{
GUILayout.FlexibleSpace();
using (new ScopedGuiEnabledModifier(!signedInUser))
{
if (GUILayout.Button("GameCenter", btnLabelStyle, GUILayout.Width(Screen.width * 0.7f), GUILayout.Height(126)))
{
SignInGameCenter();
}
}
GUILayout.FlexibleSpace();
}
GUILayout.EndHorizontal();
GUILayout.Space(48);
#elif UNITY_ANDROID
GUILayout.BeginHorizontal();
{
GUILayout.FlexibleSpace();
using (new ScopedGuiEnabledModifier(!signedInUser))
{
if (GUILayout.Button("Sign in Google", btnLabelStyle, GUILayout.Width(Screen.width * 0.7f), GUILayout.Height(126)))
{
SignInGoogle();
}
}
GUILayout.FlexibleSpace();
}
GUILayout.EndHorizontal();
GUILayout.Space(48);
GUILayout.BeginHorizontal();
{
GUILayout.FlexibleSpace();
using (new ScopedGuiEnabledModifier(!signedInUser))
{
if (GUILayout.Button("Play game", btnLabelStyle, GUILayout.Width(Screen.width * 0.7f), GUILayout.Height(126)))
{
SignInPlayGames();
}
}
GUILayout.FlexibleSpace();
}
GUILayout.EndHorizontal();
GUILayout.Space(48);
#endif
GUILayout.BeginHorizontal();
{
GUILayout.FlexibleSpace();
// using (new ScopedGuiEnabledModifier(signedInUser))
// {
if (GUILayout.Button("Read FireStore message", btnLabelStyle, GUILayout.Width(Screen.width * 0.7f), GUILayout.Height(126)))
{
StartCoroutine(ReadDoc(GetCollectionReference()));
}
// }
GUILayout.FlexibleSpace();
}
GUILayout.EndHorizontal();
GUILayout.Space(48);
GUILayout.BeginHorizontal();
{
GUILayout.FlexibleSpace();
sendMessage = GUILayout.TextField(sendMessage, textFieldStyle, GUILayout.Width(Screen.width * 0.9f), GUILayout.Height(126));
GUILayout.FlexibleSpace();
}
GUILayout.EndHorizontal();
GUILayout.Space(32);
GUILayout.BeginHorizontal();
{
GUILayout.FlexibleSpace();
// using (new ScopedGuiEnabledModifier(signedInUser))
// {
if (GUILayout.Button("Send FireStore message", btnLabelStyle, GUILayout.Width(Screen.width * 0.7f), GUILayout.Height(126)))
{
FirestoreSendTest();
}
// }
GUILayout.FlexibleSpace();
}
GUILayout.EndHorizontal();
GUIDisplayCustomControls();
GUILayout.EndVertical();
GUILayout.EndScrollView();
}
}
// 画面下部のUI表示(ログ表示)
void GUIDisplayLog() {
GUIStyle logLabelStyle = new GUIStyle();
logLabelStyle.fontSize = 36;
logLabelStyle.normal.textColor = Color.white;
scrollViewVector = GUILayout.BeginScrollView(scrollViewVector);
GUILayout.Label(logText, logLabelStyle);
GUILayout.EndScrollView();
}
/** Firebase Authの必要処理 **/
// Firebase認証の初期化処理
protected void InitializeFirebase() {
DebugLog("Setting up Firebase Auth");
auth = Firebase.Auth.FirebaseAuth.DefaultInstance;
// Firebase認証の状態監視を追加
auth.StateChanged += AuthStateChanged;
auth.IdTokenChanged += IdTokenChanged;
AuthStateChanged(this, null);
}
// 認証状態が変更された時に実行される
void AuthStateChanged(object sender, System.EventArgs eventArgs) {
Firebase.Auth.FirebaseAuth senderAuth = sender as Firebase.Auth.FirebaseAuth;
Firebase.Auth.FirebaseUser user = null;
if (senderAuth != null) userByAuth.TryGetValue(senderAuth.App.Name, out user);
if (senderAuth == auth && senderAuth.CurrentUser != user)
{
bool signedIn = user != senderAuth.CurrentUser && senderAuth.CurrentUser != null;
if (!signedIn && user != null)
{
DebugLog("Signed out " + user.UserId);
}
user = senderAuth.CurrentUser;
userByAuth[senderAuth.App.Name] = user;
if (signedIn)
{
signedInGuest = user.IsAnonymous;
signedInUser = !user.IsAnonymous;
DebugLog("AuthStateChanged Signed in " + user.UserId);
displayName = user.DisplayName ?? "";
DisplayDetailedUserInfo(user, 1);
}
}
}
// Track ID token changes.
void IdTokenChanged(object sender, System.EventArgs eventArgs) {
Firebase.Auth.FirebaseAuth senderAuth = sender as Firebase.Auth.FirebaseAuth;
if (senderAuth == auth && senderAuth.CurrentUser != null && !fetchingToken)
{
senderAuth.CurrentUser.TokenAsync(false).ContinueWithOnMainThread(
task => DebugLog(String.Format("Token[0:8] = {0}", task.Result.Substring(0, 8))));
}
}
// 匿名(ゲスト)ユーザー認証
public Task SigninAnonymouslyAsync() {
DebugLog("Attempting to sign anonymously...");
DisableUI();
return auth.SignInAnonymouslyAsync().ContinueWithOnMainThread(HandleSignInWithUser);
}
// サインアウト
protected void SignOut() {
DebugLog("Signing out.");
auth.SignOut();
}
/** Sign In with Appleに必要な実装 **/
// Start()で呼び出す初期化処理
private void StartWithSIWA() {
// Sign In with Apple認証がサポートされている端末なら認証用のインスタンスを初期化する
if (AppleAuthManager.IsCurrentPlatformSupported)
{
var deserializer = new PayloadDeserializer();
this.appleAuthManager = new AppleAuthManager(deserializer);
}
}
// Update()で呼び出す処理
private void UpdateWithSIWA() {
// SignInwithAppleを成功させる為にAppleAuthManagerのUpdate()をUpdate()で呼び続ける必要がある
if (this.appleAuthManager != null)
{
this.appleAuthManager.Update();
}
}
private static string GenerateRandomString(int length)
{
if (length <= 0)
{
throw new Exception("Expected nonce to have positive length");
}
const string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._";
var cryptographicallySecureRandomNumberGenerator = new RNGCryptoServiceProvider();
var result = string.Empty;
var remainingLength = length;
var randomNumberHolder = new byte[1];
while (remainingLength > 0)
{
var randomNumbers = new List<int>(16);
for (var randomNumberCount = 0; randomNumberCount < 16; randomNumberCount++)
{
cryptographicallySecureRandomNumberGenerator.GetBytes(randomNumberHolder);
randomNumbers.Add(randomNumberHolder[0]);
}
for (var randomNumberIndex = 0; randomNumberIndex < randomNumbers.Count; randomNumberIndex++)
{
if (remainingLength == 0)
{
break;
}
var randomNumber = randomNumbers[randomNumberIndex];
if (randomNumber < charset.Length)
{
result += charset[randomNumber];
remainingLength--;
}
}
}
return result;
}
private static string GenerateSHA256NonceFromRawNonce(string rawNonce)
{
var sha = new SHA256Managed();
var utf8RawNonce = Encoding.UTF8.GetBytes(rawNonce);
var hash = sha.ComputeHash(utf8RawNonce);
var result = string.Empty;
for (var i = 0; i < hash.Length; i++)
{
result += hash[i].ToString("x2");
}
return result;
}
// Sign In with Apple認証の実行
private void SignInWithApple() {
var rawNonce = GenerateRandomString(32);
var nonce = GenerateSHA256NonceFromRawNonce(rawNonce);
var loginArgs = new AppleAuthLoginArgs(LoginOptions.IncludeEmail | LoginOptions.IncludeFullName, nonce);
// iOSクライアントでSIWA認証を実行し、成功時のcredentialとNonceを使用してFirebaseAuthを実行する
this.appleAuthManager.LoginWithAppleId(loginArgs,
credential =>
{
var appleIdCredential = credential as IAppleIDCredential;
DebugLog(String.Format("appleIdCredential:{0} | rawNonce:{1}", appleIdCredential, rawNonce));
if (appleIdCredential != null)
{
DebugLog(String.Format("appleIdCredential:{0} | rawNonce:{1}", appleIdCredential, rawNonce));
var identityToken = Encoding.UTF8.GetString(appleIdCredential.IdentityToken);
var authorizationCode = Encoding.UTF8.GetString(appleIdCredential.AuthorizationCode);
Credential firebaseCredential = OAuthProvider.GetCredential("apple.com", identityToken, rawNonce, authorizationCode);
SignInOrLinkCredentialAsync(firebaseCredential);
}
},
error =>
{
// Something went wrong
var authorizationErrorCode = error.GetAuthorizationErrorCode();
DebugLog("SignInWithApple error:" + authorizationErrorCode);
}
);
}
// GameCenter認証
private void SignInGameCenter() {
Social.localUser.Authenticate(success => {
if (success)
{
var credentialTask = Firebase.Auth.GameCenterAuthProvider.GetCredentialAsync();
var continueTask = credentialTask.ContinueWithOnMainThread(task => {
if(!task.IsCompleted)
return;
if(task.Exception != null)
DebugLog("GC Credential Task - Exception: " + task.Exception.Message);
Credential credential = task.Result;
SignInOrLinkCredentialAsync(credential);
});
}
else
{
DebugLog("GameCenter authenticate error");
}
});
}
/** Google認証に必要な実装 **/
// Start()で呼び出す初期化処理
private void StartWithSignInGoogle() {
// Google SignInの設定
GoogleSignIn.Configuration = new GoogleSignInConfiguration {
RequestIdToken = true,
// Copy this value from the google-service.json file.
// oauth_client with type == 3
WebClientId = GoogleClientId
};
}
// Google認証
private void SignInGoogle() {
Task<GoogleSignInUser> signIn = GoogleSignIn.DefaultInstance.SignIn();
signIn.ContinueWith (task => {
if (task.IsCanceled) {
DebugLog("GoogleSignIn was canceled.");
} else if (task.IsFaulted) {
DebugLog("GoogleSignIn was error.");
} else {
Credential credential = Firebase.Auth.GoogleAuthProvider.GetCredential(((Task<GoogleSignInUser>)task).Result.IdToken, null);
SignInOrLinkCredentialAsync(credential);
}
});
}
/** Playgamesに必要な実装 **/
// Start()で呼び出す初期化処理
private void StartWithPlayGames() {
#if UNITY_ANDROID
// playgames設定、初期化
PlayGamesClientConfiguration config = new PlayGamesClientConfiguration.Builder()
.RequestServerAuthCode(false /* Don't force refresh */)
.Build();
PlayGamesPlatform.InitializeInstance(config);
PlayGamesPlatform.Activate();
#endif
}
// Playゲーム認証(iOSだとエラーになるのでAndroidでのみ呼び出す)
#if UNITY_ANDROID
private void SignInPlayGames() {
Social.localUser.Authenticate(success => {
if (success)
{
var authCode = PlayGamesPlatform.Instance.GetServerAuthCode();
Firebase.Auth.FirebaseAuth auth = Firebase.Auth.FirebaseAuth.DefaultInstance;
Credential credential = Firebase.Auth.PlayGamesAuthProvider.GetCredential(authCode);
SignInOrLinkCredentialAsync(credential);
}
else
{
DebugLog("Playgames authenticate error");
}
});
}
#endif
// ゲストユーザーで認証済みの時はリンクさせる、新規の場合はそのまま認証完了
private void SignInOrLinkCredentialAsync(Credential firebaseCredential) {
if(auth.CurrentUser != null && auth.CurrentUser.IsAnonymous)
{
auth.CurrentUser.LinkWithCredentialAsync(firebaseCredential).ContinueWith(task => {
if (task.IsCanceled)
{
DebugLog("LinkWithCredentialAsync was canceled.");
return;
}
if (task.IsFaulted)
{
DebugLog("LinkWithCredentialAsync encountered an error: " + task.Exception.Message);
return;
}
Firebase.Auth.FirebaseUser newUser = task.Result;
DebugLog(String.Format("Credentials successfully linked to Firebase user: {0} ({1})", newUser.DisplayName, newUser.UserId));
signedInGuest = newUser.IsAnonymous;
signedInUser = !newUser.IsAnonymous;
});
}
else
{
var task = auth.SignInWithCredentialAsync(firebaseCredential);
if(task.Exception != null)
DebugLog("GC Credential Task - Exception: " + task.Exception.Message);
task.ContinueWithOnMainThread(HandleSignInWithUser);
}
}
// 認証結果の取得
void HandleSignInWithUser(Task<Firebase.Auth.FirebaseUser> task) {
EnableUI();
if(task.IsCanceled)
{
DebugLog("Firebase auth was canceled");
}
else if(task.IsFaulted)
{
DebugLog("Firebase auth failed");
}
else if(LogTaskCompletion(task, "Sign-in"))
{
// FirebaseUserの取得
// Firebase.Auth.FirebaseUser newUser = task.Result;
DebugLog(String.Format("{0} signed in", task.Result.DisplayName));
}
}
// ここからFirestore
private string collectionPath = "send_test";
private string documentId = "";
protected FirebaseFirestore db {
get {
return FirebaseFirestore.DefaultInstance;
}
}
private CollectionReference GetCollectionReference() {
return db.Collection(collectionPath);
}
private DocumentReference GetDocumentReference() {
if (documentId == "") {
return GetCollectionReference().Document();
}
return GetCollectionReference().Document(documentId);
}
// 読み込み
private IEnumerator ReadDoc(Query query) {
Task<QuerySnapshot> getTask = query.GetSnapshotAsync();
yield return new WaitForTaskCompletion(this, getTask);
if (!(getTask.IsFaulted || getTask.IsCanceled))
{
QuerySnapshot allCitiesQuerySnapshot = getTask.Result;
foreach (DocumentSnapshot documentSnapshot in allCitiesQuerySnapshot.Documents)
{
Console.WriteLine("read document data for {0} document:", documentSnapshot.Id);
Dictionary<string, object> city = documentSnapshot.ToDictionary();
IDictionary<string, object> resultData = documentSnapshot.ToDictionary();
foreach (KeyValuePair<string, object> kv in resultData)
{
DebugLog(String.Format("read key = {0} : value = {1}", kv.Key, kv.Value));
}
}
}
}
// 書き込み
private IEnumerator WriteDoc(DocumentReference doc, IDictionary<string, object> data) {
Task setTask = doc.SetAsync(data);
yield return new WaitForTaskCompletion(this, setTask);
if (!(setTask.IsFaulted || setTask.IsCanceled))
{
foreach (KeyValuePair<string, object> kv in data)
{
DebugLog(String.Format("write key = {0} : value = {1}", kv.Key, kv.Value));
}
}
}
// テスト送信
private void FirestoreSendTest() {
var msg = "test";
if (sendMessage != "")
{
msg = sendMessage;
}
var data = new Dictionary<string, object>{
{"message", msg}
};
StartCoroutine(WriteDoc(GetDocumentReference(), data));
}
class WaitForTaskCompletion : CustomYieldInstruction {
Task task;
FirebaseAuthDebugUI debugUI;
public WaitForTaskCompletion(FirebaseAuthDebugUI debugUI, Task task) {
debugUI.previousTask = task;
this.debugUI = debugUI;
this.task = task;
}
public override bool keepWaiting {
get {
if (task.IsCompleted)
{
debugUI.cancellationTokenSource = new CancellationTokenSource();
if (task.IsFaulted)
{
Debug.Log("WaitForTaskCompletion exception:" + task.Exception.Message);
}
return false;
}
return true;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment