Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS M1 Mac/Tablets Fails to Initialize #363

Closed
gasci opened this issue May 3, 2024 · 15 comments
Closed

iOS M1 Mac/Tablets Fails to Initialize #363

gasci opened this issue May 3, 2024 · 15 comments

Comments

@gasci
Copy link

gasci commented May 3, 2024

I am getting this error when trigger GoogleAuth.signOut()

CodetrixStudioCapacitorGoogleAuth/Plugin.swift:121: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value

@objc
    func signOut(_ call: CAPPluginCall) {
        DispatchQueue.main.async {
            self.googleSignIn.signOut();
        }
        call.resolve();
    }
  • capacitor version 6.0.0
  • plugin version 3.4.0 rc.4
@gasci gasci changed the title iOS M1 bug iOS M1 Mac GoogleAuth.signOut() nil value error May 3, 2024
@gasci
Copy link
Author

gasci commented May 18, 2024

Has there been any update regarding this issue? @fullstackduck

@gasci
Copy link
Author

gasci commented May 29, 2024

I believe this error prevents M1 and M2 processors to run this plugin.

@gasci gasci changed the title iOS M1 Mac GoogleAuth.signOut() nil value error iOS M1 Mac/Tablets Fails to Init and SignOut in some devices. Jun 12, 2024
@gasci gasci changed the title iOS M1 Mac/Tablets Fails to Init and SignOut in some devices. iOS M1 Mac/Tablets Fails to Initialize Jun 12, 2024
@raymac101
Copy link

I also get this error running Angular 18, Ionic 8 and Capacitor 6. The Google Auth worked great until I upgraded Angular and Ionic. Now I get the error:

CodetrixStudioCapacitorGoogleAuth/Plugin.swift:74: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value

Any updates on this?

@gasci
Copy link
Author

gasci commented Jun 24, 2024

Some tablets have this issue, too because run on M1 processors.

@DamianRycerz
Copy link

DamianRycerz commented Jun 26, 2024

Hey, is this issue resolved? Or any idea for that? My app is crashing randomly during signOut process.
Looks like is crashing if user was login by e-mail. IF user login by plugin it works fine.

Or maybe have you another ways to allow login on multiple account?
For now i use this.authClient.signOut() form authClient: AngularFireAuth = inject(AngularFireAuth) and logout works fine but user can't login on another account. Looks like sth was cached.

Angular 17
Capacitor 6
Codetrilx 3.4.0-rc.4

@personalrad
Copy link

Got the same error on line 74, CodetrixStudioCapacitorGoogleAuth/Plugin.swift:74: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value

Solved calling
GoogleAuth.initialize();

Previous versions, it was called only when in web. Now, I call when in iOS too.

@DamianRycerz
Copy link

Yes, thats true, in new version you have to call initialize() on mobile as well.
But about signOut(). it works for you?

@raymac101
Copy link

I finally found the issue to the "found nil" error.. I had to update the Cocoa Pod on iOS. In the Podfile make sure you're at AppAuth 1.7.5 and GoogleSignIn 6.2.4 and Firebase 10.28.1
Once I did that it seemed to start working.

@raymac101
Copy link

I had a similar issue with the sign out. I found that the GoogleAuth.signOut() wasn't enough, it still seemed to cache the user and kept automatically signing back it.
My fix was to do both:
GoogleAuth.signOut()
and
singOut(auth) from firebase
then also set my local user Subjects all to null (using rxjs).

Hope that helps

@DamianRycerz
Copy link

I used both but GoogleAuth.signOut() crashing and close app on mobile device. maybe race condition (???). could you share snippet of your code?

@raymac101
Copy link

raymac101 commented Jun 26, 2024

Hey, here is what I used for my authentication service: The service allows for Email, Apple or Google authentication, then creates or loads a profile for the user.

import {
  Auth,
  GoogleAuthProvider,
  signInWithPopup,
  createUserWithEmailAndPassword,
  sendPasswordResetEmail,
  signInWithEmailAndPassword,
  signOut,
  OAuthProvider,
  UserCredential,import { Injectable } from '@angular/core';
import {
  Auth,
  GoogleAuthProvider,
  signInWithPopup,
  createUserWithEmailAndPassword,
  sendPasswordResetEmail,
  signInWithEmailAndPassword,
  signOut,
  OAuthProvider,
  UserCredential,
  User,
  signInWithCredential
} from '@angular/fire/auth';
import { GoogleAuth } from '@codetrix-studio/capacitor-google-auth';
import { PhpFoxService } from './phpFox.service';
import { SignInWithApple, SignInWithAppleOptions, SignInWithAppleResponse } from '@capacitor-community/apple-sign-in'
import { ProfileService } from './profile.service';
import { SongBookService } from './songbook.service';
import { Preferences } from '@capacitor/preferences';
import { Capacitor } from '@capacitor/core';
import { isPlatform } from '@ionic/angular';
import { Subject, Subscription } from 'rxjs';
import { Profile } from '../../app/models/profile.model';


@Injectable({
  providedIn: 'root'
})
export class AuthService {

  private email: string = '';
  private password: string = '';
  private fullname: string = '';
  private stagename: string = '';
  private user: User | null = null;
  public signedInUser = new Subject<User>();
  public userProfile = new Subject<Profile>();
  private phpAccessToken: string = '';
  private subProfile: Subscription | null = null;


  constructor(
    private auth: Auth,
    private profileService: ProfileService,
    private songBookService: SongBookService,
    private phpFoxService: PhpFoxService
  ) {
    this.auth.onAuthStateChanged(user => {

      if (user === null) {
        this.user = null;
        this.email = '';
        this.password = '';
        this.fullname = '';
        this.stagename = '';
      } else if (user != null) {
        this.user = user;
        this.email = user.email;
        this.fullname = user.displayName;
        this.signedInUser.next(user);

        if (user.providerData[0].providerId === 'google.com') {
          this.password = 'Google101!';
          this.setLoginLocal(user.email, this.password);
        } else if (user.providerData[0].providerId === 'apple.com') {
          this.password = 'Apple101!';
          this.setLoginLocal(user.email, this.password);
        }
      }
    });

    GoogleAuth.initialize({});
  }

  public getUser() {
    return this.user;
  }

  public async getEmail() {
    if (!this.email) {
      await this.getLoginLocal().catch(err => {
        console.log('No email & password found');
      });
    }
    return this.email;
  }

  public getPassword() {
    return this.password;
  }

  public getFullname() {
    return this.fullname;
  }

  public getStagename() {
    return this.stagename;
  }

  async register(email, password, fullname, stagename, avatarURL, foxId, provider) {
    this.email = email;
    this.password = password;
    this.fullname = fullname;
    this.stagename = stagename;

    if(provider === 'email') {
      const user = await createUserWithEmailAndPassword(
        this.auth,
        email,
        password
      ).catch(error => {
        console.log('Register User Error = ' + error);
        console.log('Register User Error USER = ' + user);
        return null;
      });
    }
    this.checkForProfile(email, fullname, stagename, avatarURL, foxId);
    this.setLoginLocal(email, password);

  }

  async login ({ email, password }) {
    this.email = email;
    this.password = password;

    const user = await signInWithEmailAndPassword(
      this.auth,
      email,
      password
    );
    this.setLoginLocal(email, password);
    return user;
  }

  loginWithGoogle() {
    if (!isPlatform('capacitor')) {
      this.signInWithGoogleWeb();
    } else {
      this.signInWithGoogleCapacitor();
    }
  }

  async signInWithGoogleCapacitor() {
     let googleUser = await GoogleAuth.signIn();

    const credential = GoogleAuthProvider.credential(googleUser.authentication.idToken);
    await signInWithCredential(this.auth, credential).then(result => {
      this.user = result.user;
    }).catch(error => {
      console.log('Google Error = ', error);
    });
  }

  async signInWithGoogleWeb() {
    const provider = new GoogleAuthProvider();
    await signInWithPopup(this.auth, provider).then(result => {
      // The signed-in user info.
      this.user = result.user;
      // This gives you a Google Access Token.
    }).catch(error => {
      // Handle Errors here.
      const errorCode = error.code;
      const errorMessage = error.message;
      // The email of the user's account used.
      const email = error.customData.email;
      // The AuthCredential type that was used.
      const credential = GoogleAuthProvider.credentialFromError(error);
    });
  }

  async signOutWithGoogle() {
    GoogleAuth.signOut();
  }

  loginWithApple() {
    if (Capacitor.getPlatform() === 'web') {
      this.signInWithAppleWeb();
    } else {
      this.signInWithAppleNative();
    }
  }

  signInWithAppleWeb() {
    const provider = new OAuthProvider('apple.com');
    signInWithPopup(this.auth, provider).then((result: UserCredential) => {
    });
  }

  signInWithAppleNative() {
    let options: SignInWithAppleOptions = {
      clientId: 'com.reackageinteractive.tagg',
      redirectURI: 'https://tagg-9ee2b.firebaseapp.com/__/auth/handler',
      scopes: 'email',
      state: '12345'
    };

    SignInWithApple.authorize(options).then(async (result: SignInWithAppleResponse) => {

      const provider = new OAuthProvider('apple.com');
      const credential = provider.credential({
        idToken: result.response.identityToken,
      });

      const UserCredential = await signInWithCredential(
        this.auth,
        credential
      );
    });
  }


  async logout(): Promise<void> {
    await this.signOutWithGoogle();

    return signOut(this.auth).then(async () => {
      await this.clearLocalData();
    }).catch(error => {
      console.log('Auth Service: Logout Error = ', error);
    });
  }

  async clearLocalData()
  {
    await Preferences.clear();
    await this.songBookService.deleteSongBookFile();

    this.signedInUser.next(null);
    this.userProfile.next(null);
    this.user = null;
    this.email = '';
    this.password = '';
    this.fullname = '';
    this.stagename = '';
    this.profileService.clearProfile();

    if(this.subProfile != null) {
      this.subProfile.unsubscribe();
    }
  }

  async setLoginLocal(email, password) {
    await Preferences.set({
      key: 'login',
      value: JSON.stringify({
        email: email,
        password: password
      })
    });
  }

  async getLoginLocal() {
    const ret = await Preferences.get({ key: 'login' });
    const localData = JSON.parse(ret.value);

    this.email = localData.email;
    this.password = localData.password;
  }

  async forgotPassword(email: string) {
    const res = await sendPasswordResetEmail(this.auth, email);
  }

  public checkForProfile(email: string, fullname: string, stagename: string, avatarURL: string, foxId: string) {
    let username = email.replace(/[&\/\\#,+()!@^$~%.'":*?<>{}]/g,'_');

    this.profileService.getUserProfile().subscribe(data => {
      if (data === undefined) {
        this.profileService.createProfile(email, fullname, foxId, '', stagename, username, avatarURL, '', '', '');
      } else {
        this.profileService.userProfile = data;
      }
    });
  }

  doesProfileExist() {
    if (this.auth.currentUser != null) {
      this.subProfile = this.profileService.getUserProfile().subscribe(data => {
        this.userProfile.next(data);
      });
    }
  }

  async loginToFeed() {
    let username: string = await this.getEmail();
    let password: string = this.getPassword();

    if(this.phpAccessToken !== undefined && this.phpAccessToken !== null && this.phpAccessToken !== '') {
      return;
    }

    this.phpFoxService.login(username, password).then(response => {
      this.phpAccessToken = response.data.access_token;

      if (response.data.error) {
        if(response.data.error == 'invalid_grant') {
          console.log('User not found in Social Platform');
          console.log('Check user list to see if this person exists.. if not, then create them');
          this.registerUserToPhpFox();
        } else {
          console.log('User not found in Social Platform');
          console.log('ERROR = ' + response.data.error_description);
        }
      } else {
        console.log('User is logged into Social Platform');
      }
    }).catch(error => {
      console.log('Login to Feed returned error: ', error);
    });
  }

  async registerUserToPhpFox() {
    let fullname: string = this.user.displayName;
    let username: string = fullname.trim().replace(' ', '');
    let email: string = await this.getEmail();
    let password: string = this.getPassword();

    this.phpFoxService.registerUser(fullname, username, email, password);
  }

}

@gasci
Copy link
Author

gasci commented Jun 27, 2024

Unfortunately, none of the solutions above worked for me. I am using vue? How can we clear the cache for this plugin via vue?

@gasci
Copy link
Author

gasci commented Jun 27, 2024

I have just found the following solution:

await GoogleAuth.initialize({});
await GoogleAuth.signIn();

await GoogleAuth.initialize({});
await GoogleAuth.signOut();

I need to call these two always together.

@ivarsmednis
Copy link

Spent hours dealing with "CodetrixStudioCapacitorGoogleAuth/Plugin.swift:74: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value". Nothing worked from this topic tips.

The solution was to add iosClientId to GoogleAuth block in capacitor.config.ts file with value from Google OAuth iOS client id.

@raymac101
Copy link

Great catch @ivarsmednis ! I forgot that I had done that as well..

@gasci gasci closed this as completed Jul 5, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants