将应用程序从Swift更新到Flutter后,Firebase Auth状态丢失

4ioopgfo  于 11个月前  发布在  Swift
关注(0)|答案(2)|浏览(117)

我有一个应用程序,住在用Swift开发的AppStore中,我使用Firebase Auth。一切都很好。在下一次更新中,应用程序将是Flutter应用程序,但也使用相同的Firebase后端,包括Auth。
当我测试应用程序时,authState工作正常。用户在应用程序重新启动后仍然登录。

然而当我得到旧的应用程序,通过TestFlight更新到新版本时,用户总是在应用程序重新启动后注销。我发现的唯一修复方法是删除应用程序(新版本)并重新安装。

这是一个已知的问题吗?有什么我可以做的吗?这对我的应用程序的老用户来说是超级令人沮丧的,他们更新到新版本。
这是我如何跟踪authState的:

static bool isLoggedIn() {
    if (_isLoggedIn == null) {
      _firebaseAuth.authStateChanges().listen((User? user) {
        _isLoggedIn = user?.uid != null;
      });
      _isLoggedIn = _firebaseAuth.currentUser?.uid != null;
    }
    return _isLoggedIn ?? false;
  }

字符串
另外,我在main中得到了这个方法,所以新用户在第一次打开新应用时会注销:

static Future<void> signOutUserIfUserDeinstalledAndReinstalledApp() async {
    bool? isFirstStart = await LocalStorageService.getData(
      key: LocalStorageKeys.isFirstStart,
    );
    if (isFirstStart == null && !kIsWeb) {
      // await PushNotificationService.cancelAllNotifications();
      await LocalStorageService.clear();
      await signOut(deleteMessaingToken: false);
      await LocalStorageService.setBool(
        key: LocalStorageKeys.isFirstStart,
        value: false,
      );
    }
  }

nkhmeac6

nkhmeac61#

你可以使用方法通道来实现它。这有点棘手,但它是可能的。
所以我们可以创建一个方法通道来检查平台(在iOS上)上的用户详细信息,auth状态和会话。我认为你对swift/objective c很好,所以这不会是一个问题。
我不擅长swift,只是创建一个结构,这样你就可以得到的想法。下面的代码是电池,但你可以做同样的FirebaseAuth从swift方面。

import UIKit
import Flutter

enum ChannelName {
  static let user = "samples.flutter.io/user"
  static let authStatus = "samples.flutter.io/authStatus"
}

enum AuthState {
  static let signedIn = "signedIn"
  static let signedOut = "unauthentic"
}

enum MyFlutterErrorCode {
  static let unavailable = "UNAVAILABLE"
}

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate, FlutterStreamHandler {
  private var eventSink: FlutterEventSink?

  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    GeneratedPluginRegistrant.register(with: self)
    guard let controller = window?.rootViewController as? FlutterViewController else {
      fatalError("rootViewController is not type FlutterViewController")
    }

<-----replace following code with returning user details----->
    let batteryChannel = FlutterMethodChannel(name: ChannelName.battery,
                                              binaryMessenger: controller.binaryMessenger)
    batteryChannel.setMethodCallHandler({
      [weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in
      guard call.method == "getBatteryLevel" else {
        result(FlutterMethodNotImplemented)
        return
      }
      self?.receiveBatteryLevel(result: result)
    })
    <-----till here----->

<-----replace following code with auth status----->
    let chargingChannel = FlutterEventChannel(name: ChannelName.charging,
                                              binaryMessenger: controller.binaryMessenger)
    chargingChannel.setStreamHandler(self)
<-----till here----->
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }

<----Replace with user auth---->
  private func receiveBatteryLevel(result: FlutterResult) {
    let device = UIDevice.current
    device.isBatteryMonitoringEnabled = true
    guard device.batteryState != .unknown  else {
      result(FlutterError(code: MyFlutterErrorCode.unavailable,
                          message: "Battery info unavailable",
                          details: nil))
      return
    }
    result(Int(device.batteryLevel * 100))
  }

  public func onListen(withArguments arguments: Any?,
                       eventSink: @escaping FlutterEventSink) -> FlutterError? {
    self.eventSink = eventSink
    UIDevice.current.isBatteryMonitoringEnabled = true
    sendBatteryStateEvent()
    NotificationCenter.default.addObserver(
      self,
      selector: #selector(AppDelegate.onBatteryStateDidChange),
      name: UIDevice.batteryStateDidChangeNotification,
      object: nil)
    return nil
  }

  @objc private func onBatteryStateDidChange(notification: NSNotification) {
    sendBatteryStateEvent()
  }

  private func sendBatteryStateEvent() {
    guard let eventSink = eventSink else {
      return
    }

    switch UIDevice.current.batteryState {
    case .full:
      eventSink(BatteryState.charging)
    case .charging:
      eventSink(BatteryState.charging)
    case .unplugged:
      eventSink(BatteryState.discharging)
    default:
      eventSink(FlutterError(code: MyFlutterErrorCode.unavailable,
                             message: "Charging status unavailable",
                             details: nil))
    }
  }

  public func onCancel(withArguments arguments: Any?) -> FlutterError? {
    NotificationCenter.default.removeObserver(self)
    eventSink = nil
    return nil
  }

<-----till----->
}

字符串
在Flutter侧

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

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

  @override
  State<PlatformChannel> createState() => _PlatformChannelState();
}

class _PlatformChannelState extends State<PlatformChannel> {
  static const MethodChannel methodChannel =
      MethodChannel('samples.flutter.io/battery');
  static const EventChannel eventChannel =
      EventChannel('samples.flutter.io/charging');

  String _batteryLevel = 'Battery level: unknown.';
  String _chargingStatus = 'Battery status: unknown.';

  Future<void> _getBatteryLevel() async {
    String batteryLevel;
    try {
      final int? result = await methodChannel.invokeMethod('getBatteryLevel');
      batteryLevel = 'Battery level: $result%.';
    } on PlatformException {
      batteryLevel = 'Failed to get battery level.';
    }
    setState(() {
      _batteryLevel = batteryLevel;
    });
  }

  @override
  void initState() {
    super.initState();
    eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError);
  }

  void _onEvent(Object? event) {
    setState(() {
      _chargingStatus =
          "Battery status: ${event == 'charging' ? '' : 'dis'}charging.";
    });
  }

  void _onError(Object error) {
    setState(() {
      _chargingStatus = 'Battery status: unknown.';
    });
  }

  @override
  Widget build(BuildContext context) {
    return Material(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: <Widget>[
          Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text(_batteryLevel, key: const Key('Battery level label')),
              Padding(
                padding: const EdgeInsets.all(16.0),
                child: ElevatedButton(
                  onPressed: _getBatteryLevel,
                  child: const Text('Refresh'),
                ),
              ),
            ],
          ),
          Text(_chargingStatus),
        ],
      ),
    );
  }
}

void main() {
  runApp(const MaterialApp(home: PlatformChannel()));
}


这个答案可以改进,这只是一个参考方法。任何帮助将不胜感激。
Ref:https://docs.flutter.dev/platform-integration/platform-channels
对于连续收听(流):https://github.com/flutter/flutter/tree/master/examples/platform_channel_swift

vxqlmq5t

vxqlmq5t2#

编辑:
听起来像是Firebase的bug/疏忽(Flutter实现不考虑这种边缘情况),或者可能是您自己代码中的意外行为。
但由于我们知道它与用户本地数据和设置中的某种冲突残留有关,而且我们知道只要清除用户设置和数据,它就能正常工作(因为这是更新和重新安装之间的唯一区别),只要用户数据和设置中没有关键内容,一个潜在的解决方法是让你的更新的Flutter版本包含一个初始化例程,它首先清除用户数据和设置(只要这是新版本第一次运行)。
或者是时候构建一个最小的完整示例项目并开始分析Firebase auth实现。
//-- original:
用户是否经过身份验证(user == null or not)的事实仅由本地设备(而不是后端)通过存储的会话令牌知道(确定)。
Flutter和ios Firebase auth实现之间在存储此令牌的位置和方式上必须存在差异。
我看不出这是如何解决的。
也不明白为什么这会是一个巨大的不便--我总是意外地退出应用程序,我只是耸耸肩,重新登录。

相关问题