magiclabs / magic-flutter

Apache License 2.0
10 stars 5 forks source link

Magic.instance.user.isLoggedIn() does not return on iOS #51

Closed hbock-42 closed 11 months ago

hbock-42 commented 1 year ago

Problem

On ios, when we try to use the Magic.instance.user.isLoggedIn() method, the app enters an unknown state (aka not crashing, but the isLoggedIn method never returns.

Test configuration

xcode 15.0.1 ios 16.6.1 iPhone SE (physical device)

flutter 3.3.9 + magic SDK 5.0.0 flutter 3.16.0 + magic SDK 5.0.0 flutter 3.10.1 + magic SDK 5.0.0 flutter 3.10.1 + magic SDK 2.0.2

What I have found so far

Messages are being dequeue in magic_sdk.relayer.webview.dart, but I suspect the ios Webview crashes during runJavaScript execution:

_webViewCtrl.runJavaScript(
          "window.dispatchEvent(new MessageEvent('message', $jsonString));");

Since _webViewCtrl.runJavaScript is a future and is not awaited (which is normal here), I cannot be sure that the problem occurs here, but since it calls the platform specific runJavaScript method I cannot debug any further.

When the problem occurs, this appears in the terminal:

[Process] 0x122078640 - GPUProcessProxy::gpuProcessExited: reason=IdleExit
[Process] 0x123000a20 - [PID=95111] WebProcessProxy::gpuProcessExited: reason=IdleExit

Sample code

import 'package:flutter/material.dart';
import 'package:magic_sdk/magic_sdk.dart';
import 'package:magic_sdk/modules/blockchain/supported_blockchain.dart';

void main() {
  runApp(const MyApp());
  Magic.instance = Magic.blockchain(
    PUT_YOUR_PUBLIC_KEY_KEY,
    rpcUrl: "https://rpc.ghostnet.teztnets.xyz/",
    chain: SupportedBlockchain.tezos,
  );
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: true,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Stack(
        children: [
          const Home(),
          Magic.instance.relayer,
        ],
      ),
    );
  }
}

class Home extends StatefulWidget {
  const Home({super.key});

  @override
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  bool _isLoggedIn = false;
  bool _loading = false;
  bool _useStub = false;

  String get _loggingText => _isLoggedIn ? "I am logged in" : "I am not logged in";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                const Text("Use stub"),
                Switch(
                  value: _useStub,
                  onChanged: (value) =>  setState(() => _useStub = value),
                ),
              ],
            ),
            Text(_loading ? "fetching logging state" : _loggingText),
            TextButton(
              onPressed: () async {
                setState(() => _loading = true);
                final isLoggedIn = await _fetchIsLoggedIn(useStub: _useStub);
                setState(() {
                  _isLoggedIn = isLoggedIn;
                  _loading = false;
                });
              },
              child: const Text('Click to test if user is logged in'),
            ),
            if (_loading) const LinearProgressIndicator(),
          ],
        ),
      ),
    );
  }

  Future<bool> _fetchIsLoggedIn({bool useStub = false}) async {
    if (useStub) {
      print("try fetching stub");
      await Future.delayed(const Duration(milliseconds: 1200));
      print("end fetching stubg");
      return true;
    } else {
      print("try fetching magic use is logged in");
      final isLoggedIn = await Magic.instance.user.isLoggedIn();
      print("End fetching magic use is logged in with value: $isLoggedIn");
      return isLoggedIn;
    }
  }
}
Ariflo commented 12 months ago

👋 @hbock-42 Thank you for bringing this to our attention. We've just a released an update to our system that may resolve this issue. Could you please try restarting the app, or even better re-install and fresh build and try again? Let us know if you continue to see the hang up.

hbock-42 commented 12 months ago

Hello @Ariflo , thank you for the quick update.

As you suggested a fress build was necessary, but the problem is solved. Rebuilding the project after flutter clean was not enough, I had to manually uninstall the application from my phone and reinstall it to solve the problem (I can confirm this is required since I had our professional app installed, and the reproducible project installed, and for both of them, just rebuilding was not enough).

So problem solved, I just have to figure out how to make my customers delete then reinstall the application

hbock-42 commented 12 months ago

Is there something we can do to prevent our users to be able de delete then re-install the application? Maybe we could delete some files generated the first time the magic SDK is used (shared pref or I don't know)? It would force us to make a new version of our app and force our users to update but this is definitely not a problem

Ariflo commented 11 months ago

Is there something we can do to prevent our users to be able de delete then re-install the application? Maybe we could delete some files generated the first time the magic SDK is used (shared pref or I don't know)? It would force us to make a new version of our app and force our users to update but this is definitely not a problem

@hbock-42 unfortunately the problem stems from data put in the local storage of the webview that our SDK utilizes. With that in mind, the only way to clear the cache of a webview on an iPhone is to uninstall and re-install the app. 😢

Our sincerest apologies, as we know this solution is not ideal. For now we are suggesting that our developers release an empty update to encourage their users to re-install the app. As is suggested here