Skip to content

[webview_flutter_wkwebview] iOS Crash on WebKitLibraryPigeonInstanceManagerApi.removeStrongReference #168535

@omar3alaa

Description

@omar3alaa

What package does this bug report belong to?

webview_flutter_wkwebview

What target platforms are you seeing this bug on?

iOS

Have you already upgraded your packages?

Yes

Dependency versions

pubspec.lock
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
meta:
    dependency: "direct main"
    description:
      name: meta
      sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
      url: "https://pub.dev"
    source: hosted
    version: "1.15.0"
  path:
    dependency: "direct dev"
    description:
      name: path
      sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
      url: "https://pub.dev"
    source: hosted
    version: "1.9.0"
  plugin_platform_interface:
    dependency: "direct dev"
    description:
      name: plugin_platform_interface
      sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
      url: "https://pub.dev"
    source: hosted
    version: "2.1.8"
  webview_flutter:
    dependency: "direct main"
    description:
      name: webview_flutter
      sha256: "889a0a678e7c793c308c68739996227c9661590605e70b1f6cf6b9a6634f7aec"
      url: "https://pub.dev"
    source: hosted
    version: "4.10.0"
  webview_flutter_android:
    dependency: transitive
    description:
      name: webview_flutter_android
      sha256: "285cedfd9441267f6cca8843458620b5fda1af75b04f5818d0441acda5d7df19"
      url: "https://pub.dev"
    source: hosted
    version: "4.1.0"
  webview_flutter_platform_interface:
    dependency: transitive
    description:
      name: webview_flutter_platform_interface
      sha256: d937581d6e558908d7ae3dc1989c4f87b786891ab47bb9df7de548a151779d8d
      url: "https://pub.dev"
    source: hosted
    version: "2.10.0"
  webview_flutter_wkwebview:
    dependency: transitive
    description:
      name: webview_flutter_wkwebview
      sha256: bf0745adeaca48a3105473440cffade47720fe2d56514de4e86f0d363439c4a7
      url: "https://pub.dev"
    source: hosted
    version: "3.18.6"
sdks:
  dart: ">=3.5.3 <4.0.0"
  flutter: ">=3.24.0"

Steps to reproduce

We haven’t been able to reproduce it locally so far. However, we’ve observed a spike in this crash, particularly on iOS 18, though it’s not limited to that version.

Expected results

Not to crash

Actual results

Crash happens on iOS.

Code sample

Code sample
import 'dart:async';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:help_center/src/data/model/help_center_message_mapper.dart';
import 'package:help_center/src/data/model/help_center_message_model.dart';
import 'package:help_center/src/di/constants/help_center_constants.dart';
import 'package:help_center/src/presentation/screen/help_center_home_screen/widgets/help_center_image_upload_option_dialog.dart';
import 'package:help_center/src/presentation/screen/help_center_home_screen/widgets/help_center_loading_widget.dart';
import 'package:help_center/src/presentation/screen/help_center_home_screen/widgets/help_center_webview/bloc/help_center_webview_bloc.dart';
import 'package:help_center/src/presentation/screen/help_center_home_screen/widgets/help_center_webview/bloc/help_center_webview_event.dart';
import 'package:help_center/src/presentation/screen/help_center_home_screen/widgets/help_center_webview/bloc/help_center_webview_state.dart';
import 'package:help_center/src/presentation/utils/help_center_file_picker.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:webview_flutter_android/webview_flutter_android.dart'
    as webview_flutter_android;

class HelpCenterWebViewWidget extends StatefulWidget {
  final bool shouldApplyFreezeIssueWorkaround;
  final String url;

  const HelpCenterWebViewWidget({
    required this.shouldApplyFreezeIssueWorkaround,
    required this.url,
  });

  @override
  _HelpCenterWebViewState createState() => _HelpCenterWebViewState();
}

class _HelpCenterWebViewState extends State<HelpCenterWebViewWidget> {
  late HelpCenterWebViewBloc _bloc;
  late Stopwatch _stopwatch;
  bool isLoadingTimeAlreadyReported = false;
  final WebViewController controller = WebViewController();

  @override
  void initState() {
    super.initState();
    _stopwatch = Stopwatch()..start();
    if (Platform.isAndroid) {
      final platformController = controller.platform
          as webview_flutter_android.AndroidWebViewController;
      platformController.setOnShowFileSelector(_showFileSelector);
    }
    controller
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..enableZoom(false) // Disabling zoom on webview
      ..loadRequest(Uri.parse(widget.url))
      ..addJavaScriptChannel(
        HelpCenterConstants.bridgeEntry(),
        onMessageReceived: (JavaScriptMessage message) async =>
            _onMessageReceived(message, controller),
      );
  }

  @override
  Widget build(BuildContext context) {
    _bloc = BlocProvider.of<HelpCenterWebViewBloc>(context);
    return Scaffold(
      body: BlocConsumer<HelpCenterWebViewBloc, HelpCenterWebViewState>(
        buildWhen: _buildWhen,
        listenWhen: _listenWhen,
        builder: _onStateChangeBuilder,
        listener: _onStateChangeListener,
      ),
    );
  }

  bool _buildWhen(
    HelpCenterWebViewState previous,
    HelpCenterWebViewState current,
  ) {
    return current is HelpCenterWebViewLoadingState ||
        current is HelpCenterWebViewLoadedState;
  }

  bool _listenWhen(
    HelpCenterWebViewState previous,
    HelpCenterWebViewState current,
  ) {
    return current is HelpCenterWebViewDeeplinkNavigationState;
  }

  Widget _onStateChangeBuilder(
    BuildContext context,
    HelpCenterWebViewState state,
  ) {
    return SafeArea(
      child: PopScope(
        key: const Key("help_center_popscope_widget"),
        canPop: _shouldAllowSwipingBack(state),
        child: Stack(
          children: [
            Offstage(
              key: const Key("help_center_webview_offstage_widget"),
              offstage: _shouldHideWebView(state),
              child: WebViewWidget(
                key: const Key("help_center_webview_widget"),
                controller: controller,
              ),
            ),
            if (state is HelpCenterWebViewLoadingState)
              const HelpCenterLoadingWidget(),
          ],
        ),
      ),
    );
  }

  bool _shouldAllowSwipingBack(HelpCenterWebViewState state) {
    // Only check loading state, if we should apply the workaround
    if (widget.shouldApplyFreezeIssueWorkaround) {
      return state is HelpCenterWebViewLoadingState;
    }
    // If no need to apply the fix (e.g older iOS versions than iOS 18), always make it swipeable
    return true;
  }

  bool _shouldHideWebView(HelpCenterWebViewState state) {
    // Only check loading state, if we should apply the workaround
    if (widget.shouldApplyFreezeIssueWorkaround) {
      return state is HelpCenterWebViewLoadingState;
    }
    // If no need to apply the fix (e.g older iOS versions than iOS 18), always show the web view even while loading.
    return false;
  }

  Future<void> _onStateChangeListener(
    BuildContext context,
    HelpCenterWebViewState state,
  ) async {
    if (state is HelpCenterWebViewDeeplinkNavigationState) {
      await _navigate(state.deeplink);
    }
  }

  Future<void> _onMessageReceived(
    JavaScriptMessage message,
    WebViewController controller,
  ) async {
    final HelpCenterMessageResponse? helpCenterMessageResponseModel =
        HelpCenterMessageMapper.map(message.message);
    if (helpCenterMessageResponseModel == null) {
      return;
    }
    if (helpCenterMessageResponseModel is! HelpCenterMessageResponseModel) {
      return;
    }
    if (helpCenterMessageResponseModel.type ==
        HelpCenterConstants.helpCenterClientCriticalContentRendered) {
      if (!isLoadingTimeAlreadyReported) {
        _reportLoadingTime();
      }
      _triggerLoadedEvent();
    }
    if (helpCenterMessageResponseModel.type ==
        HelpCenterConstants.helpCenterInitStartedMessage) {
      _helpCenterInitStartedMessage(controller);
      return;
    }

    if (helpCenterMessageResponseModel.type ==
        HelpCenterConstants.helpCenterAsksForShutdownMessage) {
      _helpCenterAskedForShutdown(controller);
      return;
    }

    if (helpCenterMessageResponseModel.type ==
        HelpCenterConstants.helpCenterDeeplinkClicked) {
      await _helpCenterDeeplinkClicked(helpCenterMessageResponseModel);
      return;
    }
  }

  void _reportLoadingTime() {
    isLoadingTimeAlreadyReported = true;
    _stopwatch.stop();
    _bloc.add(
      HelpCenterWebViewReportLoadingTimeEvent(
        timeTakenToLoad: _stopwatch.elapsedMilliseconds,
      ),
    );
    return;
  }

  void _triggerLoadedEvent() {
    _bloc.add(HelpCenterWebViewLoadedEvent());
  }

  void _helpCenterInitStartedMessage(WebViewController webViewController) {
    _bloc.add(HelpCenterWebViewInitStartedEvent());

    final javascriptCode =
        "${HelpCenterConstants.messageToSend()}(${HelpCenterConstants.helpCenterInitialBridgeMessage})";
    webViewController.runJavaScript(
      javascriptCode,
    );
  }

  void _helpCenterAskedForShutdown(WebViewController webViewController) {
    webViewController
        .removeJavaScriptChannel(HelpCenterConstants.bridgeEntry());
    Navigator.of(context).pop();
  }

  Future<void> _helpCenterDeeplinkClicked(
    HelpCenterMessageResponseModel messageModel,
  ) async {
    final Map<String, dynamic>? payload = messageModel.payload;

    if (payload == null) {
      return;
    }

    final String? deeplink = payload['link'];
    if (deeplink == null) {
      return;
    }

    _bloc.add(HelpCenterWebViewDeeplinkClickedEvent(deeplink: deeplink));
  }

  Future<List<String>> _showFileSelector(
    webview_flutter_android.FileSelectorParams params,
  ) async {
    final helpCenterFilePicker = HelpCenterFilePickerImpl();

    if (params.acceptTypes.any((type) => type == 'image/*')) {
      if (params.mode ==
          webview_flutter_android.FileSelectorMode.openMultiple) {
        final images = await helpCenterFilePicker.getImages();
        return images;
      } else {
        final image =
            await _createImageUploadOptionDialog(context, helpCenterFilePicker);
        if (image == null) {
          return [];
        }
        return [image];
      }
    } else {
      final allowMultipleSelection = (params.mode ==
          webview_flutter_android.FileSelectorMode.openMultiple);
      final files = await helpCenterFilePicker.getFiles(
        allowMultipleSelection: allowMultipleSelection,
      );
      return files;
    }
  }

  Future<String?> _createImageUploadOptionDialog(
    BuildContext context,
    HelpCenterFilePicker helpCenterFilePicker,
  ) async {
    final Completer<String?> completer = Completer<String?>();
    showDialog<String?>(
      context: context,
      builder: (BuildContext context) {
        return HelpCenterImageUploadOptionDialog(
          helpCenterFilePicker: helpCenterFilePicker,
        );
      },
    ).then((result) {
      completer.complete(result);
    });
    return completer.future;
  }

  Future<void> _navigate(String deeplink) async {
    if (deeplink.startsWith(HelpCenterConstants.talabatDeeplink)) {
      Navigator.of(context).pushNamed(deeplink);
    } else if (await canLaunchUrl(Uri.parse(deeplink))) {
      final uri = Uri.parse(deeplink);
      await launchUrl(uri);
    }
  }
}

Logs

Logs
          Fatal Exception: NSInternalInconsistencyException
0  CoreFoundation                 0x2d5fc __exceptionPreprocess
1  libobjc.A.dylib                0x31244 objc_exception_throw
2  Foundation                     0x82dec0 _userInfoForFileAndLine
3  Flutter                        0x15420 -[FlutterEngine sendOnChannel:message:binaryReply:] + 1255 (FlutterEngine.mm:1255)
4  Flutter                        0x5d3810 -[FlutterBinaryMessengerRelay sendOnChannel:message:binaryReply:] + 34 (FlutterBinaryMessengerRelay.mm:34)
5  Flutter                        0x5d6428 -[FlutterBasicMessageChannel sendMessage:reply:] + 120 (FlutterChannels.mm:120)
6  webview_flutter_wkwebview      0x1509c WebKitLibraryPigeonInstanceManagerApi.removeStrongReference(identifier:completion:) + 357 (WebKitLibrary.g.swift:357)
7  webview_flutter_wkwebview      0x13df4 WebKitLibraryPigeonInternalFinalizer.__deallocating_deinit + 532 (WebKitLibrary.g.swift:532)
8  libswiftCore.dylib             0x3d817c _swift_release_dealloc
9  libswiftCore.dylib             0x3d9290 bool swift::RefCounts<swift::RefCountBitsT<(swift::RefCountInlinedness)1>>::doDecrementSlow<(swift::PerformDeinit)1>(swift::RefCountBitsT<(swift::RefCountInlinedness)1>, unsigned int)
10 libobjc.A.dylib                0x53b0 _object_remove_associations
11 libobjc.A.dylib                0x4f5c objc_destructInstance
12 libobjc.A.dylib                0x4eec _objc_rootDealloc
13 UIKitCore                      0x989ec -[UIResponder dealloc]
14 UIKitCore                      0x9871c -[UIView dealloc]
15 WebKit                         0x58dd18 (Missing UUID 7fd7e19e8e703e7d8133e5c310a242d1)
16 Foundation                     0x199054 _NSKVOPerformWithDeallocatingObservable
17 Foundation                     0x198d68 NSKVODeallocate
18 libobjc.A.dylib                0x5bac object_cxxDestructFromClass(objc_object*, objc_class*)
19 libobjc.A.dylib                0x4f4c objc_destructInstance
20 libobjc.A.dylib                0x4eec _objc_rootDealloc
21 Flutter                        0x365c0 std::_fl::__tree<std::_fl::__value_type<long long, fml::scoped_nsobject<NSObject<FlutterPlatformView>>>, std::_fl::__map_value_compare<long long, std::_fl::__value_type<long long, fml::scoped_nsobject<NSObject<FlutterPlatformView>>>, std::_fl::less<long long>, true>, std::_fl::allocator<std::_fl::__value_type<long long, fml::scoped_nsobject<NSObject<FlutterPlatformView>>>>>::destroy(std::_fl::__tree_node<std::_fl::__value_type<long long, fml::scoped_nsobject<NSObject<FlutterPlatformView>>>, void*>*) + 133 (new.cpp:133)
22 Flutter                        0x365b4 std::_fl::__tree<std::_fl::__value_type<long long, fml::scoped_nsobject<NSObject<FlutterPlatformView>>>, std::_fl::__map_value_compare<long long, std::_fl::__value_type<long long, fml::scoped_nsobject<NSObject<FlutterPlatformView>>>, std::_fl::less<long long>, true>, std::_fl::allocator<std::_fl::__value_type<long long, fml::scoped_nsobject<NSObject<FlutterPlatformView>>>>>::destroy(std::_fl::__tree_node<std::_fl::__value_type<long long, fml::scoped_nsobject<NSObject<FlutterPlatformView>>>, void*>*) + 86 (scoped_typeref.h:86)
23 Flutter                        0x365b4 std::_fl::__tree<std::_fl::__value_type<long long, fml::scoped_nsobject<NSObject<FlutterPlatformView>>>, std::_fl::__map_value_compare<long long, std::_fl::__value_type<long long, fml::scoped_nsobject<NSObject<FlutterPlatformView>>>, std::_fl::less<long long>, true>, std::_fl::allocator<std::_fl::__value_type<long long, fml::scoped_nsobject<NSObject<FlutterPlatformView>>>>>::destroy(std::_fl::__tree_node<std::_fl::__value_type<long long, fml::scoped_nsobject<NSObject<FlutterPlatformView>>>, void*>*) + 86 (scoped_typeref.h:86)
24 Flutter                        0x19b38 std::_fl::__shared_ptr_pointer<flutter::FlutterPlatformViewsController*, std::_fl::shared_ptr<flutter::FlutterPlatformViewsController>::__shared_ptr_default_delete<flutter::FlutterPlatformViewsController, flutter::FlutterPlatformViewsController>, std::_fl::allocator<flutter::FlutterPlatformViewsController>>::__on_zero_shared() + 1087 (__tree:1087)
25 Flutter                        0x122c0 -[FlutterEngine destroyContext] + 74 (atomic_support.h:74)
26 CoreFoundation                 0x4e488 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__
27 CoreFoundation                 0x4e3a4 ___CFXRegistrationPost_block_invoke
28 CoreFoundation                 0x4cf18 _CFXRegistrationPost
29 CoreFoundation                 0x4c01c _CFXNotificationPost
30 Foundation                     0x9fa9c -[NSNotificationCenter postNotificationName:object:userInfo:]
31 UIKitCore                      0x115c528 -[UIApplication _terminateWithStatus:]
32 UIKitCore                      0x1b5644 -[_UISceneLifecycleMultiplexer _evalTransitionToSettings:fromSettings:forceExit:withTransitionStore:]
33 UIKitCore                      0x906e8c -[_UISceneLifecycleMultiplexer forceExitWithTransitionContext:scene:]
34 UIKitCore                      0x1157c28 -[UIApplication workspaceShouldExit:withTransitionContext:]
35 FrontBoardServices             0x56cd4 __63-[FBSWorkspaceScenesClient willTerminateWithTransitionContext:]_block_invoke_2
36 FrontBoardServices             0x1f4c8 -[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:]
37 FrontBoardServices             0x56c74 __63-[FBSWorkspaceScenesClient willTerminateWithTransitionContext:]_block_invoke
38 libdispatch.dylib              0x3fa8 _dispatch_client_callout
39 libdispatch.dylib              0x79f0 _dispatch_block_invoke_direct
40 FrontBoardServices             0x18378 __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__
41 FrontBoardServices             0x182f8 -[FBSMainRunLoopSerialQueue _targetQueue_performNextIfPossible]
42 FrontBoardServices             0x181d0 -[FBSMainRunLoopSerialQueue _performNextFromRunLoopSource]
43 CoreFoundation                 0x73f4c __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
44 CoreFoundation                 0x73ee0 __CFRunLoopDoSource0
45 CoreFoundation                 0x76ba4 __CFRunLoopDoSources0
46 CoreFoundation                 0x75d3c __CFRunLoopRun
47 CoreFoundation                 0xc8284 CFRunLoopRunSpecific
48 GraphicsServices               0x14c0 GSEventRunModal
49 UIKitCore                      0x3ee674 -[UIApplication _run]
50 UIKitCore                      0x14e88 UIApplicationMain
51 UIKitCore                      0x75115c keypath_get_selector_hoverStyle
52 Runner                         0x2db6c main (StandaloneAppDelegate.swift)
53 ???                            0x1bf839de8 (Missing)
        

Flutter Doctor output

Doctor output
[✓] Flutter (Channel stable, 3.24.5, on macOS 15.4 24E248 darwin-arm64, locale en-AE)
    • Flutter version 3.24.5 on channel stable at /Users/omaralaa/fvm/versions/3.24.5
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision dec2ee5c1f (6 months ago), 2024-11-13 11:13:06 -0800
    • Engine revision a18df97ca5
    • Dart version 3.5.4
    • DevTools version 2.37.3

[✓] Android toolchain - develop for Android devices (Android SDK version 35.0.1)
    • Android SDK at /Users/omaralaa/Library/Android/sdk
    • Platform android-35, build-tools 35.0.1
    • ANDROID_HOME = /Users/omaralaa/Library/Android/sdk
    • ANDROID_SDK_ROOT = /Users/omaralaa/Library/Android/sdk
    • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 21.0.5+-12932927-b750.29)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 16.3)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 16E140
    • CocoaPods version 1.15.2

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2024.2)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 21.0.5+-12932927-b750.29)

[✓] IntelliJ IDEA Community Edition (version 2023.3.8)
    • IntelliJ at /Users/omaralaa/Applications/IntelliJ IDEA Community Edition.app
    • Flutter plugin version 83.0.1
    • Dart plugin version 233.15325.11

[✓] VS Code (version 1.98.2)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.108.0

[✓] Connected device (4 available)
    • sdk gphone16k arm64 (mobile)    • emulator-5554         • android-arm64  • Android 15 (API 35) (emulator)
    • macOS (desktop)                 • macos                 • darwin-arm64   • macOS 15.4 24E248 darwin-arm64
    • Mac Designed for iPad (desktop) • mac-designed-for-ipad • darwin         • macOS 15.4 24E248 darwin-arm64
    • Chrome (web)                    • chrome                • web-javascript • Google Chrome 136.0.7103.93
    ! Error: Browsing on the local area network for Omar’s iPhone. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac.
      The device must be opted into Developer Mode to connect wirelessly. (code -27)
    ! Error: Browsing on the local area network for Nouran. Ensure the device is unlocked and attached with a cable or associated with the same local area network as this Mac.
      The device must be opted into Developer Mode to connect wirelessly. (code -27)

[✓] Network resources
    • All expected network resources are available.

• No issues found!

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work lista: productionIssues experienced in live production appsc: crashStack traces logged to the consoleneeds repro infoAutomated crash report whose cause isn't yet knownp: webviewThe WebView pluginpackageflutter/packages repository. See also p: labels.platform-iosiOS applications specificallyteam-ecosystemOwned by Ecosystem teamtriaged-ecosystemTriaged by Ecosystem team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions