Tencent / MMKV

An efficient, small mobile key-value storage framework developed by WeChat. Works on Android, iOS, macOS, Windows, and POSIX.
Other
17.44k stars 1.91k forks source link

Crash on iOS "'MMKV not initialized properly, must call +initializeMMKV:" #1082

Closed JanSob closed 1 year ago

JanSob commented 1 year ago

The language of MMKV

Dart

The version of MMKV

v1.2.7

The platform of MMKV

(Flutter) iOS

The installation of MMKV

Flutter plugin // Cocoapods

1/4 What's the issue?

The App crashes immediately after starting it, both on a physical device and on the simulator (iOS). The app runs perfectly fine on Android. The first thing i do on main in dart is to call 'await MMKV.initialize();',

2/4. This is the error-log:

2023-05-25 18:03:54.555556+0200 Runner[2309:72867] Metal API Validation Enabled
2023-05-25 18:03:54.739907+0200 Runner[2309:72867] Warning: Unable to create restoration in progress marker file
2023-05-25 18:03:54.753567+0200 Runner[2309:72867] [cManager] Monitoring disabled
2023-05-25 18:03:54.753787+0200 Runner[2309:72867] *** Assertion failure in +[MMKV mmkvWithID:cryptKey:rootPath:mode:], libMMKV.mm:190
2023-05-25 18:03:54.755083+0200 Runner[2309:72867] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'MMKV not initialized properly, must call +initializeMMKV: in main thread before calling any other MMKV methods'
*** First throw call stack:
(0x18077bc60 0x197fa7ee4 0x181f21c80 0x101360d04 0x101360a20 0x10002a0b0 0x100039910 0x1018c1fc8 0x1018c3934 0x100039974 0x10003a1f8 0x10003a494 0x100048598 0x10002d28c 0x10002d628 0x182d2e26c 0x182f02ae8 0x182eec20c 0x182d59f7c 0x182bc0ca0 0x182ce36d4 0x182df8cb0 0x183021184 0x182fb0ec4 0x182bc19ec 0x182c7d5dc 0x182bc377c 0x182ca7e2c 0x1830fd90c 0x182bf1424 0x182c331c4 0x182d5e9e4 0x182ca6248 0x191907c50 0x191929f98 0x1918eabdc 0x1918ebfd4 0x1018c1fc8 0x1018c54cc 0x1918ec2ac 0x1918eb7c0 0x1918ef960 0x18079c448 0x1807ac578 0x1806ee734 0x1806f3e08 0x180707174 0x1a1222988 0x182f0a7d8 0x182ca3c70 0x10002d9f8 0x1012144d0)
libc++abi: terminating with uncaught exception of type NSException
dyld4 config: DYLD_LIBRARY_PATH=/usr/lib/system/introspection DYLD_INSERT_LIBRARIES=/Developer/usr/lib/libBacktraceRecording.dylib:/Developer/usr/lib/libMainThreadChecker.dylib:/Developer/Library/PrivateFrameworks/DTDDISupport.framework/libViewDebuggerSupport.dylib
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'MMKV not initialized properly, must call +initializeMMKV: in main thread before calling any other MMKV methods'
terminating with uncaught exception of type NSException

3/4. This is the main-function in Flutter:

void main() async {
  await MMKV.initialize();
  WidgetsFlutterBinding.ensureInitialized();
  final sharedPreferences = await SharedPreferences.getInstance();
  final preferenceRepo = PreferenceRepository(sharedPreferences);
  await EnvRepository.load();

  final preconfiguredRegistrationId = EnvRepository.preconfiguredRegistrationId;
  if (preconfiguredRegistrationId != null &&
      preconfiguredRegistrationId.isNotEmpty) {
    await preferenceRepo.setRegistrationId(preconfiguredRegistrationId);
  }

  runApp(
    RepositoryProvider(
      create: (context) => preferenceRepo,
      child: const App(),
    ),
  );
}

4/4. This is the Podfile

# Uncomment this line to define a global platform for your project
platform :ios, '14.0'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

project 'Runner', {
  'Debug' => :debug,
  'Profile' => :release,
  'Release' => :release,
}

def flutter_root
  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
  unless File.exist?(generated_xcode_build_settings_path)
    raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
  end

  File.foreach(generated_xcode_build_settings_path) do |line|
    matches = line.match(/FLUTTER_ROOT\=(.*)/)
    return matches[1].strip if matches
  end
  raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end

def fix_mmkv_plugin_name(flutter_application_path)
  is_module = false
  plugin_deps_file = File.expand_path(File.join(flutter_application_path, '..', '.flutter-plugins-dependencies'))
  if not File.exists?(plugin_deps_file)
    is_module = true;
    plugin_deps_file = File.expand_path(File.join(flutter_application_path, '.flutter-plugins-dependencies'))
  end

  plugin_deps = JSON.parse(File.read(plugin_deps_file)).dig('plugins', 'ios') || []
  plugin_deps.each do |plugin|
    if plugin['name'] == 'mmkv' || plugin['name'] == 'mmkvflutter'
      require File.expand_path(File.join(plugin['path'], 'tool', 'mmkvpodhelper.rb'))
      mmkv_fix_plugin_name(flutter_application_path, is_module)
      return
    end
  end
  raise "Fail to find any mmkv plugin dependencies. If you're running pod install manually, make sure flutter pub get is executed first"
end

fix_mmkv_plugin_name(File.dirname(File.realpath(__FILE__)))

require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)

flutter_ios_podfile_setup

target 'Runner' do
  use_frameworks!
  use_modular_headers!

  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end

post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)

    target.build_configurations.each do |config|

          # You can enable the permissions needed here. For example to enable camera
          # permission, just remove the `#` character in front so it looks like this:
          #
          # ## dart: PermissionGroup.camera
          # 'PERMISSION_CAMERA=1'
          #
          #  Preprocessor definitions can be found in: https://github.com/Baseflow/flutter-permission-handler/blob/master/permission_handler_apple/ios/Classes/PermissionHandlerEnums.h
          config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
            '$(inherited)',

            ## dart: PermissionGroup.calendar
            # 'PERMISSION_EVENTS=1',

            ## dart: PermissionGroup.reminders
            # 'PERMISSION_REMINDERS=1',

            ## dart: PermissionGroup.contacts
            # 'PERMISSION_CONTACTS=1',

            ## dart: PermissionGroup.camera
            'PERMISSION_CAMERA=1',

            ## dart: PermissionGroup.microphone
            # 'PERMISSION_MICROPHONE=1',

            ## dart: PermissionGroup.speech
            # 'PERMISSION_SPEECH_RECOGNIZER=1',

            ## dart: PermissionGroup.photos
            # 'PERMISSION_PHOTOS=1',

            ## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse]
            'PERMISSION_LOCATION=1',

            ## dart: PermissionGroup.notification
            # 'PERMISSION_NOTIFICATIONS=1',

            ## dart: PermissionGroup.mediaLibrary
            # 'PERMISSION_MEDIA_LIBRARY=1',

            ## dart: PermissionGroup.sensors
            # 'PERMISSION_SENSORS=1',

            ## dart: PermissionGroup.bluetooth
            # 'PERMISSION_BLUETOOTH=1',

            ## dart: PermissionGroup.appTrackingTransparency
            # 'PERMISSION_APP_TRACKING_TRANSPARENCY=1',

            ## dart: PermissionGroup.criticalAlerts
            # 'PERMISSION_CRITICAL_ALERTS=1'
          ]

        end
  end
end

5/5 GeneratedPluginRegistrant.m

//
//  Generated file. Do not edit.
//

// clang-format off

#import "GeneratedPluginRegistrant.h"

#if __has_include(<device_info_plus/FLTDeviceInfoPlusPlugin.h>)
#import <device_info_plus/FLTDeviceInfoPlusPlugin.h>
#else
@import device_info_plus;
#endif

#if __has_include(<mmkvflutter/MMKVPlugin.h>)
#import <mmkvflutter/MMKVPlugin.h>
#else
@import mmkvflutter;
#endif

#if __has_include(<mobile_scanner/MobileScannerPlugin.h>)
#import <mobile_scanner/MobileScannerPlugin.h>
#else
@import mobile_scanner;
#endif

#if __has_include(<package_info_plus/FLTPackageInfoPlusPlugin.h>)
#import <package_info_plus/FLTPackageInfoPlusPlugin.h>
#else
@import package_info_plus;
#endif

#if __has_include(<path_provider_foundation/PathProviderPlugin.h>)
#import <path_provider_foundation/PathProviderPlugin.h>
#else
@import path_provider_foundation;
#endif

#if __has_include(<permission_handler_apple/PermissionHandlerPlugin.h>)
#import <permission_handler_apple/PermissionHandlerPlugin.h>
#else
@import permission_handler_apple;
#endif

#if __has_include(<shared_preferences_foundation/SharedPreferencesPlugin.h>)
#import <shared_preferences_foundation/SharedPreferencesPlugin.h>
#else
@import shared_preferences_foundation;
#endif

#if __has_include(<url_launcher_ios/FLTURLLauncherPlugin.h>)
#import <url_launcher_ios/FLTURLLauncherPlugin.h>
#else
@import url_launcher_ios;
#endif

@implementation GeneratedPluginRegistrant

+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
  [FLTDeviceInfoPlusPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTDeviceInfoPlusPlugin"]];
  [MMKVPlugin registerWithRegistrar:[registry registrarForPlugin:@"MMKVPlugin"]];
  [MobileScannerPlugin registerWithRegistrar:[registry registrarForPlugin:@"MobileScannerPlugin"]];
  [FLTPackageInfoPlusPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTPackageInfoPlusPlugin"]];
  [PathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"PathProviderPlugin"]];
  [PermissionHandlerPlugin registerWithRegistrar:[registry registrarForPlugin:@"PermissionHandlerPlugin"]];
  [SharedPreferencesPlugin registerWithRegistrar:[registry registrarForPlugin:@"SharedPreferencesPlugin"]];
  [FLTURLLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTURLLauncherPlugin"]];
}

@end
lingol commented 1 year ago

Your call stacks is not symbolized so it's impossible to investigate any further. You should use Xcode to run the application and set a ObjC exception breakpoint to catch the call stacks.

I believe when you do catch that, you could figure the problem out by yourself.

JanSob commented 1 year ago

Hmm..this is the callstack, I am not really sure why mmkv is not initialized though; it's literally the fist thing that should happen..

2023-05-25 19:19:15.420788+0200 Runner[24316:2602011] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'MMKV not initialized properly, must call +initializeMMKV: in main thread before calling any other MMKV methods'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000010ab208cb __exceptionPreprocess + 242
    1   libobjc.A.dylib                     0x000000010a7b7ba3 objc_exception_throw + 48
    2   Foundation                          0x000000010f78137c _userInfoForFileAndLine + 0
    3   MMKV                                0x000000010944e7be +[MMKV mmkvWithID:cryptKey:rootPath:mode:] + 126
    4   MMKV                                0x000000010944e4ca +[MMKV mmkvWithID:] + 58
    5   Runner                              0x00000001000f4a35 $sSo4MMKVC6mmapIDABSgSS_tcfCTO + 53
    6   Runner                              0x0000000100103f8c $sSo9OS_os_logC6RunnerE4mmkv33_ED9F07FBD337E3DDAD7A887565697ED4LL_WZ + 60
    7   libdispatch.dylib                   0x000000010b9e8f5b _dispatch_client_callout + 8
    8   libdispatch.dylib                   0x000000010b9ea5b5 _dispatch_once_callout + 66
    9   Runner                              0x0000000100103fd1 $sSo9OS_os_logC6RunnerE4mmkv33_ED9F07FBD337E3DDAD7A887565697ED4LLSo4MMKVCSgvau + 49
    10  Runner                              0x000000010010480f $sSo9OS_os_logC6RunnerE8storeLog33_ED9F07FBD337E3DDAD7A887565697ED4LL7messageySS_tFZ + 79
    11  Runner                              0x0000000100104ae4 $sSo9OS_os_logC6RunnerE0C0_AD4typeySS_ABSo0b1_c1_E2_tatFZ + 468
    12  Runner                              0x0000000100113a15 $s6Runner13BeaconMonitorC25startMonitoringIfPossible16isBackgroundModeySb_tF + 277
    13  Runner                              0x00000001000f81d8 $s6Runner11AppDelegateC11application_29didFinishLaunchingWithOptionsSbSo13UIApplicationC_SDySo0j6LaunchI3KeyaypGSgtF + 616
    14  Runner                              0x00000001000f8587 $s6Runner11AppDelegateC11application_29didFinishLaunchingWithOptionsSbSo13UIApplicationC_SDySo0j6LaunchI3KeyaypGSgtFTo + 183
    15  UIKitCore                           0x00000001260c31bb -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 271
    16  UIKitCore                           0x00000001260c505f -[UIApplication _callInitializationDelegatesWithActions:forCanvas:payload:fromOriginatingProcess:] + 4257
    17  UIKitCore                           0x00000001260cae3b -[UIApplication _runWithMainScene:transitionContext:completion:] + 1236
    18  UIKitCore                           0x00000001254fe098 -[_UISceneLifecycleMultiplexer completeApplicationLaunchWithFBSScene:transitionContext:] + 122
    19  UIKitCore                           0x0000000125b7036d _UIScenePerformActionsWithLifecycleActionMask + 88
    20  UIKitCore                           0x00000001254feb92 __101-[_UISceneLifecycleMultiplexer _evalTransitionToSettings:fromSettings:forceExit:withTransitionStore:]_block_invoke + 198
    21  UIKitCore                           0x00000001254fe5cf -[_UISceneLifecycleMultiplexer _performBlock:withApplicationOfDeactivationReasons:fromReasons:] + 252
    22  UIKitCore                           0x00000001254fe9c8 -[_UISceneLifecycleMultiplexer _evalTransitionToSettings:fromSettings:forceExit:withTransitionStore:] + 831
    23  UIKitCore                           0x00000001254fe275 -[_UISceneLifecycleMultiplexer uiScene:transitionedFromState:withTransitionContext:] + 354
    24  UIKitCore                           0x000000012550d0e1 __186-[_UIWindowSceneFBSSceneTransitionContextDrivenLifecycleSettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:]_block_invoke + 178
    25  UIKitCore                           0x0000000125a41363 +[BSAnimationSettings(UIKit) tryAnimatingWithSettings:fromCurrentState:actions:completion:] + 833
    26  UIKitCore                           0x0000000125b8e42f _UISceneSettingsDiffActionPerformChangesWithTransitionContextAndCompletion + 257
    27  UIKitCore                           0x000000012550cd61 -[_UIWindowSceneFBSSceneTransitionContextDrivenLifecycleSettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:] + 348
    28  UIKitCore                           0x00000001252eff16 __64-[UIScene scene:didUpdateWithDiff:transitionContext:completion:]_block_invoke.594 + 815
    29  UIKitCore                           0x00000001252ee92c -[UIScene _emitSceneSettingsUpdateResponseForCompletion:afterSceneUpdateWork:] + 245
    30  UIKitCore                           0x00000001252efac3 -[UIScene scene:didUpdateWithDiff:transitionContext:completion:] + 255
    31  UIKitCore                           0x00000001260c9641 -[UIApplication workspace:didCreateScene:withTransitionContext:completion:] + 733
    32  UIKitCore                           0x0000000125a73c73 -[UIApplicationSceneClientAgent scene:didInitializeWithEvent:completion:] + 350
    33  FrontBoardServices                  0x000000010ccfb41b -[FBSScene _callOutQueue_agent_didCreateWithTransitionContext:completion:] + 415
    34  FrontBoardServices                  0x000000010cd29583 __92-[FBSWorkspaceScenesClient createSceneWithIdentity:parameters:transitionContext:completion:]_block_invoke.187 + 102
    35  FrontBoardServices                  0x000000010cd0946a -[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:] + 209
    36  FrontBoardServices                  0x000000010cd29178 __92-[FBSWorkspaceScenesClient createSceneWithIdentity:parameters:transitionContext:completion:]_block_invoke + 344
    37  libdispatch.dylib                   0x000000010b9e8f5b _dispatch_client_callout + 8
    38  libdispatch.dylib                   0x000000010b9ec8d2 _dispatch_block_invoke_direct + 496
    39  FrontBoardServices                  0x000000010cd4f980 __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 30
    40  FrontBoardServices                  0x000000010cd4f876 -[FBSSerialQueue _targetQueue_performNextIfPossible] + 174
    41  FrontBoardServices                  0x000000010cd4f9a8 -[FBSSerialQueue _performNextFromRunLoopSource] + 19
    42  CoreFoundation                      0x000000010aa80035 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    43  CoreFoundation                      0x000000010aa7ff74 __CFRunLoopDoSource0 + 157
    44  CoreFoundation                      0x000000010aa7f771 __CFRunLoopDoSources0 + 212
    45  CoreFoundation                      0x000000010aa79e73 __CFRunLoopRun + 927
    46  CoreFoundation                      0x000000010aa796f7 CFRunLoopRunSpecific + 560
    47  GraphicsServices                    0x000000010cf5628a GSEventRunModal + 139
    48  UIKitCore                           0x00000001260c762b -[UIApplication _run] + 994
    49  UIKitCore                           0x00000001260cc547 UIApplicationMain + 123
    50  Runner                              0x00000001000f898f main + 63
    51  dyld                                0x00000001090eb2bf start_sim + 10
    52  ???                                 0x0000000200b4d41f 0x0 + 8601785375
)
libc++abi: terminating with uncaught exception of type NSException
Message from debugger: Terminated due to signal 6
lingol commented 1 year ago

Maybe because the so-called main function of your dart code is not actually the main function of an iOS application. You can add a log just before you called MMKV.initialize() and you should probably see it's not printed.

JanSob commented 1 year ago

Maybe because the so-called main function of your dart code is not actually the main function of an iOS application. You can add a log just before you called MMKV.initialize() and you should probably see it's not printed.

I will try to find the correct native call to MKKV.initialize and await it in 'AppDelegate'. Thank you for the hint!

JanSob commented 1 year ago

I was able to fix the bug, the App works now. If anyone ever stumbles upon this issue with the Flutter Plugin, here is a possible solution to await MMKV to initialize in AppDelegate.swift by using DispatchQueue:

import UIKit
import Flutter
import Foundation
import CoreBluetooth

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        let controller: FlutterViewController = window?.rootViewController as! FlutterViewController

        initializeMMKV {
            // additional function-calls
            ServiceSetup(controller.binaryMessenger,ServiceImpl())
            ConfigStore.shared.initializeInstanceId()
            Monitor.shared.startMonitoringIfPossible()
            TokenStore.shared.getUpdatedToken()
            GeneratedPluginRegistrant.register(with: self)
        }

        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }

     // Using DispatchQueue
    func initializeMMKV(completion: @escaping () -> Void) {
        DispatchQueue.main.async {
            MMKV.initialize(rootDir: nil) {
                completion()
            }
        }
    }
}