/// <summary>
    /// Refreshes the currently logged in user's profile
    /// </summary>
    /// <param name="OnCompleted">Function invoked with the refreshed user's profile</param>
    /// <param name="OnError">Function invoked with the provoking exception when something goes wrong</param>
    public IEnumerator RefreshUserProfile(Action <SqUser> OnCompleted, Action <Exception> OnError)
    {
        SqUser    user = null;
        Exception ex   = null;

        yield return(GetUserProfile((u) => user = u, e => ex = e));

        if (ex != null)
        {
            OnError(ex);
            yield break;
        }

        if (user?.UserId != Data.Token?.UserId)
        {
            OnError?.Invoke(new SqApiException("User refreshed data does not match user token ID!"));
            yield break;
        }
        Data.User = user;
        SaveData();
        if (Data?.Token?.GrantedScopes?.Contains(SqAuthScopes.ReadAppAchievements) ?? false)
        {
            yield return(RefreshUserAchievements(c => { }, e => ex = e));

            if (ex != null)
            {
                OnError?.Invoke(new SqApiException("Unable to refresh achievements", ex));
                yield break;
            }
        }
        OnCompleted?.Invoke(user);
    }
    private IEnumerator Poller(int delaySec)
    {
        //this coroutine loops until the short code login request either fails or succeeds, waiting delaySec between checks
        while (true)
        {
            yield return(new WaitForSecondsRealtime(delaySec));

            SqUser    user   = null;
            bool      isDone = false;
            Exception ex     = null;

            //Call to check if the short code has been completed
            yield return(sq.CheckLoginCodeComplete((done, usr) =>
            {
                //The function is invoked with two parameters:
                // the first (done) is a boolean indicating if the short code request has been completed by the user
                // the second (usr) is the user profile object, and will be null until (done) is true
                isDone = done;
                user = usr;
            }, (e) =>
            {
                ex = e;
            }));

            if (ex != null)
            {
                //failures mean the call failed, timed out or something else went wrong.
                //when this happens, stop polling because the situation won't improve.
                Debug.LogError("Exception while checking for login code completion");
                Debug.LogException(ex);
                StatusText.text = $"Failed: {ex.Message}";
                LoggedOutVisibleContainer.SetActive(true);
                StopPolling();
                yield break;
            }
            if (isDone)
            {
                Debug.Log("Login with short code has completed");
                //if the user logged in with the short code, stop the polling coroutine and continue on
                LoginCompleted();
                StopPolling();
                yield break;
            }
            else
            {
                Debug.Log($"Login with short code is not yet complete.  Will check again in {delaySec} seconds");
            }
        }
    }