├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ ├── ios.yml │ ├── main.yml │ └── npm-publish.yml ├── .gitignore ├── .npmignore ├── .prettierrc.js ├── .vscode └── settings.json ├── LICENSE ├── MMKV ├── .clang-format ├── Core │ ├── CMakeLists.txt │ ├── CodedInputData.cpp │ ├── CodedInputData.h │ ├── CodedInputDataCrypt.cpp │ ├── CodedInputDataCrypt.h │ ├── CodedInputDataCrypt_OSX.cpp │ ├── CodedInputData_OSX.cpp │ ├── CodedOutputData.cpp │ ├── CodedOutputData.h │ ├── Core.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ ├── Core.xcscheme │ │ │ └── MMKVWatchCore.xcscheme │ ├── InterProcessLock.cpp │ ├── InterProcessLock.h │ ├── InterProcessLock_Android.cpp │ ├── InterProcessLock_Win32.cpp │ ├── KeyValueHolder.cpp │ ├── KeyValueHolder.h │ ├── MMBuffer.cpp │ ├── MMBuffer.h │ ├── MMKV.cpp │ ├── MMKV.h │ ├── MMKVLog.cpp │ ├── MMKVLog.h │ ├── MMKVLog_Android.cpp │ ├── MMKVMetaInfo.hpp │ ├── MMKVPredef.h │ ├── MMKV_Android.cpp │ ├── MMKV_IO.cpp │ ├── MMKV_IO.h │ ├── MMKV_OSX.cpp │ ├── MMKV_OSX.h │ ├── MemoryFile.cpp │ ├── MemoryFile.h │ ├── MemoryFile_Android.cpp │ ├── MemoryFile_Linux.cpp │ ├── MemoryFile_OSX.cpp │ ├── MemoryFile_Win32.cpp │ ├── MiniPBCoder.cpp │ ├── MiniPBCoder.h │ ├── MiniPBCoder_OSX.cpp │ ├── PBEncodeItem.hpp │ ├── PBUtility.cpp │ ├── PBUtility.h │ ├── ScopedLock.hpp │ ├── ThreadLock.cpp │ ├── ThreadLock.h │ ├── ThreadLock_Win32.cpp │ ├── aes │ │ ├── AESCrypt.cpp │ │ ├── AESCrypt.h │ │ └── openssl │ │ │ ├── openssl_aes-armv4.S │ │ │ ├── openssl_aes.h │ │ │ ├── openssl_aes_core.cpp │ │ │ ├── openssl_aes_locl.h │ │ │ ├── openssl_aesv8-armx.S │ │ │ ├── openssl_arm_arch.h │ │ │ ├── openssl_cfb128.cpp │ │ │ ├── openssl_md32_common.h │ │ │ ├── openssl_md5.h │ │ │ ├── openssl_md5_dgst.cpp │ │ │ ├── openssl_md5_locl.h │ │ │ ├── openssl_md5_one.cpp │ │ │ └── openssl_opensslconf.h │ ├── core.vcxproj │ ├── core.vcxproj.filters │ └── crc32 │ │ ├── Checksum.h │ │ ├── crc32_armv8.cpp │ │ └── zlib │ │ ├── CMakeLists.txt │ │ ├── crc32.cpp │ │ ├── crc32.h │ │ ├── zconf.h │ │ └── zutil.h └── LICENSE.TXT ├── README.md ├── android ├── .classpath ├── .project ├── build.gradle ├── generated │ ├── java │ │ └── com │ │ │ └── facebook │ │ │ └── fbreact │ │ │ └── specs │ │ │ └── NativeMMKVStorageSpec.java │ └── jni │ │ ├── CMakeLists.txt │ │ ├── MMKVStorageSpec-generated.cpp │ │ ├── MMKVStorageSpec.h │ │ └── react │ │ └── renderer │ │ └── components │ │ └── MMKVStorageSpec │ │ ├── MMKVStorageSpecJSI-generated.cpp │ │ └── MMKVStorageSpecJSI.h ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src │ ├── main │ ├── AndroidManifest.xml │ ├── AndroidManifestNew.xml │ ├── java │ │ └── com │ │ │ └── ammarahmed │ │ │ └── mmkv │ │ │ ├── Constants.java │ │ │ ├── MMKV.java │ │ │ ├── RNMMKVModule.java │ │ │ ├── RNMMKVPackage.java │ │ │ ├── SecureKeystore.java │ │ │ └── Storage.java │ └── rnmmkv │ │ ├── CMakeLists.txt │ │ └── rnmmkv-adapter.cpp │ ├── newarch │ └── MmkvStorageSpec.kt │ └── oldarch │ └── MmkvStorageSpec.kt ├── docs ├── .gitignore ├── .nojekyll ├── CNAME ├── README.md ├── _coverpage.md ├── _sidebar.md ├── asyncapi.md ├── callbackapi.md ├── changelog.md ├── creatinginstance.md ├── datatypes.md ├── encryption.md ├── flipper.md ├── generalmethods.md ├── gettingstarted.md ├── index.html ├── loaderclass.md ├── mockjest.md ├── queryingandindexing.md ├── redux-persist.md ├── transactionmanager.md ├── useindex.md ├── usemmkvref.md ├── usemmkvstorage.md └── workingwithencryption.md ├── example ├── .bundle │ └── config ├── .watchmanconfig ├── Gemfile ├── Gemfile.lock ├── README.md ├── android │ ├── app │ │ ├── build.gradle │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── mmkvstorage │ │ │ │ └── example │ │ │ │ ├── MainActivity.kt │ │ │ │ └── MainApplication.kt │ │ │ └── res │ │ │ ├── drawable │ │ │ └── rn_edit_text_material.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── app.json ├── babel.config.js ├── index.js ├── ios │ ├── .xcode.env │ ├── .xcode.env.local │ ├── File.swift │ ├── MmkvStorageExample-Bridging-Header.h │ ├── MmkvStorageExample.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── MmkvStorageExample.xcscheme │ ├── MmkvStorageExample.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── MmkvStorageExample │ │ ├── AppDelegate.h │ │ ├── AppDelegate.mm │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── LaunchScreen.storyboard │ │ ├── PrivacyInfo.xcprivacy │ │ └── main.m │ ├── MmkvStorageExampleTests │ │ ├── Info.plist │ │ └── MmkvStorageExampleTests.m │ ├── Podfile │ └── Podfile.lock ├── jest.config.js ├── metro.config.js ├── package-lock.json ├── package.json ├── react-native.config.js ├── src │ └── App.tsx └── tsconfig.json ├── index.ts ├── ios ├── MMKVStorage.h ├── MMKVStorage.mm ├── MMKVStorage.xcodeproj │ └── project.pbxproj ├── SecureStorage.h ├── SecureStorage.m ├── YeetJSIUtils.h ├── YeetJSIUtils.mm └── generated │ ├── MMKVStorageSpec │ ├── MMKVStorageSpec-generated.mm │ └── MMKVStorageSpec.h │ ├── MMKVStorageSpecJSI-generated.cpp │ └── MMKVStorageSpecJSI.h ├── jest ├── README.md ├── dist │ ├── jest │ │ ├── memoryStore.d.ts │ │ ├── memoryStore.d.ts.map │ │ └── memoryStore.js │ └── src │ │ └── types │ │ ├── index.d.ts │ │ ├── index.d.ts.map │ │ └── index.js ├── memoryStore.ts ├── mmkvJestSetup.js └── tsconfig.json ├── package-lock.json ├── package.json ├── react-native-mmkv-storage.podspec ├── react-native.config.js ├── src ├── encryption.ts ├── eventmanager.ts ├── handlers.ts ├── hooks │ ├── constants.ts │ ├── functions.ts │ ├── useIndex.ts │ ├── useMMKV.ts │ └── useMMKVRef.ts ├── indexer │ ├── arrays.ts │ ├── booleans.ts │ ├── indexer.ts │ ├── maps.ts │ ├── numbers.ts │ └── strings.ts ├── initializer.ts ├── keygen.ts ├── mmkv │ ├── IDStore.ts │ └── init.ts ├── mmkvinstance.ts ├── mmkvloader.ts ├── module │ ├── NativeMMKVStorage.ts │ └── index.ts ├── transactions.ts ├── types │ └── index.ts └── utils.ts └── tsconfig.json /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: streetwriters 5 | open_collective: # Replace with a single Open Collective username 6 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 7 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 8 | liberapay: # Replace with a single Liberapay username 9 | issuehunt: # Replace with a single IssueHunt username 10 | otechie: # Replace with a single Otechie username -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[Bug]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Platform Information:** 27 | - OS: [e.g. iOS/Android] 28 | - React Native Version [e.g. 0.64] 29 | - Library Version [e.g. 0.5.8] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/workflows/ios.yml: -------------------------------------------------------------------------------- 1 | name: iOS 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - fix-ci 7 | pull_request: 8 | paths: 9 | - '.github/workflows/ios.yml' 10 | - 'src/**' 11 | - 'ios/**' 12 | - 'MMKV/**' 13 | jobs: 14 | build: 15 | runs-on: macos-latest 16 | steps: 17 | - uses: actions/checkout@v2 18 | 19 | - run: npm ci --legacy-peer-deps 20 | - run: npm run build 21 | 22 | - run: npm ci 23 | working-directory: example 24 | 25 | - run: xcode-select -p 26 | 27 | - run: pod install 28 | working-directory: example/ios 29 | name: Install pod dependencies 30 | - name: Build iOS (debug) 31 | working-directory: example/ios 32 | run: xcodebuild -workspace MmkvStorageExample.xcworkspace -scheme MmkvStorageExample clean archive -sdk iphonesimulator -configuration Debug -UseModernBuildSystem=YES CODE_SIGNING_ALLOWED=NO 33 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Android 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - fix-ci 8 | pull_request: 9 | paths: 10 | - '.github/workflows/main.yml' 11 | - 'src/**' 12 | - 'android/**' 13 | - 'MMKV/**' 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | 22 | - run: printenv 23 | 24 | - name: Build Android apk (release) 25 | env: 26 | ANDROID_NDK_HOME: /usr/local/lib/android/sdk/ndk/26.1.10909125 27 | run: | 28 | npm ci --legacy-peer-deps 29 | npm run build 30 | cd example 31 | npm ci --legacy-peer-deps 32 | cd android 33 | ./gradlew assembleRelease 34 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | name: NPM Publish 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | publish-npm: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: actions/setup-node@v2 13 | with: 14 | node-version: 16 15 | registry-url: https://registry.npmjs.org/ 16 | - run: npm ci --legacy-peer-deps 17 | - run: npm run build 18 | - run: npm publish 19 | env: 20 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # OSX 3 | # 4 | .DS_Store 5 | 6 | # node.js 7 | # 8 | node_modules/ 9 | npm-debug.log 10 | yarn-error.log 11 | 12 | 13 | # Xcode 14 | # 15 | build/ 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | xcuserdata 25 | *.xccheckout 26 | *.moved-aside 27 | DerivedData 28 | *.hmap 29 | *.ipa 30 | *.xcuserstate 31 | project.xcworkspace 32 | Pods 33 | vendor 34 | 35 | 36 | # Android/IntelliJ 37 | # 38 | *.aar 39 | build/ 40 | .cxx 41 | .idea 42 | .gradle 43 | local.properties 44 | *.iml 45 | 46 | # BUCK 47 | buck-out/ 48 | \.buckd/ 49 | *.keystore 50 | dist -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | !dist/src 3 | !android/src 4 | .github 5 | .vscode 6 | .yarn 7 | index.ts 8 | tsconfig.json 9 | jest/tsconfig.json 10 | 11 | jest/memoryStore.ts 12 | 13 | # OSX 14 | # 15 | .DS_Store 16 | 17 | # node.js 18 | # 19 | node_modules/ 20 | npm-debug.log 21 | yarn-error.log 22 | 23 | 24 | # Xcode 25 | # 26 | build/ 27 | *.pbxuser 28 | !default.pbxuser 29 | *.mode1v3 30 | !default.mode1v3 31 | *.mode2v3 32 | !default.mode2v3 33 | *.perspectivev3 34 | !default.perspectivev3 35 | xcuserdata 36 | *.xccheckout 37 | *.moved-aside 38 | DerivedData 39 | *.hmap 40 | *.ipa 41 | *.xcuserstate 42 | project.xcworkspace 43 | 44 | 45 | # Docs 46 | docs/ 47 | 48 | # Android/IntelliJ 49 | # 50 | build/ 51 | .idea 52 | .gradle 53 | local.properties 54 | *.iml 55 | .cxx 56 | android/build 57 | 58 | # BUCK 59 | buck-out/ 60 | \.buckd/ 61 | *.keystore 62 | 63 | # Other 64 | 65 | example/ 66 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | arrowParens: "avoid", 3 | bracketSpacing: true, 4 | jsxBracketSameLine: false, 5 | jsxSingleQuote: false, 6 | quoteProps: "as-needed", 7 | singleQuote: true, 8 | semi: true, 9 | printWidth: 100, 10 | useTabs: false, 11 | tabWidth: 2, 12 | trailingComma: "none", 13 | }; 14 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cmake.sourceDirectory": "/Users/ammarahmed/Repos/react-native/react-native-mmkv-storage/MMKV/Core" 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ammar Ahmed 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MMKV/.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: LLVM 3 | IndentWidth: 4 4 | TabWidth: 4 5 | AlwaysBreakTemplateDeclarations: true 6 | AllowShortFunctionsOnASingleLine: InlineOnly 7 | #AllowShortLambdasOnASingleLine: Inline 8 | BreakAfterJavaFieldAnnotations: true 9 | BreakBeforeBraces: Linux 10 | SpaceAfterCStyleCast: true 11 | IndentCaseLabels: true 12 | AccessModifierOffset: -4 13 | BreakBeforeBraces: Custom 14 | BraceWrapping: 15 | AfterNamespace: false 16 | AfterClass: false 17 | AfterFunction: false 18 | 19 | BreakConstructorInitializersBeforeComma: true 20 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 21 | BinPackParameters: false 22 | ReflowComments: false 23 | ObjCBlockIndentWidth: 4 24 | --- 25 | Language: Cpp 26 | ColumnLimit: 120 27 | IndentPPDirectives: AfterHash 28 | --- 29 | Language: ObjC 30 | ColumnLimit: 0 31 | #UseTab: ForIndentation 32 | --- 33 | Language: Java 34 | ColumnLimit: 120 35 | AllowShortFunctionsOnASingleLine: None 36 | BreakBeforeBinaryOperators: NonAssignment 37 | -------------------------------------------------------------------------------- /MMKV/Core/CodedInputData.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making 3 | * MMKV available. 4 | * 5 | * Copyright (C) 2018 THL A29 Limited, a Tencent company. 6 | * All rights reserved. 7 | * 8 | * Licensed under the BSD 3-Clause License (the "License"); you may not use 9 | * this file except in compliance with the License. You may obtain a copy of 10 | * the License at 11 | * 12 | * https://opensource.org/licenses/BSD-3-Clause 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | #ifndef MMKV_CODEDINPUTDATA_H 22 | #define MMKV_CODEDINPUTDATA_H 23 | #ifdef __cplusplus 24 | 25 | #include "MMKVPredef.h" 26 | 27 | #include "KeyValueHolder.h" 28 | #include "MMBuffer.h" 29 | #include 30 | 31 | namespace mmkv { 32 | 33 | class CodedInputData { 34 | uint8_t *const m_ptr; 35 | size_t m_size; 36 | size_t m_position; 37 | 38 | int8_t readRawByte(); 39 | 40 | int32_t readRawVarint32(); 41 | 42 | int32_t readRawLittleEndian32(); 43 | 44 | int64_t readRawLittleEndian64(); 45 | 46 | public: 47 | CodedInputData(const void *oData, size_t length); 48 | 49 | bool isAtEnd() const { return m_position == m_size; }; 50 | 51 | void seek(size_t addedSize); 52 | 53 | bool readBool(); 54 | 55 | double readDouble(); 56 | 57 | float readFloat(); 58 | 59 | int64_t readInt64(); 60 | 61 | uint64_t readUInt64(); 62 | 63 | int32_t readInt32(); 64 | 65 | uint32_t readUInt32(); 66 | 67 | // exactly is like getValueSize(actualSize = true) 68 | MMBuffer readData(bool copy = true, bool exactly = false); 69 | void readData(KeyValueHolder &kvHolder); 70 | 71 | static MMBuffer readRealData(mmkv::MMBuffer & data); 72 | 73 | #ifndef MMKV_APPLE 74 | std::string readString(); 75 | void readString(std::string &s); 76 | std::string readString(KeyValueHolder &kvHolder); 77 | #else 78 | NSString *readString(); 79 | NSString *readString(KeyValueHolder &kvHolder); 80 | NSData *readNSData(); 81 | #endif 82 | }; 83 | 84 | } // namespace mmkv 85 | 86 | #endif 87 | #endif //MMKV_CODEDINPUTDATA_H 88 | -------------------------------------------------------------------------------- /MMKV/Core/CodedInputDataCrypt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making 3 | * MMKV available. 4 | * 5 | * Copyright (C) 2020 THL A29 Limited, a Tencent company. 6 | * All rights reserved. 7 | * 8 | * Licensed under the BSD 3-Clause License (the "License"); you may not use 9 | * this file except in compliance with the License. You may obtain a copy of 10 | * the License at 11 | * 12 | * https://opensource.org/licenses/BSD-3-Clause 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | #ifndef CodedInputDataCrypt_h 22 | #define CodedInputDataCrypt_h 23 | #ifdef __cplusplus 24 | 25 | #include "MMKVPredef.h" 26 | 27 | #include "KeyValueHolder.h" 28 | #include "MMBuffer.h" 29 | #include "aes/AESCrypt.h" 30 | #include 31 | 32 | #ifdef MMKV_DISABLE_CRYPT 33 | 34 | namespace mmkv { 35 | class CodedInputDataCrypt; 36 | } 37 | 38 | #else 39 | 40 | namespace mmkv { 41 | 42 | class CodedInputDataCrypt { 43 | uint8_t *const m_ptr; 44 | size_t m_size; 45 | size_t m_position; 46 | size_t m_decryptPosition; // position of text that has beed decrypted 47 | 48 | AESCrypt &m_decrypter; 49 | uint8_t *m_decryptBuffer; // internal decrypt buffer, grows by (n * AES_KEY_LEN) bytes 50 | size_t m_decryptBufferSize; 51 | size_t m_decryptBufferPosition; // reader position in the buffer, synced with m_position 52 | size_t m_decryptBufferDecryptLength; // length of the buffer that has been used 53 | size_t m_decryptBufferDiscardPosition; // recycle position, any data before that can be discarded 54 | 55 | void consumeBytes(size_t length, bool discardPreData = false); 56 | void skipBytes(size_t length); 57 | void statusBeforeDecrypt(size_t rollbackSize, AESCryptStatus &status); 58 | 59 | int8_t readRawByte(); 60 | 61 | int32_t readRawVarint32(bool discardPreData = false); 62 | 63 | public: 64 | CodedInputDataCrypt(const void *oData, size_t length, AESCrypt &crypt); 65 | 66 | ~CodedInputDataCrypt(); 67 | 68 | bool isAtEnd() { return m_position == m_size; }; 69 | 70 | void seek(size_t addedSize); 71 | 72 | int32_t readInt32(); 73 | 74 | void readData(KeyValueHolderCrypt &kvHolder); 75 | 76 | #ifndef MMKV_APPLE 77 | std::string readString(KeyValueHolderCrypt &kvHolder); 78 | #else 79 | NSString *readString(KeyValueHolderCrypt &kvHolder); 80 | #endif 81 | }; 82 | 83 | } // namespace mmkv 84 | 85 | #endif // MMKV_DISABLE_CRYPT 86 | #endif // __cplusplus 87 | #endif /* CodedInputDataCrypt_h */ 88 | -------------------------------------------------------------------------------- /MMKV/Core/CodedInputDataCrypt_OSX.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making 3 | * MMKV available. 4 | * 5 | * Copyright (C) 2020 THL A29 Limited, a Tencent company. 6 | * All rights reserved. 7 | * 8 | * Licensed under the BSD 3-Clause License (the "License"); you may not use 9 | * this file except in compliance with the License. You may obtain a copy of 10 | * the License at 11 | * 12 | * https://opensource.org/licenses/BSD-3-Clause 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | #include "CodedInputDataCrypt.h" 22 | 23 | #if defined(MMKV_APPLE) && !defined(MMKV_DISABLE_CRYPT) 24 | 25 | # include "PBUtility.h" 26 | # include 27 | 28 | # if __has_feature(objc_arc) 29 | # error This file must be compiled with MRC. Use -fno-objc-arc flag. 30 | # endif 31 | 32 | using namespace std; 33 | 34 | namespace mmkv { 35 | 36 | NSString *CodedInputDataCrypt::readString(KeyValueHolderCrypt &kvHolder) { 37 | kvHolder.offset = static_cast(m_position); 38 | 39 | int32_t size = this->readRawVarint32(true); 40 | if (size < 0) { 41 | throw length_error("InvalidProtocolBuffer negativeSize"); 42 | } 43 | 44 | auto s_size = static_cast(size); 45 | if (s_size <= m_size - m_position) { 46 | consumeBytes(s_size); 47 | 48 | kvHolder.keySize = static_cast(s_size); 49 | 50 | auto ptr = m_decryptBuffer + m_decryptBufferPosition; 51 | NSString *result = [[NSString alloc] initWithBytes:ptr length:s_size encoding:NSUTF8StringEncoding]; 52 | m_position += s_size; 53 | m_decryptBufferPosition += s_size; 54 | return [result autorelease]; 55 | } else { 56 | throw out_of_range("InvalidProtocolBuffer truncatedMessage"); 57 | } 58 | } 59 | 60 | } // namespace mmkv 61 | 62 | #endif // MMKV_APPLE && !MMKV_DISABLE_CRYPT 63 | -------------------------------------------------------------------------------- /MMKV/Core/CodedInputData_OSX.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making 3 | * MMKV available. 4 | * 5 | * Copyright (C) 2019 THL A29 Limited, a Tencent company. 6 | * All rights reserved. 7 | * 8 | * Licensed under the BSD 3-Clause License (the "License"); you may not use 9 | * this file except in compliance with the License. You may obtain a copy of 10 | * the License at 11 | * 12 | * https://opensource.org/licenses/BSD-3-Clause 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | #include "CodedInputData.h" 22 | 23 | #ifdef MMKV_APPLE 24 | 25 | # include "PBUtility.h" 26 | # include 27 | 28 | # if __has_feature(objc_arc) 29 | # error This file must be compiled with MRC. Use -fno-objc-arc flag. 30 | # endif 31 | 32 | using namespace std; 33 | 34 | namespace mmkv { 35 | 36 | NSString *CodedInputData::readString() { 37 | int32_t size = this->readRawVarint32(); 38 | if (size < 0) { 39 | throw length_error("InvalidProtocolBuffer negativeSize"); 40 | } 41 | 42 | auto s_size = static_cast(size); 43 | if (s_size <= m_size - m_position) { 44 | auto ptr = m_ptr + m_position; 45 | NSString *result = [[NSString alloc] initWithBytes:ptr length:s_size encoding:NSUTF8StringEncoding]; 46 | m_position += s_size; 47 | return [result autorelease]; 48 | } else { 49 | throw out_of_range("InvalidProtocolBuffer truncatedMessage"); 50 | } 51 | } 52 | 53 | NSString *CodedInputData::readString(KeyValueHolder &kvHolder) { 54 | kvHolder.offset = static_cast(m_position); 55 | 56 | int32_t size = this->readRawVarint32(); 57 | if (size < 0) { 58 | throw length_error("InvalidProtocolBuffer negativeSize"); 59 | } 60 | 61 | auto s_size = static_cast(size); 62 | if (s_size <= m_size - m_position) { 63 | kvHolder.keySize = static_cast(s_size); 64 | 65 | auto ptr = m_ptr + m_position; 66 | NSString *result = [[NSString alloc] initWithBytes:ptr length:s_size encoding:NSUTF8StringEncoding]; 67 | m_position += s_size; 68 | return [result autorelease]; 69 | } else { 70 | throw out_of_range("InvalidProtocolBuffer truncatedMessage"); 71 | } 72 | } 73 | 74 | NSData *CodedInputData::readNSData() { 75 | int32_t size = this->readRawVarint32(); 76 | if (size < 0) { 77 | throw length_error("InvalidProtocolBuffer negativeSize"); 78 | } 79 | 80 | auto s_size = static_cast(size); 81 | if (s_size <= m_size - m_position) { 82 | NSData *result = [NSData dataWithBytes:(m_ptr + m_position) length:s_size]; 83 | m_position += s_size; 84 | return result; 85 | } else { 86 | throw out_of_range("InvalidProtocolBuffer truncatedMessage"); 87 | } 88 | } 89 | 90 | } // namespace mmkv 91 | 92 | #endif // MMKV_APPLE 93 | -------------------------------------------------------------------------------- /MMKV/Core/CodedOutputData.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making 3 | * MMKV available. 4 | * 5 | * Copyright (C) 2018 THL A29 Limited, a Tencent company. 6 | * All rights reserved. 7 | * 8 | * Licensed under the BSD 3-Clause License (the "License"); you may not use 9 | * this file except in compliance with the License. You may obtain a copy of 10 | * the License at 11 | * 12 | * https://opensource.org/licenses/BSD-3-Clause 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | #ifndef MMKV_CODEDOUTPUTDATA_H 22 | #define MMKV_CODEDOUTPUTDATA_H 23 | #ifdef __cplusplus 24 | 25 | #include "MMKVPredef.h" 26 | 27 | #include "MMBuffer.h" 28 | #include 29 | 30 | namespace mmkv { 31 | 32 | class CodedOutputData { 33 | uint8_t *const m_ptr; 34 | size_t m_size; 35 | size_t m_position; 36 | 37 | public: 38 | CodedOutputData(void *ptr, size_t len); 39 | 40 | size_t spaceLeft(); 41 | 42 | uint8_t *curWritePointer(); 43 | 44 | void seek(size_t addedSize); 45 | 46 | void reset(); 47 | 48 | size_t getPosition(); 49 | 50 | void setPosition(size_t position); 51 | 52 | void writeRawByte(uint8_t value); 53 | 54 | void writeRawLittleEndian32(int32_t value); 55 | 56 | void writeRawLittleEndian64(int64_t value); 57 | 58 | void writeRawVarint32(int32_t value); 59 | 60 | void writeRawVarint64(int64_t value); 61 | 62 | void writeRawData(const MMBuffer &data); 63 | 64 | void writeDouble(double value); 65 | 66 | void writeFloat(float value); 67 | 68 | void writeInt64(int64_t value); 69 | 70 | void writeUInt64(uint64_t value); 71 | 72 | void writeInt32(int32_t value); 73 | 74 | void writeUInt32(uint32_t value); 75 | 76 | void writeBool(bool value); 77 | 78 | void writeData(const MMBuffer &value); 79 | 80 | #ifndef MMKV_APPLE 81 | void writeString(const std::string &value); 82 | #endif 83 | }; 84 | 85 | } // namespace mmkv 86 | 87 | #endif 88 | #endif //MMKV_CODEDOUTPUTDATA_H 89 | -------------------------------------------------------------------------------- /MMKV/Core/Core.xcodeproj/xcshareddata/xcschemes/Core.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 57 | 58 | 59 | 60 | 62 | 63 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /MMKV/Core/Core.xcodeproj/xcshareddata/xcschemes/MMKVWatchCore.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 57 | 58 | 59 | 60 | 62 | 63 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /MMKV/Core/MMKVMetaInfo.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making 3 | * MMKV available. 4 | * 5 | * Copyright (C) 2018 THL A29 Limited, a Tencent company. 6 | * All rights reserved. 7 | * 8 | * Licensed under the BSD 3-Clause License (the "License"); you may not use 9 | * this file except in compliance with the License. You may obtain a copy of 10 | * the License at 11 | * 12 | * https://opensource.org/licenses/BSD-3-Clause 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | #ifndef MMKV_MMKVMETAINFO_H 22 | #define MMKV_MMKVMETAINFO_H 23 | #ifdef __cplusplus 24 | 25 | #include "aes/AESCrypt.h" 26 | #include 27 | #include 28 | 29 | namespace mmkv { 30 | 31 | enum MMKVVersion : uint32_t { 32 | MMKVVersionDefault = 0, 33 | 34 | // record full write back count 35 | MMKVVersionSequence = 1, 36 | 37 | // store random iv for encryption 38 | MMKVVersionRandomIV = 2, 39 | 40 | // store actual size together with crc checksum, try to reduce file corruption 41 | MMKVVersionActualSize = 3, 42 | 43 | // store extra flags 44 | MMKVVersionFlag = 4, 45 | 46 | // preserved for next use 47 | MMKVVersionNext = 5, 48 | 49 | // always large than next, a placeholder for error check 50 | MMKVVersionHolder = MMKVVersionNext + 1, 51 | }; 52 | 53 | struct MMKVMetaInfo { 54 | uint32_t m_crcDigest = 0; 55 | uint32_t m_version = MMKVVersionSequence; 56 | uint32_t m_sequence = 0; // full write-back count 57 | uint8_t m_vector[AES_KEY_LEN] = {}; 58 | uint32_t m_actualSize = 0; 59 | 60 | // confirmed info: it's been synced to file 61 | struct { 62 | uint32_t lastActualSize = 0; 63 | uint32_t lastCRCDigest = 0; 64 | uint32_t _reserved[16] = {}; 65 | } m_lastConfirmedMetaInfo; 66 | 67 | uint64_t m_flags = 0; 68 | 69 | enum MMKVMetaInfoFlag : uint64_t { 70 | EnableKeyExipre = 1 << 0, 71 | }; 72 | bool hasFlag(MMKVMetaInfoFlag flag) { return (m_flags & flag) != 0; } 73 | void setFlag(MMKVMetaInfoFlag flag) { m_flags |= flag; } 74 | void unsetFlag(MMKVMetaInfoFlag flag) { m_flags &= ~flag; } 75 | 76 | void write(void *ptr) const { 77 | MMKV_ASSERT(ptr); 78 | memcpy(ptr, this, sizeof(MMKVMetaInfo)); 79 | } 80 | 81 | void writeCRCAndActualSizeOnly(void *ptr) const { 82 | MMKV_ASSERT(ptr); 83 | auto other = (MMKVMetaInfo *) ptr; 84 | other->m_crcDigest = m_crcDigest; 85 | other->m_actualSize = m_actualSize; 86 | } 87 | 88 | void read(const void *ptr) { 89 | MMKV_ASSERT(ptr); 90 | memcpy(this, ptr, sizeof(MMKVMetaInfo)); 91 | } 92 | }; 93 | 94 | static_assert(sizeof(MMKVMetaInfo) <= (4 * 1024), "MMKVMetaInfo lager than one pagesize"); 95 | 96 | } // namespace mmkv 97 | 98 | #endif 99 | #endif //MMKV_MMKVMETAINFO_H 100 | -------------------------------------------------------------------------------- /MMKV/Core/MMKV_IO.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making 3 | * MMKV available. 4 | * 5 | * Copyright (C) 2020 THL A29 Limited, a Tencent company. 6 | * All rights reserved. 7 | * 8 | * Licensed under the BSD 3-Clause License (the "License"); you may not use 9 | * this file except in compliance with the License. You may obtain a copy of 10 | * the License at 11 | * 12 | * https://opensource.org/licenses/BSD-3-Clause 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | #ifndef MMKV_IO_h 22 | #define MMKV_IO_h 23 | #ifdef __cplusplus 24 | 25 | #include "MMKV.h" 26 | 27 | MMKV_NAMESPACE_BEGIN 28 | 29 | std::string mmapedKVKey(const std::string &mmapID, const MMKVPath_t *rootPath = nullptr); 30 | MMKVPath_t mappedKVPathWithID(const std::string &mmapID, MMKVMode mode, const MMKVPath_t *rootPath); 31 | MMKVPath_t crcPathWithID(const std::string &mmapID, MMKVMode mode, const MMKVPath_t *rootPath); 32 | 33 | MMKVRecoverStrategic onMMKVCRCCheckFail(const std::string &mmapID); 34 | MMKVRecoverStrategic onMMKVFileLengthError(const std::string &mmapID); 35 | 36 | template 37 | void clearDictionary(T *dic) { 38 | if (!dic) { 39 | return; 40 | } 41 | #ifdef MMKV_APPLE 42 | for (auto &pair : *dic) { 43 | [pair.first release]; 44 | } 45 | #endif 46 | dic->clear(); 47 | } 48 | 49 | enum : bool { 50 | KeepSequence = false, 51 | IncreaseSequence = true, 52 | }; 53 | 54 | MMKV_NAMESPACE_END 55 | 56 | #endif 57 | #endif /* MMKV_IO_h */ 58 | -------------------------------------------------------------------------------- /MMKV/Core/MMKV_OSX.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making 3 | * MMKV available. 4 | * 5 | * Copyright (C) 2019 THL A29 Limited, a Tencent company. 6 | * All rights reserved. 7 | * 8 | * Licensed under the BSD 3-Clause License (the "License"); you may not use 9 | * this file except in compliance with the License. You may obtain a copy of 10 | * the License at 11 | * 12 | * https://opensource.org/licenses/BSD-3-Clause 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | #pragma once 22 | #include "MMKVPredef.h" 23 | 24 | MMKV_NAMESPACE_BEGIN 25 | 26 | #if defined(MMKV_IOS) && defined(__cplusplus) 27 | 28 | class MLockPtr { 29 | size_t m_lockDownSize; 30 | uint8_t *m_lockedPtr; 31 | 32 | public: 33 | MLockPtr(void *ptr, size_t size); 34 | MLockPtr(MLockPtr &&other); 35 | 36 | ~MLockPtr(); 37 | 38 | bool isLocked() const { 39 | return (m_lockedPtr != nullptr); 40 | } 41 | 42 | static bool isMLockPtrEnabled; 43 | 44 | // just forbid it for possibly misuse 45 | explicit MLockPtr(const MLockPtr &other) = delete; 46 | MLockPtr &operator=(const MLockPtr &other) = delete; 47 | }; 48 | 49 | std::pair guardForBackgroundWriting(void *ptr, size_t size); 50 | 51 | #endif 52 | 53 | enum { UnKnown = 0, PowerMac = 1, Mac, iPhone, iPod, iPad, AppleTV, AppleWatch }; 54 | 55 | void GetAppleMachineInfo(int &device, int &version); 56 | 57 | MMKV_NAMESPACE_END 58 | -------------------------------------------------------------------------------- /MMKV/Core/PBEncodeItem.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making 3 | * MMKV available. 4 | * 5 | * Copyright (C) 2018 THL A29 Limited, a Tencent company. 6 | * All rights reserved. 7 | * 8 | * Licensed under the BSD 3-Clause License (the "License"); you may not use 9 | * this file except in compliance with the License. You may obtain a copy of 10 | * the License at 11 | * 12 | * https://opensource.org/licenses/BSD-3-Clause 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | #ifndef MMKV_PBENCODEITEM_HPP 22 | #define MMKV_PBENCODEITEM_HPP 23 | #ifdef __cplusplus 24 | 25 | #include "MMKVPredef.h" 26 | 27 | #include "MMBuffer.h" 28 | #include 29 | #include 30 | 31 | namespace mmkv { 32 | 33 | enum PBEncodeItemType { 34 | PBEncodeItemType_None, 35 | PBEncodeItemType_Data, 36 | PBEncodeItemType_Container, 37 | #ifndef MMKV_APPLE 38 | PBEncodeItemType_String, 39 | #ifdef MMKV_HAS_CPP20 40 | PBEncodeItemType_Int32, 41 | PBEncodeItemType_UInt32, 42 | PBEncodeItemType_Int64, 43 | PBEncodeItemType_UInt64, 44 | // PBEncodeItemType_Bool, 45 | // PBEncodeItemType_Float, 46 | // PBEncodeItemType_Double, 47 | #endif // MMKV_HAS_CPP20 48 | #else 49 | PBEncodeItemType_NSString, 50 | PBEncodeItemType_NSData, 51 | PBEncodeItemType_NSDate, 52 | #endif 53 | }; 54 | 55 | struct PBEncodeItem { 56 | PBEncodeItemType type; 57 | uint32_t compiledSize; 58 | uint32_t valueSize; 59 | union { 60 | const MMBuffer *bufferValue; 61 | #ifndef MMKV_APPLE 62 | #ifdef MMKV_HAS_CPP20 63 | // bool boolValue; 64 | int32_t int32Value; 65 | int64_t int64Value; 66 | uint32_t uint32Value; 67 | uint64_t uint64Value; 68 | #endif // MMKV_HAS_CPP20 69 | // float floatValue; 70 | // double doubleValue; 71 | const std::string *strValue; 72 | #else 73 | void *objectValue; 74 | void *tmpObjectValue; // this object should be released on dealloc 75 | #endif 76 | } value; 77 | 78 | PBEncodeItem() : type(PBEncodeItemType_None), compiledSize(0), valueSize(0) { memset(&value, 0, sizeof(value)); } 79 | 80 | #ifndef MMKV_APPLE 81 | // opt std::vector.push_back() on slow_path 82 | PBEncodeItem(PBEncodeItem &&other) = default; 83 | #else 84 | // opt std::vector.push_back() on slow_path 85 | PBEncodeItem(PBEncodeItem &&other) 86 | : type(other.type), compiledSize(other.compiledSize), valueSize(other.valueSize), value(other.value) { 87 | // omit unnecessary CFRetain() & CFRelease() 88 | other.type = PBEncodeItemType_None; 89 | } 90 | 91 | ~PBEncodeItem() { 92 | if (type == PBEncodeItemType_NSString) { 93 | if (value.tmpObjectValue) { 94 | CFRelease(value.tmpObjectValue); 95 | } 96 | } 97 | } 98 | #endif // MMKV_APPLE 99 | }; 100 | 101 | } // namespace mmkv 102 | 103 | #endif 104 | #endif //MMKV_PBENCODEITEM_HPP 105 | -------------------------------------------------------------------------------- /MMKV/Core/PBUtility.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making 3 | * MMKV available. 4 | * 5 | * Copyright (C) 2018 THL A29 Limited, a Tencent company. 6 | * All rights reserved. 7 | * 8 | * Licensed under the BSD 3-Clause License (the "License"); you may not use 9 | * this file except in compliance with the License. You may obtain a copy of 10 | * the License at 11 | * 12 | * https://opensource.org/licenses/BSD-3-Clause 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | #include "MMBuffer.h" 21 | #include "PBUtility.h" 22 | 23 | namespace mmkv { 24 | 25 | uint32_t pbRawVarint32Size(uint32_t value) { 26 | if ((value & (0xffffffff << 7)) == 0) { 27 | return 1; 28 | } else if ((value & (0xffffffff << 14)) == 0) { 29 | return 2; 30 | } else if ((value & (0xffffffff << 21)) == 0) { 31 | return 3; 32 | } else if ((value & (0xffffffff << 28)) == 0) { 33 | return 4; 34 | } 35 | return 5; 36 | } 37 | 38 | uint32_t pbUInt64Size(uint64_t value) { 39 | if ((value & (0xffffffffffffffffL << 7)) == 0) { 40 | return 1; 41 | } else if ((value & (0xffffffffffffffffL << 14)) == 0) { 42 | return 2; 43 | } else if ((value & (0xffffffffffffffffL << 21)) == 0) { 44 | return 3; 45 | } else if ((value & (0xffffffffffffffffL << 28)) == 0) { 46 | return 4; 47 | } else if ((value & (0xffffffffffffffffL << 35)) == 0) { 48 | return 5; 49 | } else if ((value & (0xffffffffffffffffL << 42)) == 0) { 50 | return 6; 51 | } else if ((value & (0xffffffffffffffffL << 49)) == 0) { 52 | return 7; 53 | } else if ((value & (0xffffffffffffffffL << 56)) == 0) { 54 | return 8; 55 | } else if ((value & (0xffffffffffffffffL << 63)) == 0) { 56 | return 9; 57 | } 58 | return 10; 59 | } 60 | 61 | } // namespace mmkv 62 | -------------------------------------------------------------------------------- /MMKV/Core/ScopedLock.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making 3 | * MMKV available. 4 | * 5 | * Copyright (C) 2018 THL A29 Limited, a Tencent company. 6 | * All rights reserved. 7 | * 8 | * Licensed under the BSD 3-Clause License (the "License"); you may not use 9 | * this file except in compliance with the License. You may obtain a copy of 10 | * the License at 11 | * 12 | * https://opensource.org/licenses/BSD-3-Clause 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | #ifndef MMKV_SCOPEDLOCK_HPP 22 | #define MMKV_SCOPEDLOCK_HPP 23 | #ifdef __cplusplus 24 | 25 | namespace mmkv { 26 | 27 | template 28 | class ScopedLock { 29 | T *m_lock; 30 | 31 | void lock() { 32 | if (m_lock) { 33 | m_lock->lock(); 34 | } 35 | } 36 | 37 | void unlock() { 38 | if (m_lock) { 39 | m_lock->unlock(); 40 | } 41 | } 42 | 43 | public: 44 | explicit ScopedLock(T *oLock) : m_lock(oLock) { 45 | MMKV_ASSERT(m_lock); 46 | lock(); 47 | } 48 | 49 | ~ScopedLock() { 50 | unlock(); 51 | m_lock = nullptr; 52 | } 53 | 54 | // just forbid it for possibly misuse 55 | explicit ScopedLock(const ScopedLock &other) = delete; 56 | ScopedLock &operator=(const ScopedLock &other) = delete; 57 | }; 58 | 59 | } // namespace mmkv 60 | 61 | #include 62 | 63 | #define SCOPED_LOCK(lock) _SCOPEDLOCK(lock, __COUNTER__) 64 | #define _SCOPEDLOCK(lock, counter) __SCOPEDLOCK(lock, counter) 65 | #define __SCOPEDLOCK(lock, counter) \ 66 | mmkv::ScopedLock::type> __scopedLock##counter(lock) 67 | 68 | #endif 69 | #endif //MMKV_SCOPEDLOCK_HPP 70 | -------------------------------------------------------------------------------- /MMKV/Core/ThreadLock.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making 3 | * MMKV available. 4 | * 5 | * Copyright (C) 2018 THL A29 Limited, a Tencent company. 6 | * All rights reserved. 7 | * 8 | * Licensed under the BSD 3-Clause License (the "License"); you may not use 9 | * this file except in compliance with the License. You may obtain a copy of 10 | * the License at 11 | * 12 | * https://opensource.org/licenses/BSD-3-Clause 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | #include "ThreadLock.h" 22 | #include "MMKVLog.h" 23 | 24 | #if MMKV_USING_PTHREAD 25 | 26 | using namespace std; 27 | 28 | namespace mmkv { 29 | 30 | ThreadLock::ThreadLock() : m_lock({}) { 31 | pthread_mutexattr_t attr; 32 | pthread_mutexattr_init(&attr); 33 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 34 | 35 | pthread_mutex_init(&m_lock, &attr); 36 | 37 | pthread_mutexattr_destroy(&attr); 38 | } 39 | 40 | ThreadLock::~ThreadLock() { 41 | pthread_mutex_unlock(&m_lock); 42 | 43 | pthread_mutex_destroy(&m_lock); 44 | } 45 | 46 | void ThreadLock::lock() { 47 | auto ret = pthread_mutex_lock(&m_lock); 48 | if (ret != 0) { 49 | MMKVError("fail to lock %p, ret=%d, errno=%s", &m_lock, ret, strerror(errno)); 50 | } 51 | } 52 | 53 | void ThreadLock::unlock() { 54 | auto ret = pthread_mutex_unlock(&m_lock); 55 | if (ret != 0) { 56 | MMKVError("fail to unlock %p, ret=%d, errno=%s", &m_lock, ret, strerror(errno)); 57 | } 58 | } 59 | 60 | bool ThreadLock::try_lock() { 61 | auto ret = pthread_mutex_trylock(&m_lock); 62 | return (ret == 0); 63 | } 64 | 65 | void ThreadLock::initialize() { 66 | return; 67 | } 68 | 69 | void ThreadLock::ThreadOnce(ThreadOnceToken_t *onceToken, void (*callback)()) { 70 | pthread_once(onceToken, callback); 71 | } 72 | 73 | } // namespace mmkv 74 | 75 | #endif // MMKV_USING_PTHREAD 76 | -------------------------------------------------------------------------------- /MMKV/Core/ThreadLock.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making 3 | * MMKV available. 4 | * 5 | * Copyright (C) 2018 THL A29 Limited, a Tencent company. 6 | * All rights reserved. 7 | * 8 | * Licensed under the BSD 3-Clause License (the "License"); you may not use 9 | * this file except in compliance with the License. You may obtain a copy of 10 | * the License at 11 | * 12 | * https://opensource.org/licenses/BSD-3-Clause 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | #ifndef MMKV_THREADLOCK_H 22 | #define MMKV_THREADLOCK_H 23 | 24 | #ifdef __cplusplus 25 | 26 | #include "MMKVPredef.h" 27 | 28 | #ifndef MMKV_WIN32 29 | # include 30 | # define MMKV_USING_PTHREAD 1 31 | #endif 32 | 33 | #ifndef MMKV_USING_PTHREAD 34 | # include 35 | #endif 36 | 37 | namespace mmkv { 38 | 39 | #if MMKV_USING_PTHREAD 40 | # define ThreadOnceToken_t pthread_once_t 41 | # define ThreadOnceUninitialized PTHREAD_ONCE_INIT 42 | #else 43 | enum ThreadOnceTokenEnum : int32_t { ThreadOnceUninitialized = 0, ThreadOnceInitializing, ThreadOnceInitialized }; 44 | using ThreadOnceToken_t = std::atomic; 45 | #endif 46 | 47 | class ThreadLock { 48 | #if MMKV_USING_PTHREAD 49 | pthread_mutex_t m_lock; 50 | #else 51 | CRITICAL_SECTION m_lock; 52 | #endif 53 | 54 | public: 55 | ThreadLock(); 56 | ~ThreadLock(); 57 | 58 | void initialize(); 59 | 60 | void lock(); 61 | void unlock(); 62 | 63 | #ifndef MMKV_WIN32 64 | bool try_lock(); 65 | #endif 66 | 67 | static void ThreadOnce(ThreadOnceToken_t *onceToken, void (*callback)(void)); 68 | 69 | #ifdef MMKV_WIN32 70 | static void Sleep(int ms); 71 | #endif 72 | 73 | // just forbid it for possibly misuse 74 | explicit ThreadLock(const ThreadLock &other) = delete; 75 | ThreadLock &operator=(const ThreadLock &other) = delete; 76 | }; 77 | 78 | } // namespace mmkv 79 | 80 | #endif 81 | #endif //MMKV_THREADLOCK_H 82 | -------------------------------------------------------------------------------- /MMKV/Core/ThreadLock_Win32.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making 3 | * MMKV available. 4 | * 5 | * Copyright (C) 2019 THL A29 Limited, a Tencent company. 6 | * All rights reserved. 7 | * 8 | * Licensed under the BSD 3-Clause License (the "License"); you may not use 9 | * this file except in compliance with the License. You may obtain a copy of 10 | * the License at 11 | * 12 | * https://opensource.org/licenses/BSD-3-Clause 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | #include "ThreadLock.h" 22 | 23 | #if !(MMKV_USING_PTHREAD) 24 | 25 | # include "MMKVLog.h" 26 | # include 27 | # include 28 | 29 | namespace mmkv { 30 | 31 | ThreadLock::ThreadLock() : m_lock{0} { 32 | } 33 | 34 | ThreadLock::~ThreadLock() { 35 | DeleteCriticalSection(&m_lock); 36 | } 37 | 38 | void ThreadLock::initialize() { 39 | // TODO: a better spin count? 40 | if (!InitializeCriticalSectionAndSpinCount(&m_lock, 1024)) { 41 | MMKVError("fail to init critical section:%d", GetLastError()); 42 | } 43 | } 44 | 45 | void ThreadLock::lock() { 46 | EnterCriticalSection(&m_lock); 47 | } 48 | 49 | void ThreadLock::unlock() { 50 | LeaveCriticalSection(&m_lock); 51 | } 52 | 53 | void ThreadLock::ThreadOnce(ThreadOnceToken_t *onceToken, void (*callback)()) { 54 | if (!onceToken || !callback) { 55 | assert(onceToken); 56 | assert(callback); 57 | return; 58 | } 59 | while (true) { 60 | auto expected = ThreadOnceUninitialized; 61 | atomic_compare_exchange_weak(onceToken, &expected, ThreadOnceInitializing); 62 | switch (expected) { 63 | case ThreadOnceInitialized: 64 | return; 65 | case ThreadOnceUninitialized: 66 | callback(); 67 | onceToken->store(ThreadOnceInitialized); 68 | return; 69 | case ThreadOnceInitializing: { 70 | // another thread is initializing, let's wait for 1ms 71 | ThreadLock::Sleep(1); 72 | break; 73 | } 74 | default: { 75 | MMKVError("should never happen:%d", expected); 76 | assert(0); 77 | return; 78 | } 79 | } 80 | } 81 | } 82 | 83 | void ThreadLock::Sleep(int ms) { 84 | ::Sleep(ms); 85 | } 86 | 87 | } // namespace mmkv 88 | 89 | #endif // MMKV_USING_PTHREAD 90 | -------------------------------------------------------------------------------- /MMKV/Core/aes/AESCrypt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making 3 | * MMKV available. 4 | * 5 | * Copyright (C) 2018 THL A29 Limited, a Tencent company. 6 | * All rights reserved. 7 | * 8 | * Licensed under the BSD 3-Clause License (the "License"); you may not use 9 | * this file except in compliance with the License. You may obtain a copy of 10 | * the License at 11 | * 12 | * https://opensource.org/licenses/BSD-3-Clause 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | #ifndef AES_CRYPT_H_ 22 | #define AES_CRYPT_H_ 23 | #ifdef __cplusplus 24 | 25 | #include "../MMKVPredef.h" 26 | #include 27 | #include 28 | 29 | #ifdef MMKV_DISABLE_CRYPT 30 | 31 | namespace mmkv { 32 | class AESCrypt; 33 | } 34 | 35 | #else 36 | 37 | namespace openssl { 38 | struct AES_KEY; 39 | } 40 | 41 | namespace mmkv { 42 | 43 | #pragma pack(push, 1) 44 | 45 | struct AESCryptStatus { 46 | uint8_t m_number; 47 | uint8_t m_vector[AES_KEY_LEN]; 48 | }; 49 | 50 | #pragma pack(pop) 51 | 52 | class CodedInputDataCrypt; 53 | 54 | // a AES CFB-128 encrypt-decrypt full-duplex wrapper 55 | class AESCrypt { 56 | bool m_isClone = false; 57 | uint32_t m_number = 0; 58 | openssl::AES_KEY *m_aesKey = nullptr; 59 | openssl::AES_KEY *m_aesRollbackKey = nullptr; 60 | uint8_t m_key[AES_KEY_LEN] = {}; 61 | 62 | public: 63 | uint8_t m_vector[AES_KEY_LEN] = {}; 64 | 65 | private: 66 | // for cloneWithStatus() 67 | AESCrypt(const AESCrypt &other, const AESCryptStatus &status); 68 | 69 | public: 70 | AESCrypt(const void *key, size_t keyLength, const void *iv = nullptr, size_t ivLength = 0); 71 | AESCrypt(AESCrypt &&other) = default; 72 | 73 | ~AESCrypt(); 74 | 75 | void encrypt(const void *input, void *output, size_t length); 76 | 77 | void decrypt(const void *input, void *output, size_t length); 78 | 79 | void getCurStatus(AESCryptStatus &status); 80 | void statusBeforeDecrypt(const void *input, const void *output, size_t length, AESCryptStatus &status); 81 | 82 | AESCrypt cloneWithStatus(const AESCryptStatus &status) const; 83 | 84 | void resetIV(const void *iv = nullptr, size_t ivLength = 0); 85 | void resetStatus(const AESCryptStatus &status); 86 | 87 | // output must have [AES_KEY_LEN] space 88 | void getKey(void *output) const; 89 | 90 | static void fillRandomIV(void *vector); 91 | static uint32_t randomItemSizeHolder(uint32_t size); 92 | 93 | // just forbid it for possibly misuse 94 | explicit AESCrypt(const AESCrypt &other) = delete; 95 | AESCrypt &operator=(const AESCrypt &other) = delete; 96 | 97 | friend CodedInputDataCrypt; 98 | 99 | #ifdef MMKV_DEBUG 100 | // check if AESCrypt is encrypt-decrypt full-duplex 101 | static void testAESCrypt(); 102 | #endif 103 | }; 104 | 105 | } // namespace mmkv 106 | 107 | #endif // MMKV_DISABLE_CRYPT 108 | #endif // __cplusplus 109 | #endif /* AES_CRYPT_H_ */ 110 | -------------------------------------------------------------------------------- /MMKV/Core/aes/openssl/openssl_aes_locl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2016 The OpenSSL Project Authors. All Rights Reserved. 3 | * 4 | * Licensed under the OpenSSL license (the "License"). You may not use 5 | * this file except in compliance with the License. You can obtain a copy 6 | * in the file LICENSE in the source distribution or at 7 | * https://www.openssl.org/source/license.html 8 | */ 9 | 10 | #ifndef HEADER_AES_LOCL_H 11 | # define HEADER_AES_LOCL_H 12 | #ifdef __cplusplus 13 | 14 | # include "openssl_opensslconf.h" 15 | # include 16 | # include 17 | # include 18 | # include 19 | 20 | # if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64) || defined(_M_X64)) 21 | # define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00) 22 | # define GETU32(p) SWAP(*((u32 *)(p))) 23 | # define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); } 24 | # else 25 | # define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) 26 | # define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } 27 | # endif 28 | 29 | # ifdef AES_LONG 30 | typedef unsigned long u32; 31 | # else 32 | typedef unsigned int u32; 33 | # endif 34 | typedef unsigned short u16; 35 | typedef unsigned char u8; 36 | 37 | #endif 38 | #endif /* !HEADER_AES_LOCL_H */ 39 | -------------------------------------------------------------------------------- /MMKV/Core/aes/openssl/openssl_arm_arch.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2018 The OpenSSL Project Authors. All Rights Reserved. 3 | * 4 | * Licensed under the OpenSSL license (the "License"). You may not use 5 | * this file except in compliance with the License. You can obtain a copy 6 | * in the file LICENSE in the source distribution or at 7 | * https://www.openssl.org/source/license.html 8 | */ 9 | 10 | #ifndef __ARM_ARCH_H__ 11 | # define __ARM_ARCH_H__ 12 | 13 | # if !defined(__ARM_ARCH__) 14 | # if defined(__CC_ARM) 15 | # define __ARM_ARCH__ __TARGET_ARCH_ARM 16 | # if defined(__BIG_ENDIAN) 17 | # define __ARMEB__ 18 | # else 19 | # define __ARMEL__ 20 | # endif 21 | # elif defined(__GNUC__) 22 | # if defined(__aarch64__) 23 | # define __ARM_ARCH__ 8 24 | # if __BYTE_ORDER__==__ORDER_BIG_ENDIAN__ 25 | # define __ARMEB__ 26 | # else 27 | # define __ARMEL__ 28 | # endif 29 | /* 30 | * Why doesn't gcc define __ARM_ARCH__? Instead it defines 31 | * bunch of below macros. See all_architectires[] table in 32 | * gcc/config/arm/arm.c. On a side note it defines 33 | * __ARMEL__/__ARMEB__ for little-/big-endian. 34 | */ 35 | # elif defined(__ARM_ARCH) 36 | # define __ARM_ARCH__ __ARM_ARCH 37 | # elif defined(__ARM_ARCH_8A__) 38 | # define __ARM_ARCH__ 8 39 | # elif defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || \ 40 | defined(__ARM_ARCH_7R__)|| defined(__ARM_ARCH_7M__) || \ 41 | defined(__ARM_ARCH_7EM__) || defined(__ARM_ARCH_7K__) 42 | # define __ARM_ARCH__ 7 43 | # elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || \ 44 | defined(__ARM_ARCH_6K__)|| defined(__ARM_ARCH_6M__) || \ 45 | defined(__ARM_ARCH_6Z__)|| defined(__ARM_ARCH_6ZK__) || \ 46 | defined(__ARM_ARCH_6T2__) 47 | # define __ARM_ARCH__ 6 48 | # elif defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5T__) || \ 49 | defined(__ARM_ARCH_5E__)|| defined(__ARM_ARCH_5TE__) || \ 50 | defined(__ARM_ARCH_5TEJ__) 51 | # define __ARM_ARCH__ 5 52 | # elif defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__) 53 | # define __ARM_ARCH__ 4 54 | # else 55 | //# error "unsupported ARM architecture" 56 | # define __ARM_ARCH__ 0 57 | # endif 58 | # endif 59 | # endif 60 | 61 | # if !defined(__ARM_MAX_ARCH__) 62 | # define __ARM_MAX_ARCH__ __ARM_ARCH__ 63 | # endif 64 | 65 | # if __ARM_MAX_ARCH__<__ARM_ARCH__ 66 | # error "__ARM_MAX_ARCH__ can't be less than __ARM_ARCH__" 67 | # elif __ARM_MAX_ARCH__!=__ARM_ARCH__ 68 | # if __ARM_ARCH__<7 && __ARM_MAX_ARCH__>=7 && defined(__ARMEB__) 69 | # error "can't build universal big-endian binary" 70 | # endif 71 | # endif 72 | 73 | # ifndef __ASSEMBLER__ 74 | extern unsigned int OPENSSL_armcap_P; 75 | # endif 76 | 77 | # define ARMV7_NEON (1<<0) 78 | # define ARMV7_TICK (1<<1) 79 | # define ARMV8_AES (1<<2) 80 | # define ARMV8_SHA1 (1<<3) 81 | # define ARMV8_SHA256 (1<<4) 82 | # define ARMV8_PMULL (1<<5) 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /MMKV/Core/aes/openssl/openssl_cfb128.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2008-2016 The OpenSSL Project Authors. All Rights Reserved. 3 | * 4 | * Licensed under the OpenSSL license (the "License"). You may not use 5 | * this file except in compliance with the License. You can obtain a copy 6 | * in the file LICENSE in the source distribution or at 7 | * https://www.openssl.org/source/license.html 8 | */ 9 | 10 | #include "openssl_aes.h" 11 | #include "../../MMKVPredef.h" 12 | #include 13 | 14 | #ifndef MMKV_DISABLE_CRYPT 15 | 16 | namespace openssl { 17 | 18 | /* 19 | * The input and output encrypted as though 128bit cfb mode is being used. 20 | * The extra state information to record how much of the 128bit block we have 21 | * used is contained in *num; 22 | */ 23 | void AES_cfb128_encrypt(const uint8_t *in, uint8_t *out, size_t len, const AES_KEY *key, uint8_t ivec[16], uint32_t *num) 24 | { 25 | auto n = *num; 26 | 27 | while (n && len) { 28 | *(out++) = ivec[n] ^= *(in++); 29 | --len; 30 | n = (n + 1) % 16; 31 | } 32 | while (len >= 16) { 33 | AES_encrypt(ivec, ivec, key); 34 | for (; n < 16; n += sizeof(size_t)) { 35 | *(size_t *)(out + n) = 36 | *(size_t *)(ivec + n) ^= *(size_t *)(in + n); 37 | } 38 | len -= 16; 39 | out += 16; 40 | in += 16; 41 | n = 0; 42 | } 43 | if (len) { 44 | AES_encrypt(ivec, ivec, key); 45 | while (len--) { 46 | out[n] = ivec[n] ^= in[n]; 47 | ++n; 48 | } 49 | } 50 | 51 | *num = n; 52 | } 53 | 54 | /* 55 | * The input and output encrypted as though 128bit cfb mode is being used. 56 | * The extra state information to record how much of the 128bit block we have 57 | * used is contained in *num; 58 | */ 59 | void AES_cfb128_decrypt(const uint8_t *in, uint8_t *out, size_t len, const AES_KEY *key, uint8_t ivec[16], uint32_t *num) 60 | { 61 | auto n = *num; 62 | 63 | while (n && len) { 64 | uint8_t c = *(in++); 65 | *(out++) = ivec[n] ^ c; 66 | ivec[n] = c; 67 | --len; 68 | n = (n + 1) % 16; 69 | } 70 | while (len >= 16) { 71 | AES_encrypt(ivec, ivec, key); 72 | for (; n < 16; n += sizeof(size_t)) { 73 | size_t t = *(size_t *)(in + n); 74 | *(size_t *)(out + n) = *(size_t *)(ivec + n) ^ t; 75 | *(size_t *)(ivec + n) = t; 76 | } 77 | len -= 16; 78 | out += 16; 79 | in += 16; 80 | n = 0; 81 | } 82 | if (len) { 83 | AES_encrypt(ivec, ivec, key); 84 | while (len--) { 85 | uint8_t c = in[n]; 86 | out[n] = ivec[n] ^ c; 87 | ivec[n] = c; 88 | ++n; 89 | } 90 | } 91 | 92 | *num = n; 93 | } 94 | 95 | } // namespace openssl 96 | 97 | #endif // MMKV_DISABLE_CRYPT 98 | -------------------------------------------------------------------------------- /MMKV/Core/aes/openssl/openssl_md5.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. 3 | * 4 | * Licensed under the OpenSSL license (the "License"). You may not use 5 | * this file except in compliance with the License. You can obtain a copy 6 | * in the file LICENSE in the source distribution or at 7 | * https://www.openssl.org/source/license.html 8 | */ 9 | 10 | #ifndef HEADER_MD5_H 11 | # define HEADER_MD5_H 12 | #ifdef __cplusplus 13 | 14 | # include "openssl_opensslconf.h" 15 | 16 | # ifndef OPENSSL_NO_MD5 17 | # include 18 | 19 | namespace openssl { 20 | 21 | /* 22 | * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 23 | * ! MD5_LONG has to be at least 32 bits wide. ! 24 | * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 25 | */ 26 | # define MD5_LONG unsigned int 27 | 28 | # define MD5_CBLOCK 64 29 | # define MD5_LBLOCK (MD5_CBLOCK/4) 30 | # define MD5_DIGEST_LENGTH 16 31 | 32 | typedef struct MD5state_st { 33 | MD5_LONG A, B, C, D; 34 | MD5_LONG Nl, Nh; 35 | MD5_LONG data[MD5_LBLOCK]; 36 | unsigned int num; 37 | } MD5_CTX; 38 | 39 | int MD5_Init(MD5_CTX *c); 40 | int MD5_Update(MD5_CTX *c, const void *data, size_t len); 41 | int MD5_Final(unsigned char *md, MD5_CTX *c); 42 | unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md); 43 | //void MD5_Transform(MD5_CTX *c, const unsigned char *b); 44 | 45 | } // namespace openssl 46 | # endif 47 | 48 | #endif 49 | #endif 50 | -------------------------------------------------------------------------------- /MMKV/Core/aes/openssl/openssl_md5_locl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. 3 | * 4 | * Licensed under the OpenSSL license (the "License"). You may not use 5 | * this file except in compliance with the License. You can obtain a copy 6 | * in the file LICENSE in the source distribution or at 7 | * https://www.openssl.org/source/license.html 8 | */ 9 | #ifdef __cplusplus 10 | 11 | #include 12 | #include 13 | #include "openssl_md5.h" 14 | 15 | namespace openssl { 16 | 17 | void md5_block_data_order(MD5_CTX *c, const void *p, size_t num); 18 | 19 | #define DATA_ORDER_IS_LITTLE_ENDIAN 20 | 21 | #define HASH_LONG MD5_LONG 22 | #define HASH_CTX MD5_CTX 23 | #define HASH_CBLOCK MD5_CBLOCK 24 | #define HASH_UPDATE MD5_Update 25 | //#define HASH_TRANSFORM MD5_Transform 26 | #define HASH_FINAL MD5_Final 27 | #define HASH_MAKE_STRING(c,s) do { \ 28 | unsigned long ll; \ 29 | ll=(c)->A; (void)HOST_l2c(ll,(s)); \ 30 | ll=(c)->B; (void)HOST_l2c(ll,(s)); \ 31 | ll=(c)->C; (void)HOST_l2c(ll,(s)); \ 32 | ll=(c)->D; (void)HOST_l2c(ll,(s)); \ 33 | } while (0) 34 | #define HASH_BLOCK_DATA_ORDER md5_block_data_order 35 | 36 | #include "openssl_md32_common.h" 37 | 38 | /*- 39 | #define F(x,y,z) (((x) & (y)) | ((~(x)) & (z))) 40 | #define G(x,y,z) (((x) & (z)) | ((y) & (~(z)))) 41 | */ 42 | 43 | /* 44 | * As pointed out by Wei Dai, the above can be simplified to the code 45 | * below. Wei attributes these optimizations to Peter Gutmann's 46 | * SHS code, and he attributes it to Rich Schroeppel. 47 | */ 48 | #define F(b,c,d) ((((c) ^ (d)) & (b)) ^ (d)) 49 | #define G(b,c,d) ((((b) ^ (c)) & (d)) ^ (c)) 50 | #define H(b,c,d) ((b) ^ (c) ^ (d)) 51 | #define I(b,c,d) (((~(d)) | (b)) ^ (c)) 52 | 53 | #define R0(a,b,c,d,k,s,t) { \ 54 | a+=((k)+(t)+F((b),(c),(d))); \ 55 | a=ROTATE(a,s); \ 56 | a+=b; };\ 57 | 58 | #define R1(a,b,c,d,k,s,t) { \ 59 | a+=((k)+(t)+G((b),(c),(d))); \ 60 | a=ROTATE(a,s); \ 61 | a+=b; }; 62 | 63 | #define R2(a,b,c,d,k,s,t) { \ 64 | a+=((k)+(t)+H((b),(c),(d))); \ 65 | a=ROTATE(a,s); \ 66 | a+=b; }; 67 | 68 | #define R3(a,b,c,d,k,s,t) { \ 69 | a+=((k)+(t)+I((b),(c),(d))); \ 70 | a=ROTATE(a,s); \ 71 | a+=b; }; 72 | 73 | } // namespace openssl 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /MMKV/Core/aes/openssl/openssl_md5_one.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. 3 | * 4 | * Licensed under the OpenSSL license (the "License"). You may not use 5 | * this file except in compliance with the License. You can obtain a copy 6 | * in the file LICENSE in the source distribution or at 7 | * https://www.openssl.org/source/license.html 8 | */ 9 | 10 | #include 11 | #include 12 | #include "openssl_md5.h" 13 | 14 | namespace openssl { 15 | 16 | unsigned char *MD5(const unsigned char *d, size_t n, unsigned char *md) 17 | { 18 | MD5_CTX c; 19 | static unsigned char m[MD5_DIGEST_LENGTH]; 20 | 21 | if (md == nullptr) 22 | md = m; 23 | if (!MD5_Init(&c)) 24 | return nullptr; 25 | MD5_Update(&c, d, n); 26 | MD5_Final(md, &c); 27 | return md; 28 | } 29 | 30 | } // namespace openssl 31 | -------------------------------------------------------------------------------- /MMKV/Core/crc32/Checksum.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Tencent is pleased to support the open source community by making 3 | * MMKV available. 4 | * 5 | * Copyright (C) 2018 THL A29 Limited, a Tencent company. 6 | * All rights reserved. 7 | * 8 | * Licensed under the BSD 3-Clause License (the "License"); you may not use 9 | * this file except in compliance with the License. You may obtain a copy of 10 | * the License at 11 | * 12 | * https://opensource.org/licenses/BSD-3-Clause 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | #ifndef CHECKSUM_H 22 | #define CHECKSUM_H 23 | #ifdef __cplusplus 24 | 25 | #include "../MMKVPredef.h" 26 | 27 | #if MMKV_EMBED_ZLIB 28 | 29 | # include "zlib/zconf.h" 30 | 31 | namespace zlib { 32 | 33 | uLong crc32(uLong crc, const Bytef *buf, z_size_t len); 34 | 35 | } // namespace zlib 36 | 37 | # define ZLIB_CRC32(crc, buf, len) zlib::crc32(crc, buf, len) 38 | 39 | #else // MMKV_EMBED_ZLIB 40 | 41 | # include 42 | // some old version of zlib doesn't define z_size_t 43 | # ifndef z_size_t 44 | typedef size_t z_size_t; 45 | # endif 46 | # define ZLIB_CRC32(crc, buf, len) ::crc32(crc, buf, static_cast(len)) 47 | 48 | #endif // MMKV_EMBED_ZLIB 49 | 50 | 51 | #if defined(__aarch64__) && defined(__linux__) 52 | 53 | # define MMKV_USE_ARMV8_CRC32 54 | 55 | namespace mmkv { 56 | uint32_t armv8_crc32(uint32_t crc, const uint8_t *buf, size_t len); 57 | } 58 | 59 | # ifdef MMKV_OHOS 60 | // getauxval(AT_HWCAP) in OHOS returns wrong value, we just assume all OHOS device have crc32 instr 61 | # define CRC32 mmkv::armv8_crc32 62 | # else 63 | // have to check CPU's instruction set dynamically 64 | typedef uint32_t (*CRC32_Func_t)(uint32_t crc, const uint8_t *buf, size_t len); 65 | extern CRC32_Func_t CRC32; 66 | # endif 67 | 68 | #else // defined(__aarch64__) && defined(__linux__) 69 | 70 | # define CRC32(crc, buf, len) ZLIB_CRC32(crc, buf, len) 71 | 72 | #endif // defined(__aarch64__) && defined(__linux__) 73 | 74 | #endif // __cplusplus 75 | #endif // CHECKSUM_H 76 | -------------------------------------------------------------------------------- /MMKV/Core/crc32/zlib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Tencent is pleased to support the open source community by making 3 | # MMKV available. 4 | # 5 | # Copyright (C) 2020 THL A29 Limited, a Tencent company. 6 | # All rights reserved. 7 | # 8 | # Licensed under the BSD 3-Clause License (the "License"); you may not use 9 | # this file except in compliance with the License. You may obtain a copy of 10 | # the License at 11 | # 12 | # https://opensource.org/licenses/BSD-3-Clause 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # 20 | 21 | # Sets the minimum version of CMake required to build the native library. 22 | 23 | cmake_minimum_required(VERSION 3.10.0) 24 | 25 | project(z) 26 | 27 | IF(APPLE) 28 | add_compile_definitions(FORCE_POSIX) 29 | ENDIF() 30 | 31 | 32 | # Creates and names a library, sets it as either STATIC 33 | # or SHARED, and provides the relative paths to its source code. 34 | # You can define multiple libraries, and CMake builds them for you. 35 | # Gradle automatically packages shared libraries with your APK. 36 | 37 | add_library( # Sets the name of the library. 38 | z 39 | 40 | # Sets the library as a shared library. 41 | STATIC 42 | 43 | # Provides a relative path to your source file(s). 44 | crc32.h 45 | crc32.cpp 46 | zconf.h 47 | zutil.h 48 | ) 49 | 50 | 51 | set_target_properties(z PROPERTIES 52 | CXX_STANDARD 17 53 | CXX_EXTENSIONS OFF 54 | ) 55 | 56 | # Specifies libraries CMake should link to your target library. You 57 | # can link multiple libraries, such as libraries you define in this 58 | # build script, prebuilt third-party libraries, or system libraries. 59 | 60 | 61 | -------------------------------------------------------------------------------- /MMKV/Core/crc32/zlib/crc32.cpp: -------------------------------------------------------------------------------- 1 | /* crc32.c -- compute the CRC-32 of a data stream 2 | * Copyright (C) 1995-2006, 2010, 2011, 2012, 2016 Mark Adler 3 | * For conditions of distribution and use, see copyright notice in zlib.h 4 | * 5 | * Thanks to Rodney Brown for his contribution of faster 6 | * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing 7 | * tables for updating the shift register in one step with three exclusive-ors 8 | * instead of four steps with four exclusive-ors. This results in about a 9 | * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. 10 | */ 11 | 12 | /* @(#) $Id$ */ 13 | 14 | #include "../../MMKVPredef.h" 15 | 16 | #if MMKV_EMBED_ZLIB 17 | 18 | #include "zutil.h" /* for STDC and FAR definitions */ 19 | 20 | #define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ 21 | # define TBLS 1 22 | 23 | #include "crc32.h" 24 | 25 | namespace zlib { 26 | 27 | /* ========================================================================= */ 28 | #define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) 29 | #define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 30 | 31 | /* ========================================================================= */ 32 | unsigned long local crc32_z(unsigned long crc, const unsigned char FAR *buf, z_size_t len) 33 | { 34 | if (buf == Z_NULL) return 0UL; 35 | 36 | crc = crc ^ 0xffffffffUL; 37 | while (len >= 8) { 38 | DO8; 39 | len -= 8; 40 | } 41 | if (len) do { 42 | DO1; 43 | } while (--len); 44 | return crc ^ 0xffffffffUL; 45 | } 46 | 47 | /* ========================================================================= */ 48 | 49 | unsigned long ZEXPORT crc32(unsigned long crc, const unsigned char FAR *buf, z_size_t len) { 50 | return crc32_z(crc, buf, len); 51 | } 52 | 53 | } // namespace zlib 54 | 55 | #endif // MMKV_EMBED_ZLIB -------------------------------------------------------------------------------- /MMKV/Core/crc32/zlib/zutil.h: -------------------------------------------------------------------------------- 1 | /* zutil.h -- internal interface and configuration of the compression library 2 | * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler 3 | * For conditions of distribution and use, see copyright notice in zlib.h 4 | */ 5 | 6 | /* WARNING: this file should *not* be used by applications. It is 7 | part of the implementation of the compression library and is 8 | subject to change. Applications should only use zlib.h. 9 | */ 10 | 11 | /* @(#) $Id$ */ 12 | 13 | #ifndef ZUTIL_H 14 | #define ZUTIL_H 15 | 16 | #include "zconf.h" 17 | 18 | #ifndef local 19 | #define local static 20 | #endif 21 | /* since "static" is used to mean two completely different things in C, we 22 | define "local" for the non-static meaning of "static", for readability 23 | (compile with -Dlocal if your debugger can't find static symbols) */ 24 | 25 | #endif /* ZUTIL_H */ 26 | -------------------------------------------------------------------------------- /android/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | android 4 | Project android created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.buildship.core.gradleprojectbuilder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.buildship.core.gradleprojectnature 22 | 23 | 24 | -------------------------------------------------------------------------------- /android/generated/java/com/facebook/fbreact/specs/NativeMMKVStorageSpec.java: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). 4 | * 5 | * Do not edit this file as changes may cause incorrect behavior and will be lost 6 | * once the code is regenerated. 7 | * 8 | * @generated by codegen project: GenerateModuleJavaSpec.js 9 | * 10 | * @nolint 11 | */ 12 | 13 | package com.facebook.fbreact.specs; 14 | 15 | import com.facebook.proguard.annotations.DoNotStrip; 16 | import com.facebook.react.bridge.ReactApplicationContext; 17 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 18 | import com.facebook.react.bridge.ReactMethod; 19 | import com.facebook.react.turbomodule.core.interfaces.TurboModule; 20 | import javax.annotation.Nonnull; 21 | 22 | public abstract class NativeMMKVStorageSpec extends ReactContextBaseJavaModule implements TurboModule { 23 | public static final String NAME = "MMKVStorage"; 24 | 25 | public NativeMMKVStorageSpec(ReactApplicationContext reactContext) { 26 | super(reactContext); 27 | } 28 | 29 | @Override 30 | public @Nonnull String getName() { 31 | return NAME; 32 | } 33 | 34 | @ReactMethod(isBlockingSynchronousMethod = true) 35 | @DoNotStrip 36 | public abstract boolean install(); 37 | } 38 | -------------------------------------------------------------------------------- /android/generated/jni/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree. 5 | 6 | cmake_minimum_required(VERSION 3.13) 7 | set(CMAKE_VERBOSE_MAKEFILE on) 8 | 9 | file(GLOB react_codegen_SRCS CONFIGURE_DEPENDS *.cpp react/renderer/components/MMKVStorageSpec/*.cpp) 10 | 11 | add_library( 12 | react_codegen_MMKVStorageSpec 13 | OBJECT 14 | ${react_codegen_SRCS} 15 | ) 16 | 17 | target_include_directories(react_codegen_MMKVStorageSpec PUBLIC . react/renderer/components/MMKVStorageSpec) 18 | 19 | target_link_libraries( 20 | react_codegen_MMKVStorageSpec 21 | fbjni 22 | jsi 23 | # We need to link different libraries based on whether we are building rncore or not, that's necessary 24 | # because we want to break a circular dependency between react_codegen_rncore and reactnative 25 | reactnative 26 | ) 27 | 28 | target_compile_options( 29 | react_codegen_MMKVStorageSpec 30 | PRIVATE 31 | -DLOG_TAG=\"ReactNative\" 32 | -fexceptions 33 | -frtti 34 | -std=c++20 35 | -Wall 36 | ) 37 | -------------------------------------------------------------------------------- /android/generated/jni/MMKVStorageSpec-generated.cpp: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). 4 | * 5 | * Do not edit this file as changes may cause incorrect behavior and will be lost 6 | * once the code is regenerated. 7 | * 8 | * @generated by codegen project: GenerateModuleJniCpp.js 9 | */ 10 | 11 | #include "MMKVStorageSpec.h" 12 | 13 | namespace facebook::react { 14 | 15 | static facebook::jsi::Value __hostFunction_NativeMMKVStorageSpecJSI_install(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { 16 | static jmethodID cachedMethodId = nullptr; 17 | return static_cast(turboModule).invokeJavaMethod(rt, BooleanKind, "install", "()Z", args, count, cachedMethodId); 18 | } 19 | 20 | NativeMMKVStorageSpecJSI::NativeMMKVStorageSpecJSI(const JavaTurboModule::InitParams ¶ms) 21 | : JavaTurboModule(params) { 22 | methodMap_["install"] = MethodMetadata {0, __hostFunction_NativeMMKVStorageSpecJSI_install}; 23 | } 24 | 25 | std::shared_ptr MMKVStorageSpec_ModuleProvider(const std::string &moduleName, const JavaTurboModule::InitParams ¶ms) { 26 | if (moduleName == "MMKVStorage") { 27 | return std::make_shared(params); 28 | } 29 | return nullptr; 30 | } 31 | 32 | } // namespace facebook::react 33 | -------------------------------------------------------------------------------- /android/generated/jni/MMKVStorageSpec.h: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). 4 | * 5 | * Do not edit this file as changes may cause incorrect behavior and will be lost 6 | * once the code is regenerated. 7 | * 8 | * @generated by codegen project: GenerateModuleJniH.js 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | namespace facebook::react { 18 | 19 | /** 20 | * JNI C++ class for module 'NativeMMKVStorage' 21 | */ 22 | class JSI_EXPORT NativeMMKVStorageSpecJSI : public JavaTurboModule { 23 | public: 24 | NativeMMKVStorageSpecJSI(const JavaTurboModule::InitParams ¶ms); 25 | }; 26 | 27 | 28 | JSI_EXPORT 29 | std::shared_ptr MMKVStorageSpec_ModuleProvider(const std::string &moduleName, const JavaTurboModule::InitParams ¶ms); 30 | 31 | } // namespace facebook::react 32 | -------------------------------------------------------------------------------- /android/generated/jni/react/renderer/components/MMKVStorageSpec/MMKVStorageSpecJSI-generated.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). 3 | * 4 | * Do not edit this file as changes may cause incorrect behavior and will be lost 5 | * once the code is regenerated. 6 | * 7 | * @generated by codegen project: GenerateModuleCpp.js 8 | */ 9 | 10 | #include "MMKVStorageSpecJSI.h" 11 | 12 | namespace facebook::react { 13 | 14 | static jsi::Value __hostFunction_NativeMMKVStorageCxxSpecJSI_install(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { 15 | return static_cast(&turboModule)->install( 16 | rt 17 | ); 18 | } 19 | 20 | NativeMMKVStorageCxxSpecJSI::NativeMMKVStorageCxxSpecJSI(std::shared_ptr jsInvoker) 21 | : TurboModule("MMKVStorage", jsInvoker) { 22 | methodMap_["install"] = MethodMetadata {0, __hostFunction_NativeMMKVStorageCxxSpecJSI_install}; 23 | } 24 | 25 | 26 | } // namespace facebook::react 27 | -------------------------------------------------------------------------------- /android/generated/jni/react/renderer/components/MMKVStorageSpec/MMKVStorageSpecJSI.h: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). 3 | * 4 | * Do not edit this file as changes may cause incorrect behavior and will be lost 5 | * once the code is regenerated. 6 | * 7 | * @generated by codegen project: GenerateModuleH.js 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | #include 14 | 15 | namespace facebook::react { 16 | 17 | 18 | class JSI_EXPORT NativeMMKVStorageCxxSpecJSI : public TurboModule { 19 | protected: 20 | NativeMMKVStorageCxxSpecJSI(std::shared_ptr jsInvoker); 21 | 22 | public: 23 | virtual bool install(jsi::Runtime &rt) = 0; 24 | 25 | }; 26 | 27 | template 28 | class JSI_EXPORT NativeMMKVStorageCxxSpec : public TurboModule { 29 | public: 30 | jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &propName) override { 31 | return delegate_.get(rt, propName); 32 | } 33 | 34 | static constexpr std::string_view kModuleName = "MMKVStorage"; 35 | 36 | protected: 37 | NativeMMKVStorageCxxSpec(std::shared_ptr jsInvoker) 38 | : TurboModule(std::string{NativeMMKVStorageCxxSpec::kModuleName}, jsInvoker), 39 | delegate_(reinterpret_cast(this), jsInvoker) {} 40 | 41 | 42 | private: 43 | class Delegate : public NativeMMKVStorageCxxSpecJSI { 44 | public: 45 | Delegate(T *instance, std::shared_ptr jsInvoker) : 46 | NativeMMKVStorageCxxSpecJSI(std::move(jsInvoker)), instance_(instance) { 47 | 48 | } 49 | 50 | bool install(jsi::Runtime &rt) override { 51 | static_assert( 52 | bridging::getParameterCount(&T::install) == 1, 53 | "Expected install(...) to have 1 parameters"); 54 | 55 | return bridging::callFromJs( 56 | rt, &T::install, jsInvoker_, instance_); 57 | } 58 | 59 | private: 60 | friend class NativeMMKVStorageCxxSpec; 61 | T *instance_; 62 | }; 63 | 64 | Delegate delegate_; 65 | }; 66 | 67 | } // namespace facebook::react 68 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ammarahm-ed/react-native-mmkv-storage/09790b470e6162d1aee662a73784a9423040100a/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifestNew.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /android/src/main/java/com/ammarahmed/mmkv/Constants.java: -------------------------------------------------------------------------------- 1 | package com.ammarahmed.mmkv; 2 | 3 | public class Constants { 4 | 5 | // Key Store 6 | public static final String KEYSTORE_PROVIDER_1 = "AndroidKeyStore"; 7 | public static final String KEYSTORE_PROVIDER_2 = "AndroidKeyStoreBCWorkaround"; 8 | public static final String KEYSTORE_PROVIDER_3 = "AndroidOpenSSL"; 9 | 10 | public static final String RSA_ALGORITHM = "RSA/ECB/PKCS1Padding"; 11 | public static final String AES_ALGORITHM = "AES/ECB/PKCS5Padding"; 12 | 13 | public static final String TAG = "RNSecureStorage"; 14 | 15 | // Internal storage file 16 | public static final String SKS_KEY_FILENAME = "SKS_KEY_FILE"; 17 | public static final String SKS_DATA_FILENAME = "SKS_DATA_FILE"; 18 | 19 | 20 | public static final int DATA_TYPE_STRING = 1; 21 | public static final int DATA_TYPE_INT = 2; 22 | public static final int DATA_TYPE_BOOL = 3; 23 | public static final int DATA_TYPE_MAP = 4; 24 | public static final int DATA_TYPE_ARRAY = 5; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /android/src/main/java/com/ammarahmed/mmkv/RNMMKVPackage.java: -------------------------------------------------------------------------------- 1 | 2 | package com.ammarahmed.mmkv; 3 | 4 | import java.util.Map; 5 | import java.util.HashMap; 6 | 7 | import androidx.annotation.Nullable; 8 | 9 | import com.facebook.react.bridge.NativeModule; 10 | import com.facebook.react.bridge.ReactApplicationContext; 11 | import com.facebook.react.TurboReactPackage; 12 | import com.facebook.react.module.model.ReactModuleInfo; 13 | import com.facebook.react.module.model.ReactModuleInfoProvider; 14 | 15 | public class RNMMKVPackage extends TurboReactPackage { 16 | @Nullable 17 | @Override 18 | public NativeModule getModule(String name, ReactApplicationContext reactContext) { 19 | if (name.equals(RNMMKVModule.NAME)) { 20 | return new RNMMKVModule(reactContext); 21 | } else { 22 | return null; 23 | } 24 | } 25 | 26 | @Override 27 | public ReactModuleInfoProvider getReactModuleInfoProvider() { 28 | return () -> { 29 | final Map moduleInfos = new HashMap<>(); 30 | boolean isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; 31 | moduleInfos.put( 32 | RNMMKVModule.NAME, 33 | new ReactModuleInfo( 34 | RNMMKVModule.NAME, 35 | RNMMKVModule.NAME, 36 | false, // canOverrideExistingModule 37 | false, // needsEagerInit 38 | true, // hasConstants 39 | false, // isCxxModule 40 | true // isTurboModule 41 | )); 42 | return moduleInfos; 43 | }; 44 | } 45 | } -------------------------------------------------------------------------------- /android/src/main/java/com/ammarahmed/mmkv/Storage.java: -------------------------------------------------------------------------------- 1 | package com.ammarahmed.mmkv; 2 | 3 | import android.content.Context; 4 | 5 | import java.io.ByteArrayOutputStream; 6 | import java.io.File; 7 | import java.io.FileInputStream; 8 | import java.io.FileOutputStream; 9 | import java.io.IOException; 10 | 11 | public final class Storage { 12 | 13 | public static void writeValues(Context context, String filename, byte[] bytes) throws IOException { 14 | FileOutputStream fos = context.openFileOutput(filename, Context.MODE_PRIVATE); 15 | fos.write(bytes); 16 | fos.close(); 17 | } 18 | 19 | public static byte[] readValues(Context context, String filename) throws IOException { 20 | FileInputStream fis = context.openFileInput(filename); 21 | ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); 22 | byte[] buffer = new byte[1024]; 23 | int bytesRead = fis.read(buffer); 24 | while(bytesRead != -1) { 25 | baos.write(buffer, 0, bytesRead); 26 | bytesRead = fis.read(buffer); 27 | } 28 | return baos.toByteArray(); 29 | } 30 | 31 | public static boolean exists(Context context, String filename) throws IOException { 32 | File file = context.getFileStreamPath(filename); 33 | return file != null && file.exists(); 34 | } 35 | 36 | public static void resetValues(Context context, String[] filenames) { 37 | for(String filename : filenames) { 38 | context.deleteFile(filename); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /android/src/main/rnmmkv/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.9.0) 2 | 3 | set (PACKAGE_NAME "rnmmkv") 4 | project(PACKAGE_NAME) 5 | set (BUILD_DIR ../../../) 6 | set(CMAKE_VERBOSE_MAKEFILE ON) 7 | set(CMAKE_CXX_STANDARD 17) 8 | 9 | add_subdirectory(../../../../MMKV/Core core) 10 | 11 | add_library( 12 | ${PACKAGE_NAME} 13 | SHARED 14 | ./rnmmkv-adapter.cpp 15 | ) 16 | 17 | set_target_properties( 18 | ${PACKAGE_NAME} PROPERTIES 19 | CXX_STANDARD 17 20 | CXX_EXTENSIONS OFF 21 | POSITION_INDEPENDENT_CODE ON 22 | ) 23 | 24 | find_package(ReactAndroid REQUIRED CONFIG) 25 | find_library(log-lib log) 26 | find_package(fbjni REQUIRED CONFIG) 27 | 28 | 29 | if(${REACT_NATIVE_MINOR_VERSION} GREATER_EQUAL 76) 30 | target_link_libraries( 31 | ${PACKAGE_NAME} 32 | core 33 | ${log-lib} 34 | fbjni::fbjni 35 | ReactAndroid::jsi 36 | ReactAndroid::reactnative 37 | android 38 | ) 39 | else() 40 | target_link_libraries( 41 | ${PACKAGE_NAME} 42 | core 43 | ${log-lib} 44 | fbjni::fbjni 45 | ReactAndroid::jsi 46 | ReactAndroid::turbomodulejsijni 47 | ReactAndroid::react_nativemodule_core 48 | android 49 | ) 50 | endif() 51 | 52 | 53 | -------------------------------------------------------------------------------- /android/src/newarch/MmkvStorageSpec.kt: -------------------------------------------------------------------------------- 1 | package com.ammarahmed.mmkv 2 | 3 | import com.facebook.fbreact.specs.NativeMMKVStorageSpec 4 | import com.facebook.react.bridge.ReactApplicationContext 5 | 6 | abstract class MmkvStorageSpec internal constructor(context: ReactApplicationContext) : 7 | NativeMMKVStorageSpec(context) { 8 | } 9 | -------------------------------------------------------------------------------- /android/src/oldarch/MmkvStorageSpec.kt: -------------------------------------------------------------------------------- 1 | package com.ammarahmed.mmkv 2 | 3 | import com.facebook.react.bridge.ReactApplicationContext 4 | import com.facebook.react.bridge.ReactContextBaseJavaModule 5 | import com.facebook.react.bridge.Promise 6 | 7 | abstract class MmkvStorageSpec internal constructor(context: ReactApplicationContext) : 8 | ReactContextBaseJavaModule(context) { 9 | 10 | 11 | 12 | abstract fun install(): Boolean 13 | } 14 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | .now -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ammarahm-ed/react-native-mmkv-storage/09790b470e6162d1aee662a73784a9423040100a/docs/.nojekyll -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | docsify.js.org -------------------------------------------------------------------------------- /docs/_coverpage.md: -------------------------------------------------------------------------------- 1 | # react-native-mmkv-storage 0.10.3 2 | 3 | 4 | > An ultra fast(0.0002s read/write), small & encrypted mobile key-value storage framework for React Native written in C++ using JSI 5 | 6 | Ultra fast | Encrypted | Easy to use 7 | 8 | [GitHub](https://github.com/ammarahm-ed/react-native-mmkv-storage) 9 | [Getting Started](#react-native-mmkv-storage) 10 | 11 | 12 | 13 | ![color](#000000) -------------------------------------------------------------------------------- /docs/_sidebar.md: -------------------------------------------------------------------------------- 1 | - Getting started 2 | 3 | - [Installation](gettingstarted.md) 4 | - [Creating an MMKV Instance](creatinginstance.md) 5 | - [Working with Encryption](workingwithencryption.md) 6 | - [Reactive Apps with useMMKVStorage Hook](usemmkvstorage.md) 7 | - [Value Lifecycle Control](transactionmanager.md) 8 | - [Supported Data Types](datatypes.md) 9 | - [Redux persist support](redux-persist.md) 10 | - [Testing with Jest](mockjest.md) 11 | - [Flipper support](flipper.md) 12 | 13 | - API Reference 14 | 15 | - [Loader Class](loaderclass.md) 16 | - [Async API](asyncapi.md) 17 | - [Sync API](callbackapi.md) 18 | - [General Methods](generalmethods.md) 19 | - [Encryption](encryption.md) 20 | - [Querying and Indexing](queryingandindexing.md) 21 | - [useMMKVStorage](usemmkvstorage.md) 22 | - [useIndex](useindex.md) 23 | - [useMMKVRef](usemmkvref.md) 24 | - [Transcation Manager](transactionmanager.md) 25 | 26 | - [Changelog](changelog.md) 27 | -------------------------------------------------------------------------------- /docs/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog -------------------------------------------------------------------------------- /docs/creatinginstance.md: -------------------------------------------------------------------------------- 1 | # Creating an MMKV Instance 2 | 3 | The first step is to import the library in your project files 4 | 5 | ```js 6 | import { MMKVLoader } from "react-native-mmkv-storage"; 7 | ``` 8 | 9 | Creating a new instance is simple and follows a builder pattern. Here is an example of loading the default instance. 10 | 11 | ```js 12 | // Create a new Loader Class. 13 | 14 | const MMKV = new MMKVLoader().initialize(); // Returns an MMKV Instance 15 | 16 | // Then make are read/write requests 17 | 18 | await MMKV.setStringAsync("string", "string"); 19 | 20 | let string = await MMKV.getStringAsync("string"); 21 | 22 | // 23 | ``` 24 | 25 | ## MMKV Instance with ID 26 | 27 | The library allows you to create as many instances of MMKV as you might need giving a unique ID to each instance. 28 | 29 | ```js 30 | const MMKVwithID = new MMKVLoader() 31 | .withInstanceID("mmkvWithID") 32 | .initialize(); 33 | 34 | // Then make are read/write requests 35 | 36 | await MMKVwithID.setStringAsync("string", "string"); 37 | 38 | let string = await MMKVwithID.getStringAsync("string"); 39 | ``` 40 | 41 | ## MMKV Instance with Encryption 42 | 43 | You can also encrypt MMKV Instance when you initialize it. By default the library generates a strong encryption key and saves it in Keychain on iOS and Android Keystore on Android for continuious usage 44 | 45 | ```js 46 | const MMKVwithEncryption = new MMKVLoader() 47 | .withEncryption() 48 | .initialize(); 49 | 50 | // OR if you are initializing with an instance ID 51 | 52 | const MMKVwithEncryptionAndID = new MMKVLoader() 53 | .withInstanceID("mmkvWithEncryptionAndID") 54 | .withEncryption() 55 | .initialize(); 56 | ``` 57 | 58 | !> Remember that if you encrypt an already created instance using the loader class, it will create a new MMKV instance even if the instance exists. To encrypt an already existing instance, use encrypt() method. Read in detail about Encryption API here. 59 | 60 | ## Encryption with custom key 61 | 62 | While the library can handle the encryption itself, you can choose to provide your own custom encryption key etc. For example, you maybe want to encrypt the storage with a token or user password. 63 | 64 | ```js 65 | const MMKVwithEncryptionKey = new MMKVLoader() 66 | .withEncryption() 67 | .encryptWithCustomKey("encryptionKey") 68 | .initialize(); 69 | ``` 70 | 71 | And if you want to store this key in secure storage. 72 | 73 | ```js 74 | const MMKVwithEncryptionKey = new MMKVLoader() 75 | .withEncryption() 76 | .encryptWithCustomKey("encryptionKey",true) 77 | .initialize(); 78 | ``` 79 | 80 | If you want to set your own custom alias for the key that is stored in the secure storage you can set it also. 81 | 82 | ```js 83 | const MMKVwithEncryptionKey = new MMKVLoader() 84 | .withEncryption() 85 | .encryptWithCustomKey("encryptionKey", true, "myCustomAlias") 86 | .initialize(); 87 | ``` 88 | -------------------------------------------------------------------------------- /docs/datatypes.md: -------------------------------------------------------------------------------- 1 | # Data Types 2 | 3 | The following javascript data types are supported 4 | 5 |
12 | 13 |
27 |

28 | Strings
29 | "" 30 |

31 |
32 | 33 |
47 |

48 | Boolean
49 | true/false 50 |

51 |
52 | 53 |
67 |

68 | Numbers
69 | 123 70 |

71 |
72 | 73 |
87 |

88 | Objects
89 | {} 90 |

91 |
92 | 93 |
107 |

108 | Arrays
109 | [] 110 |

111 |
112 | 113 |
114 | 115 | 116 | The Map data type is **not** supported. 117 | -------------------------------------------------------------------------------- /docs/flipper.md: -------------------------------------------------------------------------------- 1 | # Flipper support 2 | 3 | Thanks to [pnthach95](https://github.com/pnthach95/flipper-plugin-react-native-mmkv-storage/commits?author=pnthach95) Flipper plugin is finally here. It supports logging and manipulating storage values on the fly. 4 | 5 | - Desktop plugin: https://github.com/pnthach95/flipper-plugin-react-native-mmkv-storage 6 | - React Native plugin: https://github.com/pnthach95/rn-mmkv-storage-flipper 7 | 8 | ## Features 9 | 10 | - Logging on read/write 11 | - Edit values on the fly. If you use `useMMKVStorage` hook. Values will update automatically. 12 | 13 | ![gif](https://github.com/pnthach95/flipper-plugin-react-native-mmkv-storage/raw/main/docs/example.gif) 14 | 15 | _\* Tested on Flipper v0.152.0, React Native v0.68.2, RN MMKV Storage source code from github_ 16 | 17 | ## Installation 18 | 19 | Open Flipper and search on Plugin Manager 20 | 21 | ![](https://github.com/pnthach95/flipper-plugin-react-native-mmkv-storage/raw/main/docs/manager.png) 22 | 23 | On your React Native project, install plugin: 24 | 25 | ```bash 26 | yarn add react-native-flipper rn-mmkv-storage-flipper --dev 27 | ``` 28 | 29 | or 30 | 31 | ```bash 32 | npm i react-native-flipper rn-mmkv-storage-flipper -D 33 | ``` 34 | 35 | And update your code: 36 | 37 | ```js 38 | import {MMKVLoader} from 'react-native-mmkv-storage'; 39 | import mmkvFlipper from 'rn-mmkv-storage-flipper'; 40 | 41 | const MMKV = new MMKVLoader() 42 | .withInstanceID('test') 43 | .withEncryption() 44 | .initialize(); 45 | 46 | if (__DEV__) { 47 | mmkvFlipper(MMKV); 48 | } 49 | ``` 50 | -------------------------------------------------------------------------------- /docs/generalmethods.md: -------------------------------------------------------------------------------- 1 | # General Methods 2 | 3 | Some useful general methods for each instance with a single variant. 4 | 5 | First we create a default MMKV Instance 6 | 7 | ```js 8 | import { MMKVLoader } from "react-native-mmkv-storage"; 9 | 10 | MMKV = new MMKVLoader().initialize(); 11 | ``` 12 | 13 | ## removeItem 14 | 15 | Remove an item for a given key. 16 | 17 | **Arguments** 18 | 19 | | Name | Type | 20 | |------|--------| 21 | | key | String | 22 | 23 | ```js 24 | MMKV.removeItem(key); 25 | ``` 26 | 27 | ## clearStore 28 | 29 | Clear the storage. 30 | 31 | ```js 32 | MMKV.clearStore(); 33 | ``` 34 | 35 | ## clearMemoryCache 36 | 37 | Clear the storage from memory. 38 | 39 | ```js 40 | MMKV.clearMemoryCache(); 41 | ``` 42 | 43 | ## getAllMMKVInstanceIDs 44 | 45 | Returns a list of all the MMKV Instance IDs created. 46 | 47 | ```js 48 | let allInstances = MMKV.getAllMMKVInstanceIDs(); 49 | ``` 50 | 51 | ## getCurrentMMKVInstances 52 | 53 | get the currently initialized instance IDs. 54 | 55 | ```js 56 | let intializedInstances = MMKV.getCurrentMMKVInstanceIDs(); 57 | ``` 58 | 59 | ## getKey 60 | 61 | get the encryption key for the current MMKV instance 62 | 63 | ```js 64 | let key = MMKV.getKey(); 65 | ``` 66 | -------------------------------------------------------------------------------- /docs/gettingstarted.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | Add the library to your React Native project. 4 | 5 | ```bash 6 | npm install react-native-mmkv-storage 7 | ``` 8 | or 9 | 10 | ```bash 11 | yarn add react-native-mmkv-storage 12 | ``` 13 | 14 | It's recommended that you upgrade your project to latest version of react native. However if you are on react native < 0.66.x run the following: 15 | 16 | ``` 17 | npx mmkv-link 18 | ``` 19 | 20 | ### iOS Steps 21 | 22 | ```bash 23 | pod install 24 | ``` 25 | 26 | ### Manual Installation (For React Native 0.65.x & older) 27 | If `npx mmkv-link` fails for some reason. You can follow the steps below and make the changes manually. 28 | 29 | Update the Android Gradle plugin to `4.1+` (`android/build.gradle`) and `ndkVersion` define property: 30 | 31 | ```diff 32 | // When SDK 30 33 | buildscript { 34 | build { 35 | compileSdkVersion = 30 36 | targetSdkVersion = 30 37 | + ndkVersion = "21.4.7075529" 38 | } 39 | dependencies { 40 | - classpath 'com.android.tools.build:gradle:3.2.0' 41 | + classpath 'com.android.tools.build:gradle:4.2.2' 42 | 43 | // When SDK 29 44 | buildscript { 45 | build { 46 | compileSdkVersion = 29 47 | targetSdkVersion = 29 48 | + ndkVersion = "21.1.6352462" 49 | } 50 | dependencies { 51 | - classpath 'com.android.tools.build:gradle:3.2.0' 52 | + classpath 'com.android.tools.build:gradle:4.1.0' 53 | ``` 54 | 55 | Update Gradle version in `android/gradle/wrapper/gradle-wrapper.properties` 56 | 57 | ```diff 58 | distributionBase=GRADLE_USER_HOME 59 | distributionPath=wrapper/dists 60 | -distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-all.zip 61 | 62 | // When SDK 30 63 | +distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip 64 | 65 | // When SDK 29 66 | +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 67 | ``` 68 | 69 | #### Troubleshooting 70 | 71 | if your build fails, check your NDK version & CMake version installed in Android Studio SDK Manager. 72 | Also make sure you do not have multiple CMake & NDK versions selected in SDK Manager.[Refer to this comment](https://github.com/ammarahm-ed/react-native-mmkv-storage/issues/67#issuecomment-801467636) 73 | 74 | ##### `CMake 3.9.0 or higher is required. You are running version 3.6.0-rc2` 75 | 76 | You can specify newer cmake version on CI using `local.properties`: 77 | 78 | ```bash 79 | echo "cmake.dir=$ANDROID_HOME/cmake/3.10.2.4988404" >> android/local.properties 80 | 81 | # Check installed cmake on CI 82 | ls $ANDROID_HOME/cmake/ # 3.10.2.4988404 3.6.4111459 83 | ``` 84 | 85 | ### iOS 86 | 87 | 1. Update your project deployment target to `11.0` 88 | 89 | 2. Update your deployment target in project Podfile 90 | 91 | ``` 92 | platform :ios, '11.0' 93 | ``` 94 | 95 | ## No Debug Mode 96 | 97 | You cannot attach chrome debugger if you are using >=0.5.0 version of this library since debugging is not available when JSI modules are used. You can use Flipper to debug if necessary. 98 | 99 | ## 100 | 101 | **Read Next:** [Creating an MMKV Instance](creatinginstance.md) 102 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | react-native-mmkv-storage 7 | 8 | 29 | 31 | 33 | 34 | 35 | 38 | 39 | 46 | 47 | 48 | 49 | 50 | 51 |
Loading ...
52 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /docs/mockjest.md: -------------------------------------------------------------------------------- 1 | ## Testing with Jest 2 | 3 | The library supports mocking the storage in memory to support read/write during tests. Follow the guide below to make it work with jest. 4 | 5 | Add `mmkvJestSetup.js` and `transformIgnorePatterns` in `jest.config.js`. 6 | 7 | ```js 8 | module.exports = { 9 | preset: 'react-native', 10 | setupFiles: [ 11 | './node_modules/react-native-mmkv-storage/jest/mmkvJestSetup.js', 12 | ], 13 | transformIgnorePatterns: ['/!node_modules\\/react-native-mmkv-storage/'], 14 | }; 15 | 16 | ``` 17 | 18 | You will need to mock the storage with an `in-memory adapter` when your tests run as follows: 19 | 20 | ```js 21 | import 'react-native'; 22 | import {MMKVLoader,isLoaded} from 'react-native-mmkv-storage'; // Import the library as normal. 23 | 24 | describe('MMKV Storage mock functionality', () => { 25 | 26 | beforeEach(function () { 27 | // Install the in-memory adapter 28 | let mmkvMock = require('react-native-mmkv-storage/jest/dist/jest/memoryStore.js'); 29 | mmkvMock.unmock(); // Cleanup if already mocked 30 | mmkvMock.mock(); // Mock the storage 31 | }); 32 | 33 | // Use the storage methods as needed. Everything is mocked now 34 | it('Mock bindings are installed', () => { 35 | expect(isLoaded()).toBe(true); 36 | }); 37 | 38 | // Create a new instance. 39 | it('Init an instance', () => { 40 | let instance = new MMKVLoader().initialize(); 41 | expect(instance.instanceID).toBe('default'); 42 | expect(instance.getString('unknown')).toBe(null); 43 | }); 44 | 45 | }); 46 | ``` 47 | -------------------------------------------------------------------------------- /docs/redux-persist.md: -------------------------------------------------------------------------------- 1 | # Support for redux-persist 2 | 3 | The library provides support for redux-persist so you can replace it directly with AsyncStorage. 4 | 5 | First we create an MMKV instance, it could be with or without encryption. 6 | 7 | ```js 8 | import { MMKVLoader } from "react-native-mmkv-storage"; 9 | 10 | const storage = new MMKVLoader().initialize(); 11 | ``` 12 | 13 | Then we pass the storage to persistConfig. 14 | 15 | ```js 16 | const persistConfig = { 17 | //... 18 | storage, 19 | }; 20 | ``` 21 | -------------------------------------------------------------------------------- /docs/useindex.md: -------------------------------------------------------------------------------- 1 | # useIndex hook 2 | 3 | A hook that will take an array of keys and returns an array of values for those keys. This is supposed to work in combination with [Transactions](transactionmanager.md). When you have build your custom index, you will need an easy and quick way to load values for your index. useIndex hook actively listens to all read/write changes and updates the values accordingly. 4 | 5 | **Arguments** 6 | 7 | | Name | Required | Type | Description | 8 | |-------------------------------------|----------|------------------------------------------------------|--------------------------------------------------------------------| 9 | | index | yes | Array | The type of data you want to register the function for. | 10 | | type | yes | "string" / "number" / "object" / "array" / "boolean" | Type of values in index | 11 | | [`MMKVStorage.API`](callbackapi.md) | no | `MMKVStorage.API` | MMKV storage instance created from `new MMKVLoader().initialize()` | 12 | 13 | **returns:** `[values,update,remove]` 14 | 15 | #### `values` 16 | 17 | An array of values for the given index 18 | 19 | #### `update(key:string,value:any)` 20 | 21 | A function that allows to update a key in an index. You can also add new ones. 22 | 23 | #### `remove(key:string)` 24 | 25 | Remove an item from index and storage. 26 | 27 | ### How to use 28 | 29 | Here's a simple example use case 30 | 31 | ```js 32 | const MyComponent = () => { 33 | const postsIndex = useMMKVStorage("postsIndex",MMKV,[]); 34 | const [posts] = useIndex(postsIndex,"object" MMKV); 35 | 36 | return 37 | 41 | 42 | 43 | } 44 | ``` 45 | -------------------------------------------------------------------------------- /docs/usemmkvref.md: -------------------------------------------------------------------------------- 1 | # useMMKVRef hook 2 | 3 | A persisted ref that will always write every change to it's `current` value to storage. It doesn't matter if you reload the app or restart it. Everything will be in place on app load. Let's see how this works: 4 | 5 | ### `useMMKVRef` 6 | A `useRef` like hook that allows you to easily manage values in storage. 7 | 8 | **Arguments** 9 | 10 | | Name | Required | Type | Description | 11 | |-------------------|----------|-------------------|--------------------------------------------------------------------| 12 | | key | yes | String | The key against which to get the value | 13 | | `MMKVStorage.API` | yes | `MMKVStorage.API` | MMKV storage instance created from `new MMKVLoader().initialize()` | 14 | | defaultValue | no | String | Pass a default value for the hook if any | 15 | 16 | **returns:** `{current: RefObject, reset: () => void}`. 17 | 18 | The `reset` function can be used to reset the value to `defaultValue`. 19 | 20 | ### `createMMKVRefHookForStorage` 21 | A helper function that returns `useMMKVRef` which can then be used inside a component. 22 | **Arguments** 23 | 24 | | Name | Required | Type | Description | 25 | |-------------------|----------|---------|--------------------------------------------------------------------| 26 | | `MMKVStorage.API` | yes | boolean | MMKV storage instance created from `new MMKVLoader().initialize()` | 27 | 28 | **returns:** `useMMKVRef(key:string, defaultValue:any)` 29 | 30 | ### How to use 31 | 32 | Import `MMKVLoader` and `useMMKVRef` Hook. 33 | 34 | ```js 35 | import { MMKVLoader, useMMKVRef } from "react-native-mmkv-storage"; 36 | ``` 37 | 38 | Initialize the `MMKVLoader` instance. 39 | 40 | ```js 41 | const MMKV = new MMKVLoader().initialize(); 42 | ``` 43 | 44 | Next, in our component we are going to register our hook. 45 | 46 | ```jsx 47 | const App = () => { 48 | const name = useMMKVRef("username", MMKV); 49 | 50 | return ( 51 | 52 | { 55 | name.current = value; 56 | }} 57 | /> 58 | 59 | ); 60 | }; 61 | ``` 62 | Now whenever you update `current` value, it will be stored in storage, until you call `reset` function; 63 | 64 | -------------------------------------------------------------------------------- /docs/workingwithencryption.md: -------------------------------------------------------------------------------- 1 | # Working with encryption 2 | 3 | So now you know how to create an instance of MMKV. Lets dig into encryption and how to handle different use cases. 4 | 5 | An already created instance of MMKV can be encrypted without destroying it. 6 | 7 | Lets suppose you have this MMKV instance created during first app startup. 8 | 9 | ```js 10 | import { MMKVLoader } from "react-native-mmkv-storage"; 11 | 12 | const MMKV = new MMKVStorage().initialize(); 13 | ``` 14 | 15 | Now later you might want to encrypt it somwhere during the lifecycle of an app. So you can then simply do this: 16 | 17 | Let the library do everything **(RECOMMENDED)** 18 | 19 | ```js 20 | await MMKV.encryption.encrypt(); 21 | ``` 22 | 23 | Or provide your own password 24 | 25 | ```js 26 | await MMKV.encryption.encrypt("myencryptionkey"); 27 | ``` 28 | 29 | if you want the key to be stored in the secure storage 30 | 31 | ```js 32 | await MMKV.encryption.encrypt("myencryptionkey", true); 33 | ``` 34 | 35 | This will encrypt the storage. Remember that **you will not need to change your Loader function** for this instance afterwards. The Library will handle everything itself 36 | 37 | Now lets say you want to decrypt it later on. 38 | 39 | ```js 40 | await MMKV.encryption.decrypt(); 41 | ``` 42 | 43 | Or you might want to update the encryption key to a newer one: 44 | 45 | ```js 46 | await MMKV.encryption.changeEncryptionKey(); 47 | ``` 48 | 49 | Or provide your own password, it will be stored securely 50 | 51 | ```js 52 | await MMKV.encryption.changeEncryptionKey("myencryptionkey"); 53 | ``` 54 | 55 | If you want it to be stored in the secure storage 56 | 57 | ```js 58 | await MMKV.encryption.changeEncryptionKey("myencryptionkey",true); 59 | ``` 60 | 61 | Remember that whenever you encrypt your storage, a strongpassword is automatically generated, stored and used to decrypt it behind the scenes. It is recommended to use it since it handles everything smoothly. However you can choose to not do so, in such a case, things get a little complicated. 62 | 63 | Lets say you created an MMKV Instance with encryption and you did not store the password so. 64 | 65 | ```js 66 | const MMKV = new MMKVLoader() 67 | .withEncryption() 68 | .encryptWithCustomKey("oldkey") 69 | .initialize(); 70 | ``` 71 | 72 | Now if you change the encryption key 73 | 74 | ```js 75 | await MMKV.encryption.changeEncryptionKey("newkey"); 76 | ``` 77 | 78 | When the app starts again on next start up. You will need to update the value of key in the Loader function or your database will not load. 79 | 80 | So on next app startup: 81 | 82 | ```js 83 | const MMKV = new MMKVLoader() 84 | .withEncryption() 85 | .encryptWithCustomKey("newkey") 86 | .initialize(); 87 | ``` 88 | 89 | How you handle the change from old key to the new one, is up to you. 90 | -------------------------------------------------------------------------------- /example/.bundle/config: -------------------------------------------------------------------------------- 1 | BUNDLE_PATH: "vendor/bundle" 2 | BUNDLE_FORCE_RUBY_PLATFORM: 1 3 | -------------------------------------------------------------------------------- /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /example/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version 4 | ruby ">= 2.6.10" 5 | 6 | # Exclude problematic versions of cocoapods and activesupport that causes build failures. 7 | gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1' 8 | gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0' 9 | gem 'xcodeproj', '< 1.26.0' 10 | -------------------------------------------------------------------------------- /example/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.7) 5 | base64 6 | nkf 7 | rexml 8 | activesupport (7.1.5) 9 | base64 10 | benchmark (>= 0.3) 11 | bigdecimal 12 | concurrent-ruby (~> 1.0, >= 1.0.2) 13 | connection_pool (>= 2.2.5) 14 | drb 15 | i18n (>= 1.6, < 2) 16 | logger (>= 1.4.2) 17 | minitest (>= 5.1) 18 | mutex_m 19 | securerandom (>= 0.3) 20 | tzinfo (~> 2.0) 21 | addressable (2.8.7) 22 | public_suffix (>= 2.0.2, < 7.0) 23 | algoliasearch (1.27.5) 24 | httpclient (~> 2.8, >= 2.8.3) 25 | json (>= 1.5.1) 26 | atomos (0.1.3) 27 | base64 (0.2.0) 28 | benchmark (0.3.0) 29 | bigdecimal (3.1.8) 30 | claide (1.1.0) 31 | cocoapods (1.15.2) 32 | addressable (~> 2.8) 33 | claide (>= 1.0.2, < 2.0) 34 | cocoapods-core (= 1.15.2) 35 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 36 | cocoapods-downloader (>= 2.1, < 3.0) 37 | cocoapods-plugins (>= 1.0.0, < 2.0) 38 | cocoapods-search (>= 1.0.0, < 2.0) 39 | cocoapods-trunk (>= 1.6.0, < 2.0) 40 | cocoapods-try (>= 1.1.0, < 2.0) 41 | colored2 (~> 3.1) 42 | escape (~> 0.0.4) 43 | fourflusher (>= 2.3.0, < 3.0) 44 | gh_inspector (~> 1.0) 45 | molinillo (~> 0.8.0) 46 | nap (~> 1.0) 47 | ruby-macho (>= 2.3.0, < 3.0) 48 | xcodeproj (>= 1.23.0, < 2.0) 49 | cocoapods-core (1.15.2) 50 | activesupport (>= 5.0, < 8) 51 | addressable (~> 2.8) 52 | algoliasearch (~> 1.0) 53 | concurrent-ruby (~> 1.1) 54 | fuzzy_match (~> 2.0.4) 55 | nap (~> 1.0) 56 | netrc (~> 0.11) 57 | public_suffix (~> 4.0) 58 | typhoeus (~> 1.0) 59 | cocoapods-deintegrate (1.0.5) 60 | cocoapods-downloader (2.1) 61 | cocoapods-plugins (1.0.0) 62 | nap 63 | cocoapods-search (1.0.1) 64 | cocoapods-trunk (1.6.0) 65 | nap (>= 0.8, < 2.0) 66 | netrc (~> 0.11) 67 | cocoapods-try (1.2.0) 68 | colored2 (3.1.2) 69 | concurrent-ruby (1.3.4) 70 | connection_pool (2.4.1) 71 | drb (2.2.1) 72 | escape (0.0.4) 73 | ethon (0.16.0) 74 | ffi (>= 1.15.0) 75 | ffi (1.17.0) 76 | fourflusher (2.3.1) 77 | fuzzy_match (2.0.4) 78 | gh_inspector (1.1.3) 79 | httpclient (2.8.3) 80 | i18n (1.14.6) 81 | concurrent-ruby (~> 1.0) 82 | json (2.7.5) 83 | logger (1.6.1) 84 | minitest (5.25.1) 85 | molinillo (0.8.0) 86 | mutex_m (0.2.0) 87 | nanaimo (0.3.0) 88 | nap (1.1.0) 89 | netrc (0.11.0) 90 | nkf (0.2.0) 91 | public_suffix (4.0.7) 92 | rexml (3.3.9) 93 | ruby-macho (2.5.1) 94 | securerandom (0.3.1) 95 | typhoeus (1.4.1) 96 | ethon (>= 0.9.0) 97 | tzinfo (2.0.6) 98 | concurrent-ruby (~> 1.0) 99 | xcodeproj (1.25.1) 100 | CFPropertyList (>= 2.3.3, < 4.0) 101 | atomos (~> 0.1.3) 102 | claide (>= 1.0.2, < 2.0) 103 | colored2 (~> 3.1) 104 | nanaimo (~> 0.3.0) 105 | rexml (>= 3.3.6, < 4.0) 106 | 107 | PLATFORMS 108 | ruby 109 | 110 | DEPENDENCIES 111 | activesupport (>= 6.1.7.5, != 7.1.0) 112 | cocoapods (>= 1.13, != 1.15.1, != 1.15.0) 113 | xcodeproj (< 1.26.0) 114 | 115 | RUBY VERSION 116 | ruby 2.7.8p225 117 | 118 | BUNDLED WITH 119 | 2.4.10 120 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | This is a new [**React Native**](https://reactnative.dev) project, bootstrapped using [`@react-native-community/cli`](https://github.com/react-native-community/cli). 2 | 3 | # Getting Started 4 | 5 | >**Note**: Make sure you have completed the [React Native - Environment Setup](https://reactnative.dev/docs/environment-setup) instructions till "Creating a new application" step, before proceeding. 6 | 7 | ## Step 1: Start the Metro Server 8 | 9 | First, you will need to start **Metro**, the JavaScript _bundler_ that ships _with_ React Native. 10 | 11 | To start Metro, run the following command from the _root_ of your React Native project: 12 | 13 | ```bash 14 | # using npm 15 | npm start 16 | 17 | # OR using Yarn 18 | yarn start 19 | ``` 20 | 21 | ## Step 2: Start your Application 22 | 23 | Let Metro Bundler run in its _own_ terminal. Open a _new_ terminal from the _root_ of your React Native project. Run the following command to start your _Android_ or _iOS_ app: 24 | 25 | ### For Android 26 | 27 | ```bash 28 | # using npm 29 | npm run android 30 | 31 | # OR using Yarn 32 | yarn android 33 | ``` 34 | 35 | ### For iOS 36 | 37 | ```bash 38 | # using npm 39 | npm run ios 40 | 41 | # OR using Yarn 42 | yarn ios 43 | ``` 44 | 45 | If everything is set up _correctly_, you should see your new app running in your _Android Emulator_ or _iOS Simulator_ shortly provided you have set up your emulator/simulator correctly. 46 | 47 | This is one way to run your app — you can also run it directly from within Android Studio and Xcode respectively. 48 | 49 | ## Step 3: Modifying your App 50 | 51 | Now that you have successfully run the app, let's modify it. 52 | 53 | 1. Open `App.tsx` in your text editor of choice and edit some lines. 54 | 2. For **Android**: Press the R key twice or select **"Reload"** from the **Developer Menu** (Ctrl + M (on Window and Linux) or Cmd ⌘ + M (on macOS)) to see your changes! 55 | 56 | For **iOS**: Hit Cmd ⌘ + R in your iOS Simulator to reload the app and see your changes! 57 | 58 | ## Congratulations! :tada: 59 | 60 | You've successfully run and modified your React Native App. :partying_face: 61 | 62 | ### Now what? 63 | 64 | - If you want to add this new React Native code to an existing application, check out the [Integration guide](https://reactnative.dev/docs/integration-with-existing-apps). 65 | - If you're curious to learn more about React Native, check out the [Introduction to React Native](https://reactnative.dev/docs/getting-started). 66 | 67 | # Troubleshooting 68 | 69 | If you can't get this to work, see the [Troubleshooting](https://reactnative.dev/docs/troubleshooting) page. 70 | 71 | # Learn More 72 | 73 | To learn more about React Native, take a look at the following resources: 74 | 75 | - [React Native Website](https://reactnative.dev) - learn more about React Native. 76 | - [Getting Started](https://reactnative.dev/docs/environment-setup) - an **overview** of React Native and how setup your environment. 77 | - [Learn the Basics](https://reactnative.dev/docs/getting-started) - a **guided tour** of the React Native **basics**. 78 | - [Blog](https://reactnative.dev/blog) - read the latest official React Native **Blog** posts. 79 | - [`@facebook/react-native`](https://github.com/facebook/react-native) - the Open Source; GitHub **repository** for React Native. 80 | -------------------------------------------------------------------------------- /example/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ammarahm-ed/react-native-mmkv-storage/09790b470e6162d1aee662a73784a9423040100a/example/android/app/debug.keystore -------------------------------------------------------------------------------- /example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 13 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/mmkvstorage/example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package mmkvstorage.example 2 | 3 | import com.facebook.react.ReactActivity 4 | import com.facebook.react.ReactActivityDelegate 5 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled 6 | import com.facebook.react.defaults.DefaultReactActivityDelegate 7 | 8 | class MainActivity : ReactActivity() { 9 | 10 | /** 11 | * Returns the name of the main component registered from JavaScript. This is used to schedule 12 | * rendering of the component. 13 | */ 14 | override fun getMainComponentName(): String = "MmkvStorageExample" 15 | 16 | /** 17 | * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] 18 | * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] 19 | */ 20 | override fun createReactActivityDelegate(): ReactActivityDelegate = 21 | DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) 22 | } 23 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/mmkvstorage/example/MainApplication.kt: -------------------------------------------------------------------------------- 1 | package mmkvstorage.example 2 | 3 | import android.app.Application 4 | import com.facebook.react.PackageList 5 | import com.facebook.react.ReactApplication 6 | import com.facebook.react.ReactHost 7 | import com.facebook.react.ReactNativeHost 8 | import com.facebook.react.ReactPackage 9 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load 10 | import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost 11 | import com.facebook.react.defaults.DefaultReactNativeHost 12 | import com.facebook.react.soloader.OpenSourceMergedSoMapping 13 | import com.facebook.soloader.SoLoader 14 | 15 | class MainApplication : Application(), ReactApplication { 16 | 17 | override val reactNativeHost: ReactNativeHost = 18 | object : DefaultReactNativeHost(this) { 19 | override fun getPackages(): List = 20 | PackageList(this).packages.apply { 21 | // Packages that cannot be autolinked yet can be added manually here, for example: 22 | // add(MyReactNativePackage()) 23 | } 24 | 25 | override fun getJSMainModuleName(): String = "index" 26 | 27 | override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG 28 | 29 | override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED 30 | override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED 31 | } 32 | 33 | override val reactHost: ReactHost 34 | get() = getDefaultReactHost(applicationContext, reactNativeHost) 35 | 36 | override fun onCreate() { 37 | super.onCreate() 38 | SoLoader.init(this, OpenSourceMergedSoMapping) 39 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { 40 | // If you opted-in for the New Architecture, we load the native entry point for this app. 41 | load() 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/rn_edit_text_material.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 22 | 23 | 24 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ammarahm-ed/react-native-mmkv-storage/09790b470e6162d1aee662a73784a9423040100a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ammarahm-ed/react-native-mmkv-storage/09790b470e6162d1aee662a73784a9423040100a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ammarahm-ed/react-native-mmkv-storage/09790b470e6162d1aee662a73784a9423040100a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ammarahm-ed/react-native-mmkv-storage/09790b470e6162d1aee662a73784a9423040100a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ammarahm-ed/react-native-mmkv-storage/09790b470e6162d1aee662a73784a9423040100a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ammarahm-ed/react-native-mmkv-storage/09790b470e6162d1aee662a73784a9423040100a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ammarahm-ed/react-native-mmkv-storage/09790b470e6162d1aee662a73784a9423040100a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ammarahm-ed/react-native-mmkv-storage/09790b470e6162d1aee662a73784a9423040100a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ammarahm-ed/react-native-mmkv-storage/09790b470e6162d1aee662a73784a9423040100a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ammarahm-ed/react-native-mmkv-storage/09790b470e6162d1aee662a73784a9423040100a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | MmkvStorageExample 3 | 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | buildToolsVersion = "35.0.0" 4 | minSdkVersion = 24 5 | compileSdkVersion = 35 6 | targetSdkVersion = 34 7 | ndkVersion = "26.1.10909125" 8 | kotlinVersion = "1.9.24" 9 | } 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | dependencies { 15 | classpath("com.android.tools.build:gradle") 16 | classpath("com.facebook.react:react-native-gradle-plugin") 17 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin") 18 | } 19 | } 20 | 21 | apply plugin: "com.facebook.react.rootproject" 22 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m 13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | # AndroidX package structure to make it clearer which packages are bundled with the 21 | # Android operating system, and which are packaged with your app's APK 22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 23 | android.useAndroidX=true 24 | 25 | # Use this property to specify which architecture you want to build. 26 | # You can also override it from the CLI using 27 | # ./gradlew -PreactNativeArchitectures=x86_64 28 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 29 | 30 | # Use this property to enable support to the new architecture. 31 | # This will allow you to use TurboModules and the Fabric render in 32 | # your application. You should enable this flag either if you want 33 | # to write custom TurboModules/Fabric components OR use libraries that 34 | # are providing them. 35 | newArchEnabled=true 36 | 37 | # Use this property to enable or disable the Hermes JS engine. 38 | # If set to false, you will be using JSC instead. 39 | hermesEnabled=true 40 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ammarahm-ed/react-native-mmkv-storage/09790b470e6162d1aee662a73784a9423040100a/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") } 2 | plugins { id("com.facebook.react.settings") } 3 | extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() } 4 | rootProject.name = 'mmkvstorage.example' 5 | include ':app' 6 | includeBuild('../node_modules/@react-native/gradle-plugin') 7 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MmkvStorageExample", 3 | "displayName": "MmkvStorageExample" 4 | } 5 | -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:@react-native/babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | import { AppRegistry } from 'react-native'; 2 | import App from './src/App'; 3 | import { name as appName } from './app.json'; 4 | 5 | AppRegistry.registerComponent(appName, () => App); 6 | -------------------------------------------------------------------------------- /example/ios/.xcode.env: -------------------------------------------------------------------------------- 1 | # This `.xcode.env` file is versioned and is used to source the environment 2 | # used when running script phases inside Xcode. 3 | # To customize your local environment, you can create an `.xcode.env.local` 4 | # file that is not versioned. 5 | 6 | # NODE_BINARY variable contains the PATH to the node executable. 7 | # 8 | # Customize the NODE_BINARY variable here. 9 | # For example, to use nvm with brew, add the following line 10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use 11 | export NODE_BINARY=$(command -v node) 12 | -------------------------------------------------------------------------------- /example/ios/.xcode.env.local: -------------------------------------------------------------------------------- 1 | export NODE_BINARY=/usr/local/bin/node 2 | -------------------------------------------------------------------------------- /example/ios/File.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // MmkvStorageExample 4 | // 5 | 6 | import Foundation 7 | -------------------------------------------------------------------------------- /example/ios/MmkvStorageExample-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | -------------------------------------------------------------------------------- /example/ios/MmkvStorageExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/MmkvStorageExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/MmkvStorageExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : RCTAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /example/ios/MmkvStorageExample/AppDelegate.mm: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | 5 | @implementation AppDelegate 6 | 7 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 8 | { 9 | self.moduleName = @"MmkvStorageExample"; 10 | // You can add your custom initial props in the dictionary below. 11 | // They will be passed down to the ViewController used by React Native. 12 | self.initialProps = @{}; 13 | 14 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 15 | } 16 | 17 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 18 | { 19 | return [self bundleURL]; 20 | } 21 | 22 | - (NSURL *)bundleURL 23 | { 24 | #if DEBUG 25 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; 26 | #else 27 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 28 | #endif 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /example/ios/MmkvStorageExample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "scale" : "1x", 46 | "size" : "1024x1024" 47 | } 48 | ], 49 | "info" : { 50 | "author" : "xcode", 51 | "version" : 1 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /example/ios/MmkvStorageExample/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/ios/MmkvStorageExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | MmkvStorageExample 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(CURRENT_PROJECT_VERSION) 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | 30 | NSAllowsArbitraryLoads 31 | 32 | NSAllowsLocalNetworking 33 | 34 | 35 | NSLocationWhenInUseUsageDescription 36 | 37 | UILaunchStoryboardName 38 | LaunchScreen 39 | UIRequiredDeviceCapabilities 40 | 41 | arm64 42 | 43 | UISupportedInterfaceOrientations 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | UIViewControllerBasedStatusBarAppearance 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /example/ios/MmkvStorageExample/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyAccessedAPITypes 6 | 7 | 8 | NSPrivacyAccessedAPIType 9 | NSPrivacyAccessedAPICategoryFileTimestamp 10 | NSPrivacyAccessedAPITypeReasons 11 | 12 | C617.1 13 | 14 | 15 | 16 | NSPrivacyAccessedAPIType 17 | NSPrivacyAccessedAPICategoryUserDefaults 18 | NSPrivacyAccessedAPITypeReasons 19 | 20 | CA92.1 21 | 22 | 23 | 24 | NSPrivacyAccessedAPIType 25 | NSPrivacyAccessedAPICategorySystemBootTime 26 | NSPrivacyAccessedAPITypeReasons 27 | 28 | 35F9.1 29 | 30 | 31 | 32 | NSPrivacyCollectedDataTypes 33 | 34 | NSPrivacyTracking 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/MmkvStorageExample/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | @autoreleasepool { 8 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /example/ios/MmkvStorageExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/ios/MmkvStorageExampleTests/MmkvStorageExampleTests.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import 5 | #import 6 | 7 | #define TIMEOUT_SECONDS 600 8 | #define TEXT_TO_LOOK_FOR @"Welcome to React" 9 | 10 | @interface MmkvStorageExampleTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation MmkvStorageExampleTests 15 | 16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test 17 | { 18 | if (test(view)) { 19 | return YES; 20 | } 21 | for (UIView *subview in [view subviews]) { 22 | if ([self findSubviewInView:subview matching:test]) { 23 | return YES; 24 | } 25 | } 26 | return NO; 27 | } 28 | 29 | - (void)testRendersWelcomeScreen 30 | { 31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 33 | BOOL foundElement = NO; 34 | 35 | __block NSString *redboxError = nil; 36 | #ifdef DEBUG 37 | RCTSetLogFunction( 38 | ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 39 | if (level >= RCTLogLevelError) { 40 | redboxError = message; 41 | } 42 | }); 43 | #endif 44 | 45 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 46 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 47 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 48 | 49 | foundElement = [self findSubviewInView:vc.view 50 | matching:^BOOL(UIView *view) { 51 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 52 | return YES; 53 | } 54 | return NO; 55 | }]; 56 | } 57 | 58 | #ifdef DEBUG 59 | RCTSetLogFunction(RCTDefaultLogFunction); 60 | #endif 61 | 62 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 63 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Resolve react_native_pods.rb with node to allow for hoisting 2 | require Pod::Executable.execute_command('node', ['-p', 3 | 'require.resolve( 4 | "react-native/scripts/react_native_pods.rb", 5 | {paths: [process.argv[1]]}, 6 | )', __dir__]).strip 7 | 8 | platform :ios, min_ios_version_supported 9 | prepare_react_native_project! 10 | 11 | linkage = ENV['USE_FRAMEWORKS'] 12 | if linkage != nil 13 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green 14 | use_frameworks! :linkage => linkage.to_sym 15 | end 16 | 17 | target 'MmkvStorageExample' do 18 | config = use_native_modules! 19 | 20 | use_react_native!( 21 | :path => config[:reactNativePath], 22 | # An absolute path to your application root. 23 | :app_path => "#{Pod::Config.instance.installation_root}/.." 24 | ) 25 | 26 | target 'MmkvStorageExampleTests' do 27 | inherit! :complete 28 | # Pods for testing 29 | end 30 | 31 | post_install do |installer| 32 | # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 33 | react_native_post_install( 34 | installer, 35 | config[:reactNativePath], 36 | :mac_catalyst_enabled => false, 37 | # :ccache_enabled => true 38 | ) 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /example/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'react-native', 3 | }; 4 | -------------------------------------------------------------------------------- /example/metro.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { getDefaultConfig } = require('@react-native/metro-config'); 3 | const { getConfig } = require('react-native-builder-bob/metro-config'); 4 | const pkg = require('../package.json'); 5 | const root = path.resolve(__dirname, '..'); 6 | 7 | const config = getConfig(getDefaultConfig(__dirname), { 8 | root, 9 | pkg, 10 | project: __dirname 11 | }); 12 | 13 | /** 14 | * Metro configuration 15 | * https://facebook.github.io/metro/docs/configuration 16 | * 17 | * @type {import('metro-config').MetroConfig} 18 | */ 19 | module.exports = { 20 | ...config, 21 | resolver: { 22 | ...config.resolver, 23 | resolveRequest: (context, moduleName, platform) => { 24 | if (moduleName === 'react') { 25 | return { 26 | filePath: path.resolve(path.join(__dirname, '../node_modules', 'react', 'index.js')), 27 | type: 'sourceFile' 28 | }; 29 | } 30 | 31 | if (moduleName === pkg.name) { 32 | return { 33 | filePath: path.resolve(path.join(__dirname, '../dist', 'index.js')), 34 | type: 'sourceFile' 35 | }; 36 | } 37 | return context.resolveRequest(context, moduleName, platform); 38 | } 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-mmkv-storage-example", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "android": "react-native run-android", 7 | "ios": "react-native run-ios", 8 | "start": "react-native start", 9 | "build:android": "react-native build-android --extra-params \"--no-daemon --console=plain -PreactNativeArchitectures=arm64-v8a\"", 10 | "build:ios": "react-native build-ios --scheme MmkvStorageExample --mode Debug --extra-params \"-sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO\"" 11 | }, 12 | "dependencies": { 13 | "react": "18.3.1", 14 | "react-native": "0.76.1" 15 | }, 16 | "devDependencies": { 17 | "@babel/core": "^7.25.2", 18 | "@babel/preset-env": "^7.25.3", 19 | "@babel/runtime": "^7.25.0", 20 | "@react-native-community/cli": "15.0.0", 21 | "@react-native-community/cli-platform-android": "15.0.0", 22 | "@react-native-community/cli-platform-ios": "15.0.0", 23 | "@react-native/babel-preset": "0.76.1", 24 | "@react-native/metro-config": "0.76.1", 25 | "@react-native/typescript-config": "0.76.1", 26 | "react-native-builder-bob": "^0.30.3" 27 | }, 28 | "engines": { 29 | "node": ">=18" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /example/react-native.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const pkg = require('../package.json'); 3 | 4 | module.exports = { 5 | project: { 6 | ios: { 7 | automaticPodsInstallation: true, 8 | }, 9 | }, 10 | dependencies: { 11 | [pkg.name]: { 12 | root: path.join(__dirname, '..'), 13 | }, 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@react-native/typescript-config/tsconfig.json", 3 | "compilerOptions": { 4 | "lib": ["DOM", "ES2015"], 5 | "paths": { 6 | "react-native-mmkv-storage": ["../dist/index"] 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import MMKVInstance from './src/mmkvinstance'; 2 | import { useIndex } from './src/hooks/useIndex'; 3 | import { create, useMMKVStorage } from './src/hooks/useMMKV'; 4 | import { createMMKVRefHookForStorage, useMMKVRef } from './src/hooks/useMMKVRef'; 5 | import { getCurrentMMKVInstanceIDs } from './src/initializer'; 6 | import MMKVLoader from './src/mmkvloader'; 7 | import IDStore from './src/mmkv/IDStore'; 8 | import { init, isLoaded } from './src/mmkv/init'; 9 | import mmkvJsiModule, { mmkvBridgeModule } from './src/module'; 10 | import { IOSAccessibleStates, ProcessingModes } from './src/utils'; 11 | 12 | const MMKVStorage = { 13 | /** 14 | * @deprecated Use `import {MMKVLoader} from "react-native-mmkv-storage`" 15 | */ 16 | Loader: MMKVLoader, 17 | /** 18 | * @deprecated Use `import {MMKVInstance} from "react-native-mmkv-storage`" 19 | */ 20 | API: MMKVInstance, 21 | /** 22 | * @deprecated Use `import {ProcessingModes} from "react-native-mmkv-storage`" 23 | */ 24 | MODES: ProcessingModes, 25 | /** 26 | * @deprecated Use `import {IOSAccessibleStates} from "react-native-mmkv-storage`" 27 | */ 28 | ACCESSIBLE: IOSAccessibleStates, 29 | /** 30 | * @deprecated Use `import {getAllMMKVInstanceIDs} from "react-native-mmkv-storage`" 31 | */ 32 | getAllMMKVInstanceIDs: IDStore.getAllMMKVInstanceIDs, 33 | /** 34 | * @deprecated Use `import {getCurrentMMKVInstanceIDs} from "react-native-mmkv-storage`" 35 | */ 36 | getCurrentMMKVInstanceIDs: getCurrentMMKVInstanceIDs, 37 | /** 38 | * @deprecated Use `import {IDSTORE_ID} from "react-native-mmkv-storage`" 39 | */ 40 | IDSTORE_ID: IDStore.STORE_ID, 41 | _jsiModule: mmkvJsiModule, 42 | _bridgeModule: mmkvBridgeModule 43 | }; 44 | 45 | export default MMKVStorage; 46 | 47 | const { getAllMMKVInstanceIDs, STORE_ID: IDSTORE_ID } = IDStore; 48 | 49 | export { 50 | useMMKVStorage, 51 | create, 52 | useIndex, 53 | isLoaded, 54 | init, 55 | MMKVInstance, 56 | MMKVLoader, 57 | ProcessingModes, 58 | IOSAccessibleStates, 59 | getCurrentMMKVInstanceIDs, 60 | getAllMMKVInstanceIDs, 61 | IDSTORE_ID, 62 | createMMKVRefHookForStorage, 63 | useMMKVRef 64 | }; 65 | -------------------------------------------------------------------------------- /ios/MMKVStorage.h: -------------------------------------------------------------------------------- 1 | 2 | #ifdef RCT_NEW_ARCH_ENABLED 3 | #import "MMKVStorageSpec.h" 4 | @interface MMKVStorage : NSObject 5 | @property (nonatomic, assign) BOOL setBridgeOnMainQueue; 6 | #else 7 | #import 8 | #import 9 | 10 | @interface MMKVStorage : NSObject 11 | 12 | @property (nonatomic, assign) BOOL setBridgeOnMainQueue; 13 | #endif 14 | @end 15 | 16 | 17 | -------------------------------------------------------------------------------- /ios/SecureStorage.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface SecureStorage: NSObject 5 | 6 | 7 | 8 | - (void) setSecureKey: (nonnull NSString *)key value:(nonnull NSString *)value 9 | options: (nonnull NSDictionary *)options; 10 | - (nullable NSString *) getSecureKey:(nonnull NSString *)key; 11 | - (bool) secureKeyExists:(nonnull NSString *)key; 12 | - (void) removeSecureKey:(nonnull NSString *)key; 13 | 14 | - (BOOL)searchKeychainCopyMatchingExists:(nonnull NSString *)identifier; 15 | 16 | - (nonnull NSString *)searchKeychainCopyMatching:(nonnull NSString *)identifier; 17 | 18 | - (nonnull NSMutableDictionary *)newSearchDictionary:(nonnull NSString *)identifier; 19 | 20 | - (BOOL)createKeychainValue:(nonnull NSString *)value forIdentifier:(nonnull NSString *)identifier options: (NSDictionary * __nullable)options; 21 | 22 | - (BOOL)updateKeychainValue:(nonnull NSString *)password forIdentifier:(nonnull NSString *)identifier options:(NSDictionary * __nullable)options; 23 | 24 | - (BOOL)deleteKeychainValue:(nonnull NSString *)identifier; 25 | 26 | - (void)clearSecureKeyStore; 27 | 28 | - (void)handleAppUninstallation; 29 | 30 | - (void) setServiceName: (nonnull NSString *)serviceName; 31 | 32 | @end 33 | 34 | -------------------------------------------------------------------------------- /ios/YeetJSIUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // YeetJSIUTils.h 3 | // yeet 4 | // 5 | // Created by Jarred WSumner on 1/30/20. 6 | // Copyright © 2020 Facebook. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | using namespace facebook; 14 | /** 15 | * All static helper functions are ObjC++ specific. 16 | */ 17 | jsi::Value convertNSNumberToJSIBoolean(jsi::Runtime &runtime, NSNumber *value); 18 | jsi::Value convertNSNumberToJSINumber(jsi::Runtime &runtime, NSNumber *value); 19 | jsi::String convertNSStringToJSIString(jsi::Runtime &runtime, NSString *value); 20 | jsi::Value convertObjCObjectToJSIValue(jsi::Runtime &runtime, id value);; 21 | jsi::Object convertNSDictionaryToJSIObject(jsi::Runtime &runtime, NSDictionary *value); 22 | jsi::Array convertNSArrayToJSIArray(jsi::Runtime &runtime, NSArray *value); 23 | std::vector convertNSArrayToStdVector(jsi::Runtime &runtime, NSArray *value); 24 | jsi::Value convertObjCObjectToJSIValue(jsi::Runtime &runtime, id value); 25 | id convertJSIValueToObjCObject( 26 | jsi::Runtime &runtime, 27 | const jsi::Value &value); 28 | NSString* convertJSIStringToNSString(jsi::Runtime &runtime, const jsi::String &value); 29 | NSArray* convertJSIArrayToNSArray( 30 | jsi::Runtime &runtime, 31 | const jsi::Array &value); 32 | NSDictionary *convertJSIObjectToNSDictionary( 33 | jsi::Runtime &runtime, 34 | const jsi::Object &value); 35 | RCTResponseSenderBlock convertJSIFunctionToCallback( 36 | jsi::Runtime &runtime, 37 | const jsi::Function &value); 38 | id convertJSIValueToObjCObject( 39 | jsi::Runtime &runtime, 40 | const jsi::Value &value); 41 | RCTResponseSenderBlock convertJSIFunctionToCallback( 42 | jsi::Runtime &runtime, 43 | const jsi::Function &value); 44 | 45 | struct Promise { 46 | Promise(jsi::Runtime &rt, jsi::Function resolve, jsi::Function reject); 47 | 48 | void resolve(const jsi::Value &result); 49 | void reject(const std::string &error); 50 | 51 | jsi::Runtime &runtime_; 52 | jsi::Function resolve_; 53 | jsi::Function reject_; 54 | }; 55 | 56 | using PromiseSetupFunctionType = 57 | std::function)>; 58 | jsi::Value createPromiseAsJSIValue( 59 | jsi::Runtime &rt, 60 | const PromiseSetupFunctionType func); 61 | -------------------------------------------------------------------------------- /ios/generated/MMKVStorageSpec/MMKVStorageSpec-generated.mm: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). 3 | * 4 | * Do not edit this file as changes may cause incorrect behavior and will be lost 5 | * once the code is regenerated. 6 | * 7 | * @generated by codegen project: GenerateModuleObjCpp 8 | * 9 | * We create an umbrella header (and corresponding implementation) here since 10 | * Cxx compilation in BUCK has a limitation: source-code producing genrule()s 11 | * must have a single output. More files => more genrule()s => slower builds. 12 | */ 13 | 14 | #import "MMKVStorageSpec.h" 15 | 16 | 17 | @implementation NativeMMKVStorageSpecBase 18 | 19 | 20 | - (void)setEventEmitterCallback:(EventEmitterCallbackWrapper *)eventEmitterCallbackWrapper 21 | { 22 | _eventEmitterCallback = std::move(eventEmitterCallbackWrapper->_eventEmitterCallback); 23 | } 24 | @end 25 | 26 | 27 | namespace facebook::react { 28 | 29 | static facebook::jsi::Value __hostFunction_NativeMMKVStorageSpecJSI_install(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) { 30 | return static_cast(turboModule).invokeObjCMethod(rt, BooleanKind, "install", @selector(install), args, count); 31 | } 32 | 33 | NativeMMKVStorageSpecJSI::NativeMMKVStorageSpecJSI(const ObjCTurboModule::InitParams ¶ms) 34 | : ObjCTurboModule(params) { 35 | 36 | methodMap_["install"] = MethodMetadata {0, __hostFunction_NativeMMKVStorageSpecJSI_install}; 37 | 38 | } 39 | } // namespace facebook::react 40 | -------------------------------------------------------------------------------- /ios/generated/MMKVStorageSpec/MMKVStorageSpec.h: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). 3 | * 4 | * Do not edit this file as changes may cause incorrect behavior and will be lost 5 | * once the code is regenerated. 6 | * 7 | * @generated by codegen project: GenerateModuleObjCpp 8 | * 9 | * We create an umbrella header (and corresponding implementation) here since 10 | * Cxx compilation in BUCK has a limitation: source-code producing genrule()s 11 | * must have a single output. More files => more genrule()s => slower builds. 12 | */ 13 | 14 | #ifndef __cplusplus 15 | #error This file must be compiled as Obj-C++. If you are importing it, you must change your file extension to .mm. 16 | #endif 17 | 18 | // Avoid multiple includes of MMKVStorageSpec symbols 19 | #ifndef MMKVStorageSpec_H 20 | #define MMKVStorageSpec_H 21 | 22 | #import 23 | #import 24 | #import 25 | #import 26 | #import 27 | #import 28 | #import 29 | #import 30 | #import 31 | #import 32 | 33 | @protocol NativeMMKVStorageSpec 34 | 35 | - (NSNumber *)install; 36 | 37 | @end 38 | 39 | @interface NativeMMKVStorageSpecBase : NSObject { 40 | @protected 41 | facebook::react::EventEmitterCallback _eventEmitterCallback; 42 | } 43 | - (void)setEventEmitterCallback:(EventEmitterCallbackWrapper *)eventEmitterCallbackWrapper; 44 | 45 | 46 | @end 47 | 48 | namespace facebook::react { 49 | /** 50 | * ObjC++ class for module 'NativeMMKVStorage' 51 | */ 52 | class JSI_EXPORT NativeMMKVStorageSpecJSI : public ObjCTurboModule { 53 | public: 54 | NativeMMKVStorageSpecJSI(const ObjCTurboModule::InitParams ¶ms); 55 | }; 56 | } // namespace facebook::react 57 | 58 | #endif // MMKVStorageSpec_H 59 | -------------------------------------------------------------------------------- /ios/generated/MMKVStorageSpecJSI-generated.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). 3 | * 4 | * Do not edit this file as changes may cause incorrect behavior and will be lost 5 | * once the code is regenerated. 6 | * 7 | * @generated by codegen project: GenerateModuleCpp.js 8 | */ 9 | 10 | #include "MMKVStorageSpecJSI.h" 11 | 12 | namespace facebook::react { 13 | 14 | static jsi::Value __hostFunction_NativeMMKVStorageCxxSpecJSI_install(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { 15 | return static_cast(&turboModule)->install( 16 | rt 17 | ); 18 | } 19 | 20 | NativeMMKVStorageCxxSpecJSI::NativeMMKVStorageCxxSpecJSI(std::shared_ptr jsInvoker) 21 | : TurboModule("MMKVStorage", jsInvoker) { 22 | methodMap_["install"] = MethodMetadata {0, __hostFunction_NativeMMKVStorageCxxSpecJSI_install}; 23 | } 24 | 25 | 26 | } // namespace facebook::react 27 | -------------------------------------------------------------------------------- /ios/generated/MMKVStorageSpecJSI.h: -------------------------------------------------------------------------------- 1 | /** 2 | * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). 3 | * 4 | * Do not edit this file as changes may cause incorrect behavior and will be lost 5 | * once the code is regenerated. 6 | * 7 | * @generated by codegen project: GenerateModuleH.js 8 | */ 9 | 10 | #pragma once 11 | 12 | #include 13 | #include 14 | 15 | namespace facebook::react { 16 | 17 | 18 | class JSI_EXPORT NativeMMKVStorageCxxSpecJSI : public TurboModule { 19 | protected: 20 | NativeMMKVStorageCxxSpecJSI(std::shared_ptr jsInvoker); 21 | 22 | public: 23 | virtual bool install(jsi::Runtime &rt) = 0; 24 | 25 | }; 26 | 27 | template 28 | class JSI_EXPORT NativeMMKVStorageCxxSpec : public TurboModule { 29 | public: 30 | jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &propName) override { 31 | return delegate_.get(rt, propName); 32 | } 33 | 34 | static constexpr std::string_view kModuleName = "MMKVStorage"; 35 | 36 | protected: 37 | NativeMMKVStorageCxxSpec(std::shared_ptr jsInvoker) 38 | : TurboModule(std::string{NativeMMKVStorageCxxSpec::kModuleName}, jsInvoker), 39 | delegate_(reinterpret_cast(this), jsInvoker) {} 40 | 41 | 42 | private: 43 | class Delegate : public NativeMMKVStorageCxxSpecJSI { 44 | public: 45 | Delegate(T *instance, std::shared_ptr jsInvoker) : 46 | NativeMMKVStorageCxxSpecJSI(std::move(jsInvoker)), instance_(instance) { 47 | 48 | } 49 | 50 | bool install(jsi::Runtime &rt) override { 51 | static_assert( 52 | bridging::getParameterCount(&T::install) == 1, 53 | "Expected install(...) to have 1 parameters"); 54 | 55 | return bridging::callFromJs( 56 | rt, &T::install, jsInvoker_, instance_); 57 | } 58 | 59 | private: 60 | friend class NativeMMKVStorageCxxSpec; 61 | T *instance_; 62 | }; 63 | 64 | Delegate delegate_; 65 | }; 66 | 67 | } // namespace facebook::react 68 | -------------------------------------------------------------------------------- /jest/README.md: -------------------------------------------------------------------------------- 1 | ## Testing with Jest 2 | The library supports mocking the storage in memory to support read/write during tests. Follow the guide below to make it work with jest. 3 | 4 | Add `mmkvJestSetup.js` and `transformIgnorePatterns` in `jest.config.js`. 5 | 6 | ```js 7 | module.exports = { 8 | preset: 'react-native', 9 | setupFiles: [ 10 | './node_modules/react-native-mmkv-storage/jest/mmkvJestSetup.js', 11 | ], 12 | transformIgnorePatterns: ['/!node_modules\\/react-native-mmkv-storage/'], 13 | }; 14 | 15 | ``` 16 | 17 | You will need to mock the storage when your tests run as follows: 18 | 19 | ```js 20 | import 'react-native'; 21 | import MMKVStorage, {isLoaded} from 'react-native-mmkv-storage'; // Import the library as normal. 22 | 23 | describe('MMKV Storage mock functionality', () => { 24 | 25 | beforeEach(function () { 26 | // Install the in-memory adapter 27 | let mmkvMock = require('react-native-mmkv-storage/jest/dist/jest/memoryStore.js'); 28 | mmkvMock.unmock(); // Cleanup if already mocked 29 | mmkvMock.mock(); // Mock the storage 30 | }); 31 | 32 | // Use the storage methods as needed. Everything is mocked now 33 | it('Mock bindings are installed', () => { 34 | expect(isLoaded()).toBe(true); 35 | }); 36 | 37 | it('Init an instance', () => { 38 | let instance = new MMKVStorage.Loader().initialize(); 39 | expect(instance.instanceID).toBe('default'); 40 | expect(instance.getString('unknown')).toBe(null); 41 | }); 42 | 43 | }); 44 | ``` 45 | -------------------------------------------------------------------------------- /jest/dist/jest/memoryStore.d.ts: -------------------------------------------------------------------------------- 1 | export declare const unmock: () => boolean; 2 | export declare const mock: () => boolean; 3 | //# sourceMappingURL=memoryStore.d.ts.map -------------------------------------------------------------------------------- /jest/dist/jest/memoryStore.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"memoryStore.d.ts","sourceRoot":"","sources":["../../memoryStore.ts"],"names":[],"mappings":"AAoBA,eAAO,MAAM,MAAM,QAAO,OAMzB,CAAC;AAEF,eAAO,MAAM,IAAI,QAAO,OAqKvB,CAAC"} -------------------------------------------------------------------------------- /jest/dist/src/types/index.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/types/index.ts"],"names":[],"mappings":"AAAA,oBAAY,cAAc,GAAG;IAC3B;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,kBAAkB,EAAE,OAAO,CAAC;IAC5B;;;;OAIG;IACH,gBAAgB,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB;;OAEG;IACH,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB;;OAEG;IACH,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;;OAEG;IACH,WAAW,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,oBAAY,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;AAE5E,oBAAY,iBAAiB,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAE9E,oBAAY,SAAS,GAAG,aAAa,GAAG,WAAW,GAAG,aAAa,GAAG,UAAU,GAAG,YAAY,CAAC;AAEhG,oBAAY,aAAa,GAAG;IAC1B,iBAAiB,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;IAE5F,kBAAkB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,MAAM,CAAC;IACnE,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC/C,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9E,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IAC5C,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;IAE5C,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,SAAS,CAAC;IAC/E,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAEtE,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,SAAS,CAAC;IAC5E,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAEnE,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,SAAS,CAAC;IAC9E,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAErE,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,SAAS,CAAC;IAC/E,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAEtE,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,SAAS,CAAC;IAC9E,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;IAErE,eAAe,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,SAAS,CAAC;IAElE,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,EAAE,GAAG,SAAS,CAAC;IACrD,YAAY,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,KAAK,MAAM,EAAE,GAAG,SAAS,CAAC;IACpE,eAAe,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,SAAS,CAAC;IAElE,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,SAAS,CAAC;IAC/C,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,SAAS,CAAC;IAEtD,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,SAAS,CAAC;IACnE,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,SAAS,CAAC;CAClD,CAAC"} -------------------------------------------------------------------------------- /jest/dist/src/types/index.js: -------------------------------------------------------------------------------- 1 | export {}; 2 | -------------------------------------------------------------------------------- /jest/mmkvJestSetup.js: -------------------------------------------------------------------------------- 1 | if (!global.__fbBatchedBridgeConfig) { 2 | global.__fbBatchedBridgeConfig = {}; 3 | } 4 | 5 | if (!global.__turboModuleProxy) { 6 | global.__turboModuleProxy = name => { 7 | return {}; 8 | }; 9 | } 10 | 11 | require('react-native').NativeModules.MMKVNative = { 12 | install: jest.fn(() => true) 13 | }; 14 | -------------------------------------------------------------------------------- /jest/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["memoryStore.ts"], 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "jsx": "react-native", 6 | "module": "esnext", 7 | "moduleResolution": "node", 8 | "lib": ["es2017"], 9 | "declaration": true, 10 | "declarationMap": true, 11 | "outDir": "dist", 12 | "allowSyntheticDefaultImports": true, 13 | "esModuleInterop": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "noImplicitReturns": true, 17 | "noUnusedLocals": true, 18 | "noUnusedParameters": true, 19 | "pretty": true, 20 | "resolveJsonModule": true, 21 | "skipLibCheck": true, 22 | "strict": true, 23 | "allowJs": true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-mmkv-storage", 3 | "version": "0.11.2", 4 | "description": "This library aims to provide a fast & reliable solution for you data storage needs in react-native apps. It uses [MMKV](https://github.com/Tencent/MMKV) by Tencent under the hood on Android and iOS both that is used by their WeChat app(more than 1 Billion users). Unlike other storage solutions for React Native, this library lets you store any kind of data type, in any number of database instances, with or without encryption in a very fast and efficient way.", 5 | "main": "./dist/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "build": "tsc", 9 | "watch": "tsc --watch" 10 | }, 11 | "bin": { 12 | "mmkv-link": "./autolink/postlink/run.js" 13 | }, 14 | "keywords": [ 15 | "react-native", 16 | "fast-storage", 17 | "react", 18 | "native", 19 | "asyncstorage", 20 | "async-storage", 21 | "mmkv-database", 22 | "mmkv-storage", 23 | "encrypted-storage", 24 | "encryption", 25 | "mmkv", 26 | "android", 27 | "ios", 28 | "key-value-storage", 29 | "database", 30 | "fast", 31 | "storage", 32 | "persist-storage", 33 | "secure-storage", 34 | "redux-persist-storage", 35 | "react-native-mmkv-storage" 36 | ], 37 | "repository": { 38 | "type": "git", 39 | "url": "https://github.com/ammarahm-ed/react-native-mmkv-storage" 40 | }, 41 | "author": "Ammar Ahmed for @ammarahm-ed", 42 | "license": "MIT", 43 | "peerDependencies": { 44 | "react-native": "*" 45 | }, 46 | "devDependencies": { 47 | "@babel/core": "^7.26.0", 48 | "@babel/runtime": "^7.26.0", 49 | "@types/react": "18.3.1", 50 | "@types/react-native": "0.73.0", 51 | "babel-jest": "^29.6.3", 52 | "babel-plugin-module-resolver": "^5.0.0", 53 | "jest": "^29.2.1", 54 | "metro": "0.76.1", 55 | "metro-react-native-babel-preset": "0.76.1", 56 | "react": "18.3.1", 57 | "react-native": "0.76.1", 58 | "typescript": "^5.1.6", 59 | "eslint": "^8.57.1", 60 | "@react-native-community/eslint-config": "^3.2.0" 61 | }, 62 | "codegenConfig": { 63 | "name": "MMKVStorageSpec", 64 | "type": "modules", 65 | "jsSrcsDir": "./", 66 | "outputDir": { 67 | "ios": "ios/generated", 68 | "android": "android/generated" 69 | }, 70 | "android": { 71 | "javaPackageName": "com.ammarahmed.mmkv" 72 | }, 73 | "includesGeneratedCode": true 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /react-native-mmkv-storage.podspec: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) 4 | 5 | Pod::Spec.new do |s| 6 | s.name = package["name"] 7 | s.version = package["version"] 8 | s.summary = package["description"] 9 | s.homepage = "https://github.com/ammarahm-ed/react-native-mmkv-storage" 10 | s.license = package["license"] 11 | s.authors = package["author"] 12 | s.platform = :ios, "12.4" 13 | s.source = { :git => "#{s.homepage}", :tag => "V#{s.version}" } 14 | s.requires_arc = true 15 | 16 | if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then 17 | s.source_files = "ios/**/*.{h,m,mm,cpp}" 18 | else 19 | s.source_files = "ios/*.{h,m,mm,cpp}" 20 | end 21 | 22 | s.dependency 'MMKV', '~> 1.3.9' 23 | if respond_to?(:install_modules_dependencies, true) 24 | install_modules_dependencies(s) 25 | else 26 | s.dependency "React-Core" 27 | 28 | # Don't install the dependencies when we run `pod install` in the old architecture. 29 | if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then 30 | s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1" 31 | s.pod_target_xcconfig = { 32 | "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"", 33 | "OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1", 34 | "CLANG_CXX_LANGUAGE_STANDARD" => "c++17" 35 | } 36 | s.dependency "React-Codegen" 37 | s.dependency "RCT-Folly" 38 | s.dependency "RCTRequired" 39 | s.dependency "RCTTypeSafety" 40 | s.dependency "ReactCommon/turbomodule/core" 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /react-native.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('@react-native-community/cli-types').UserDependencyConfig} 3 | */ 4 | module.exports = { 5 | dependency: { 6 | platforms: { 7 | android: { 8 | cmakeListsPath: 'generated/jni/CMakeLists.txt', 9 | }, 10 | }, 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /src/eventmanager.ts: -------------------------------------------------------------------------------- 1 | class EventManager { 2 | _registry: { [name: string]: Function[] }; 3 | constructor() { 4 | this._registry = {}; 5 | } 6 | 7 | unsubscribeAll() { 8 | this._registry = {}; 9 | } 10 | 11 | subscribeMulti(names: string[], handler: Function) { 12 | names.forEach(name => { 13 | this.subscribe(name, handler); 14 | }); 15 | } 16 | 17 | subscribe(name: string, handler: Function) { 18 | if (!name || !handler) throw new Error('name and handler are required.'); 19 | if (!this._registry[name]) this._registry[name] = []; 20 | this._registry[name].push(handler); 21 | } 22 | 23 | unsubscribe(name: string, handler: Function) { 24 | if (!this._registry[name]) return; 25 | const index = this._registry[name].indexOf(handler); 26 | if (index <= -1) return; 27 | this._registry[name].splice(index, 1); 28 | } 29 | 30 | publish(name: string, ...args: any[]) { 31 | if (!this._registry[name]) return; 32 | const handlers = this._registry[name]; 33 | handlers.forEach(handler => { 34 | handler(...args); 35 | }); 36 | } 37 | 38 | async publishWithResult(name: string, ...args: any[]) { 39 | if (!this._registry[name]) return true; 40 | const handlers = this._registry[name]; 41 | if (handlers.length <= 0) return true; 42 | return await Promise.all(handlers.map(handler => handler(...args))); 43 | } 44 | } 45 | 46 | export default EventManager; 47 | -------------------------------------------------------------------------------- /src/handlers.ts: -------------------------------------------------------------------------------- 1 | import { currentInstancesStatus, initialize } from './initializer'; 2 | 3 | /** 4 | * 5 | * A handler function used to handle all the 6 | * calls made to native code. The purpose is 7 | * to make sure that the storage is initialized 8 | * before any read/write requests are sent to the 9 | * MMKV instance. 10 | * 11 | * 12 | * @param action The native function that will be called 13 | * @param args Arguments for the native function 14 | */ 15 | export function handleAction any | undefined | null>( 16 | action: T, 17 | ...args: any[] 18 | ): ReturnType | undefined { 19 | // The last argument is always the instance id. 20 | let id: string = args[args.length - 1]; 21 | if (!currentInstancesStatus[id]) { 22 | currentInstancesStatus[id] = initialize(id); 23 | } 24 | if (!action) return undefined; 25 | let result = action(...args); 26 | if (result === undefined) currentInstancesStatus[id] = initialize(id); 27 | result = action(...args); 28 | return result; 29 | } 30 | 31 | /** 32 | * 33 | * A handler function used to handle all the 34 | * calls made to native code. The purpose is 35 | * to make sure that the storage is initialized 36 | * before any read/write requests are sent to the 37 | * MMKV instance. 38 | * 39 | * 40 | * @param action The native function that will be called 41 | * @param args Arguments for the native function 42 | */ 43 | export async function handleActionAsync any | undefined | null>( 44 | action: T, 45 | ...args: any[] 46 | ): Promise | undefined | null> { 47 | let id = args[args.length - 1]; 48 | return new Promise(resolve => { 49 | if (!currentInstancesStatus[id]) { 50 | currentInstancesStatus[id] = initialize(id); 51 | } 52 | if (!action) return resolve(undefined); 53 | let result = action(...args); 54 | if (result === undefined) currentInstancesStatus[id] = initialize(id); 55 | result = action(...args); 56 | resolve(result); 57 | }); 58 | } 59 | 60 | export async function handlePromise any | undefined | null>( 61 | action: T, 62 | ...args: any[] 63 | ): Promise | undefined> { 64 | // The last argument is always the instance id. 65 | let id: string = args[args.length - 1]; 66 | if (!currentInstancesStatus[id]) { 67 | currentInstancesStatus[id] = initialize(id); 68 | } 69 | if (!action) return undefined; 70 | let result = await action(...args); 71 | if (result === undefined) currentInstancesStatus[id] = initialize(id); 72 | result = await action(...args); 73 | return result; 74 | } 75 | -------------------------------------------------------------------------------- /src/hooks/constants.ts: -------------------------------------------------------------------------------- 1 | export const types = ['string', 'number', 'boolean', 'object', 'array']; 2 | 3 | export const methods = { 4 | string: { 5 | indexer: 'strings', 6 | get: 'getString', 7 | set: 'setString', 8 | copy: (value: string) => { 9 | return value; 10 | } 11 | }, 12 | number: { 13 | indexer: 'numbers', 14 | get: 'getInt', 15 | set: 'setInt', 16 | copy: (value: number) => { 17 | return value; 18 | } 19 | }, 20 | boolean: { 21 | indexer: 'booleans', 22 | get: 'getBool', 23 | set: 'setBool', 24 | copy: (value: boolean) => { 25 | return value; 26 | } 27 | }, 28 | object: { 29 | indexer: 'maps', 30 | get: 'getMap', 31 | set: 'setMap', 32 | copy: (value: object) => { 33 | return { ...value }; 34 | } 35 | }, 36 | array: { 37 | indexer: 'arrays', 38 | get: 'getArray', 39 | set: 'setArray', 40 | copy: (value: any[]) => { 41 | return [...value]; 42 | } 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /src/hooks/functions.ts: -------------------------------------------------------------------------------- 1 | import MMKVInstance from '../mmkvinstance'; 2 | import { methods, types } from './constants'; 3 | export const getDataType = (value: any) => { 4 | if (value === null || value === undefined) return null; 5 | let type = Array.isArray(value) ? 'array' : typeof value; 6 | return type; 7 | }; 8 | 9 | export const getInitialValue = 10 | (key: string, storage: MMKVInstance, initialValueType: 'type' | 'value') => () => { 11 | if (!storage?.indexer) { 12 | return null; 13 | } 14 | let indexer = storage.indexer; 15 | if (indexer.hasKey(key)) { 16 | for (let i = 0; i < types.length; i++) { 17 | let type: string = types[i]; 18 | //@ts-ignore 19 | if (indexer[methods[type].indexer].hasKey(key)) { 20 | if (initialValueType === 'value') { 21 | //@ts-ignore 22 | return storage[methods[type]['get']](key); 23 | } 24 | if (initialValueType === 'type') { 25 | return type; 26 | } 27 | } 28 | } 29 | } 30 | return null; 31 | }; 32 | -------------------------------------------------------------------------------- /src/hooks/useIndex.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from 'react'; 2 | import MMKVInstance from '../mmkvinstance'; 3 | import { DataType } from '../types'; 4 | import { methods } from './constants'; 5 | 6 | type GenericValueType = [key: string, value: T | null | undefined]; 7 | 8 | /** 9 | * A hook that will take an array of keys and returns an array of values for those keys. 10 | * This is supposed to work in combination with `Transactions`s. When you have build your custom index, 11 | * you will need an easy and quick way to load values for your index. useIndex hook actively listens 12 | * to all read/write changes and updates the values accordingly. 13 | * 14 | * ```tsx 15 | * import MMKVStorage from "react-native-mmkv-storage" 16 | * 17 | * const storage = new MMKVStorage.Loader().initialize(); 18 | * 19 | * const App = () => { 20 | const postsIndex = useMMKVStorage("postsIndex",MMKV,[]); 21 | const [posts] = useIndex(postsIndex,"object" MMKV); 22 | 23 | return 24 | 28 | 29 | 30 | } 31 | * ``` 32 | * 33 | * Documentation: https://rnmmkv.vercel.app/#/useindex 34 | * 35 | * @param keys Array of keys against which the hook should load values 36 | * @param type Type of values 37 | * @param storage The storage instance 38 | * 39 | * @returns `[values, update, remove]` 40 | */ 41 | export const useIndex = ( 42 | keys: string[], 43 | type: DataType, 44 | storage: MMKVInstance 45 | ): [ 46 | values: (T | null | undefined)[], 47 | update: (key: string, value: T) => void, 48 | remove: (key: string) => void 49 | ] => { 50 | const [values, setValues] = useState[]>( 51 | storage.getMultipleItems(keys || [], type) 52 | ); 53 | 54 | const onChange = useCallback(({ key }) => { 55 | setValues(values => { 56 | let index = values.findIndex(v => v[0] === key); 57 | //@ts-ignore 58 | let value = storage[methods[type]['get']](key); 59 | if (value) { 60 | if (index !== -1) { 61 | values[index][1] = value; 62 | } else { 63 | storage.getMultipleItemsAsync(keys || [], type).then(data => { 64 | setValues(data); 65 | }); 66 | } 67 | } else { 68 | values.splice(index); 69 | } 70 | return [...values]; 71 | }); 72 | }, []); 73 | 74 | useEffect(() => { 75 | let names = keys.map(v => `${v}:onwrite`); 76 | storage.ev.subscribeMulti(names, onChange); 77 | 78 | return () => { 79 | names.forEach(name => { 80 | storage.ev.unsubscribe(name, onChange); 81 | }); 82 | }; 83 | }, [keys, type]); 84 | 85 | const update = useCallback((key, value) => { 86 | if (!value) return remove(key); 87 | //@ts-ignore 88 | storage[methods[type]['set']](key, value); 89 | }, []); 90 | 91 | const remove = useCallback(key => { 92 | storage.removeItem(key); 93 | }, []); 94 | 95 | return [values.map(v => v[1]).filter(v => v !== null), update, remove]; 96 | }; 97 | -------------------------------------------------------------------------------- /src/hooks/useMMKVRef.ts: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of the Notesnook project (https://notesnook.com/) 3 | 4 | Copyright (C) 2023 Streetwriters (Private) Limited 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | */ 19 | import { useEffect, useRef } from 'react'; 20 | import MMKVInstance from '../mmkvinstance'; 21 | 22 | export function useMMKVRef( 23 | key: string, 24 | storage: MMKVInstance, 25 | defaultValue: T 26 | ): { 27 | current: T; 28 | reset(): void; 29 | } { 30 | const refKey = `__mmkvref:${key}`; 31 | const value = useRef(storage.getMap<{ current: T }>(refKey)?.current || defaultValue); 32 | const frameRef = useRef(0); 33 | 34 | useEffect(() => { 35 | const onWrite = event => { 36 | value.current = event.value; 37 | }; 38 | if (storage !== null) { 39 | storage.ev.subscribe(`${key}:onwrite`, onWrite); 40 | } 41 | return () => { 42 | if (storage != null) { 43 | storage.ev.unsubscribe(`${key}:onwrite`, onWrite); 44 | } 45 | }; 46 | }, [key, storage]); 47 | 48 | return { 49 | get current() { 50 | return value.current; 51 | }, 52 | set current(next: T) { 53 | value.current = next; 54 | cancelAnimationFrame(frameRef.current); 55 | frameRef.current = requestAnimationFrame(() => { 56 | storage.setMap(refKey, { 57 | current: value.current 58 | }); 59 | }); 60 | }, 61 | reset() { 62 | storage.removeItem(refKey); 63 | } 64 | }; 65 | } 66 | 67 | export const createMMKVRefHookForStorage = 68 | (storage: MMKVInstance) => 69 | (key: string, defaultValue?: T) => { 70 | if (!key || typeof key !== 'string' || !storage) 71 | throw new Error('Key and Storage are required parameters.'); 72 | 73 | return useMMKVRef(key, storage, defaultValue as T); 74 | }; 75 | -------------------------------------------------------------------------------- /src/indexer/arrays.ts: -------------------------------------------------------------------------------- 1 | import { handleActionAsync, handleAction } from '../handlers'; 2 | import mmkvJsiModule from '../module'; 3 | import { GenericReturnType } from '../types'; 4 | const INDEX_TYPE = 'arrayIndex'; 5 | 6 | /** 7 | * Index of all array values stored in storage 8 | */ 9 | export default class arrayIndex { 10 | instanceID: string; 11 | constructor(id: string) { 12 | this.instanceID = id; 13 | } 14 | 15 | /** 16 | * 17 | * Get all keys 18 | */ 19 | async getKeys() { 20 | return await handleActionAsync(mmkvJsiModule.getIndexMMKV, INDEX_TYPE, this.instanceID); 21 | } 22 | /** 23 | * Check if a key exists. 24 | */ 25 | hasKey(key: string) { 26 | let keys = handleAction(mmkvJsiModule.getIndexMMKV, INDEX_TYPE, this.instanceID); 27 | return keys && keys.indexOf(key) > -1; 28 | } 29 | /** 30 | * Get all arrays from storage. 31 | */ 32 | async getAll() { 33 | return new Promise(resolve => { 34 | let keys = handleAction(mmkvJsiModule.getIndexMMKV, INDEX_TYPE, this.instanceID); 35 | if (!keys) keys = []; 36 | let items: GenericReturnType[] = []; 37 | for (let i = 0; i < keys.length; i++) { 38 | //@ts-ignore 39 | let item: GenericReturnType = []; 40 | item[0] = keys[i]; 41 | let array = mmkvJsiModule.getArrayMMKV(keys[i], this.instanceID); 42 | 43 | item[1] = array ? JSON.parse(array) : null; 44 | items.push(item); 45 | } 46 | resolve(items); 47 | }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/indexer/booleans.ts: -------------------------------------------------------------------------------- 1 | import { handleActionAsync, handleAction } from '../handlers'; 2 | import mmkvJsiModule from '../module'; 3 | const INDEX_TYPE = 'boolIndex'; 4 | 5 | /** 6 | * Index of all boolean values stored in storage. 7 | */ 8 | export default class boolIndex { 9 | instanceID: string; 10 | constructor(id: string) { 11 | this.instanceID = id; 12 | } 13 | /** 14 | * Get all keys 15 | */ 16 | async getKeys() { 17 | return await handleActionAsync(mmkvJsiModule.getIndexMMKV, INDEX_TYPE, this.instanceID); 18 | } 19 | 20 | /** 21 | * Check if a key exists. 22 | */ 23 | hasKey(key: string) { 24 | let keys = handleAction(mmkvJsiModule.getIndexMMKV, INDEX_TYPE, this.instanceID); 25 | return keys && keys.indexOf(key) > -1; 26 | } 27 | 28 | /** 29 | * Get all boolean values from storage 30 | */ 31 | async getAll() { 32 | return new Promise(resolve => { 33 | let keys = handleAction(mmkvJsiModule.getIndexMMKV, INDEX_TYPE, this.instanceID); 34 | if (!keys) keys = []; 35 | let items = []; 36 | for (let i = 0; i < keys.length; i++) { 37 | let item = []; 38 | item[0] = keys[i]; 39 | item[1] = mmkvJsiModule.getBoolMMKV(keys[i], this.instanceID); 40 | items.push(item); 41 | } 42 | resolve(items); 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/indexer/indexer.ts: -------------------------------------------------------------------------------- 1 | import stringsIndex from './strings'; 2 | import numbersIndex from './numbers'; 3 | import boolIndex from './booleans'; 4 | import mapsIndex from './maps'; 5 | import arrayIndex from './arrays'; 6 | import { handleAction, handleActionAsync } from '../handlers'; 7 | import mmkvJsiModule from '../module'; 8 | 9 | export default class indexer { 10 | instanceID: string; 11 | strings: stringsIndex; 12 | numbers: numbersIndex; 13 | booleans: boolIndex; 14 | maps: mapsIndex; 15 | arrays: arrayIndex; 16 | 17 | constructor(id: string) { 18 | this.instanceID = id; 19 | this.strings = new stringsIndex(id); 20 | this.numbers = new numbersIndex(id); 21 | this.booleans = new boolIndex(id); 22 | this.maps = new mapsIndex(id); 23 | this.arrays = new arrayIndex(id); 24 | } 25 | 26 | /** 27 | * Get all keys from storage. 28 | * 29 | */ 30 | async getKeys() { 31 | return await handleActionAsync(mmkvJsiModule.getAllKeysMMKV, this.instanceID); 32 | } 33 | 34 | /** 35 | * Check if a key exists in storage. 36 | */ 37 | hasKey(key: string) { 38 | return handleAction(mmkvJsiModule.containsKeyMMKV, key, this.instanceID); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/indexer/maps.ts: -------------------------------------------------------------------------------- 1 | import { handleActionAsync, handleAction } from '../handlers'; 2 | import mmkvJsiModule from '../module'; 3 | import { GenericReturnType } from '../types'; 4 | const INDEX_TYPE = 'mapIndex'; 5 | 6 | /** 7 | * Index of all objects stored in storage. 8 | */ 9 | export default class mapsIndex { 10 | instanceID: string; 11 | constructor(id: string) { 12 | this.instanceID = id; 13 | } 14 | 15 | /** 16 | * 17 | * Get all keys 18 | */ 19 | async getKeys() { 20 | return await handleActionAsync(mmkvJsiModule.getIndexMMKV, INDEX_TYPE, this.instanceID); 21 | } 22 | 23 | /** 24 | * Check if a key exists. 25 | */ 26 | hasKey(key: string) { 27 | let keys = handleAction(mmkvJsiModule.getIndexMMKV, INDEX_TYPE, this.instanceID); 28 | return keys && keys.indexOf(key) > -1; 29 | } 30 | 31 | /** 32 | * Get all objects stored in storage. 33 | */ 34 | async getAll() { 35 | return new Promise(resolve => { 36 | let keys = handleAction(mmkvJsiModule.getIndexMMKV, INDEX_TYPE, this.instanceID); 37 | if (!keys) keys = []; 38 | let items: GenericReturnType[] = []; 39 | for (let i = 0; i < keys.length; i++) { 40 | //@ts-ignore 41 | let item: GenericReturnType = []; 42 | item[0] = keys[i]; 43 | let map = mmkvJsiModule.getMapMMKV(keys[i], this.instanceID); 44 | item[1] = map ? JSON.parse(map) : null; 45 | items.push(item); 46 | } 47 | resolve(items); 48 | }); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/indexer/numbers.ts: -------------------------------------------------------------------------------- 1 | import { handleActionAsync, handleAction } from '../handlers'; 2 | import mmkvJsiModule from '../module'; 3 | 4 | const INDEX_TYPE = 'numberIndex'; 5 | /** 6 | * Index of all numbers stored in storage. 7 | */ 8 | export default class numbersIndex { 9 | instanceID: string; 10 | constructor(id: string) { 11 | this.instanceID = id; 12 | } 13 | 14 | /** 15 | * 16 | * Get all keys 17 | */ 18 | async getKeys() { 19 | return await handleActionAsync(mmkvJsiModule.getIndexMMKV, INDEX_TYPE, this.instanceID); 20 | } 21 | 22 | /** 23 | * Check if a key exists 24 | */ 25 | hasKey(key: string) { 26 | let keys = handleAction(mmkvJsiModule.getIndexMMKV, INDEX_TYPE, this.instanceID); 27 | return keys && keys.indexOf(key) > -1; 28 | } 29 | 30 | /** 31 | * Get all numbers from storage 32 | */ 33 | async getAll() { 34 | return new Promise(resolve => { 35 | let keys = handleAction(mmkvJsiModule.getIndexMMKV, INDEX_TYPE, this.instanceID); 36 | if (!keys) keys = []; 37 | let items = []; 38 | for (let i = 0; i < keys.length; i++) { 39 | let item = []; 40 | item[0] = keys[i]; 41 | item[1] = mmkvJsiModule.getNumberMMKV(keys[i], this.instanceID); 42 | items.push(item); 43 | } 44 | resolve(items); 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/indexer/strings.ts: -------------------------------------------------------------------------------- 1 | import { handleAction, handleActionAsync } from '../handlers'; 2 | import mmkvJsiModule from '../module'; 3 | const INDEX_TYPE = 'stringIndex'; 4 | /** 5 | * Index of all string values in storage 6 | */ 7 | export default class stringsIndex { 8 | instanceID: string; 9 | constructor(id: string) { 10 | this.instanceID = id; 11 | } 12 | 13 | /** 14 | * Get all keys 15 | */ 16 | async getKeys() { 17 | return await handleActionAsync(mmkvJsiModule.getIndexMMKV, INDEX_TYPE, this.instanceID); 18 | } 19 | 20 | /** 21 | * Checck if a key exists 22 | */ 23 | hasKey(key: string) { 24 | let keys = handleAction(mmkvJsiModule.getIndexMMKV, INDEX_TYPE, this.instanceID); 25 | return keys && keys.indexOf(key) > -1; 26 | } 27 | 28 | /** 29 | * Get all string values from storage 30 | */ 31 | async getAll() { 32 | return new Promise(resolve => { 33 | let keys = handleAction(mmkvJsiModule.getIndexMMKV, INDEX_TYPE, this.instanceID); 34 | if (!keys) keys = []; 35 | let items = []; 36 | for (let i = 0; i < keys.length; i++) { 37 | let item = []; 38 | item[0] = keys[i]; 39 | item[1] = mmkvJsiModule.getStringMMKV(keys[i], this.instanceID); 40 | items.push(item); 41 | } 42 | resolve(items); 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/keygen.ts: -------------------------------------------------------------------------------- 1 | const lowercase = 'abcdefghijklmnopqrstuvwxyz'; 2 | const uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; 3 | const numbers = '0123456789'; 4 | 5 | const all = lowercase + uppercase + numbers; 6 | 7 | /** 8 | * A utility function that generates a strong password 9 | * @returns 10 | */ 11 | export default function generatePassword(): string { 12 | let password = ''; 13 | 14 | password += pick(password, lowercase, 1, 5); 15 | password += pick(password, uppercase, 1, 5); 16 | password += pick(password, all, 10); 17 | 18 | return shuffle(password); 19 | } 20 | 21 | function pick(exclusions: string, string: string, min: number, max?: number): string { 22 | var n: number, 23 | chars = ''; 24 | 25 | if (max === undefined) { 26 | n = min; 27 | } else { 28 | n = min + Math.floor(Math.random() * (max - min + 1)); 29 | } 30 | 31 | var i = 0; 32 | while (i < n) { 33 | const character = string.charAt(Math.floor(Math.random() * string.length)); 34 | if (exclusions.indexOf(character) < 0 && chars.indexOf(character) < 0) { 35 | chars += character; 36 | i++; 37 | } 38 | } 39 | 40 | return chars; 41 | } 42 | 43 | function shuffle(string: string): string { 44 | var array = string.split(''); 45 | var tmp: string, 46 | current: number, 47 | top = array.length; 48 | 49 | if (top) 50 | while (--top) { 51 | current = Math.floor(Math.random() * (top + 1)); 52 | tmp = array[current]; 53 | array[current] = array[top]; 54 | array[top] = tmp; 55 | } 56 | 57 | return array.join(''); 58 | } 59 | -------------------------------------------------------------------------------- /src/mmkv/IDStore.ts: -------------------------------------------------------------------------------- 1 | import { Platform } from 'react-native'; 2 | import mmkvJsiModule from '../module'; 3 | const STORE_ID = Platform.OS === 'ios' ? 'mmkvIdStore' : 'mmkvIDStore'; 4 | 5 | export type StorageInstanceInfo = { 6 | encrypted: boolean; 7 | id: string; 8 | alias: string; 9 | }; 10 | /** 11 | * Store instance properties that we will use later to 12 | * load the storage again. 13 | */ 14 | function add(id: string, encrypted?: boolean, alias?: string | null) { 15 | let storeUnit = { 16 | id, 17 | encrypted, 18 | alias 19 | }; 20 | mmkvJsiModule.setStringMMKV(id, JSON.stringify(storeUnit), STORE_ID); 21 | } 22 | 23 | /** 24 | * Check if the storage instance with the given ID is encrypted or not. 25 | */ 26 | function encrypted(id: string) { 27 | let json = mmkvJsiModule.getStringMMKV(id, STORE_ID); 28 | if (!json) { 29 | return false; 30 | } 31 | let storeUnit: StorageInstanceInfo = JSON.parse(json); 32 | return storeUnit.encrypted; 33 | } 34 | 35 | /** 36 | * Get the alias for the storage which we used 37 | * to store the crypt key in secure storage. 38 | * @param {string} id instance id 39 | */ 40 | function getAlias(id: string) { 41 | let json = mmkvJsiModule.getStringMMKV(id, STORE_ID); 42 | if (!json) { 43 | return null; 44 | } 45 | let storeUnit: StorageInstanceInfo = JSON.parse(json); 46 | return storeUnit.alias; 47 | } 48 | 49 | /** 50 | * Check if an instance is already present in the store. 51 | * @param {string} id instance id 52 | */ 53 | function exists(id: string) { 54 | let json = mmkvJsiModule.getStringMMKV(id, STORE_ID); 55 | if (!json) { 56 | return false; 57 | } 58 | return true; 59 | } 60 | 61 | let blacklist = ['stringIndex']; 62 | /** 63 | * Get all the available instances that 64 | * were loaded since the app was installed. 65 | */ 66 | function getAll() { 67 | let keys = mmkvJsiModule.getAllKeysMMKV(STORE_ID); 68 | if (!keys) return []; 69 | let storeUnits: { [name: string]: StorageInstanceInfo } = {}; 70 | keys.forEach(key => { 71 | if (!blacklist.includes(key)) { 72 | let json = mmkvJsiModule.getStringMMKV(key, STORE_ID); 73 | if (json) { 74 | let storeUnit: StorageInstanceInfo = JSON.parse(json); 75 | storeUnits[key] = storeUnit; 76 | } 77 | } 78 | }); 79 | return storeUnits; 80 | } 81 | 82 | /** 83 | * Get all the instance ids for instances 84 | * that were loaded since the app was installed 85 | */ 86 | function getAllMMKVInstanceIDs() { 87 | return Object.keys(getAll()); 88 | } 89 | 90 | export default { 91 | getAll, 92 | getAlias, 93 | getAllMMKVInstanceIDs, 94 | add, 95 | exists, 96 | encrypted, 97 | STORE_ID 98 | }; 99 | -------------------------------------------------------------------------------- /src/mmkv/init.ts: -------------------------------------------------------------------------------- 1 | import mmkvJsiModule, { mmkvBridgeModule } from '../module/index'; 2 | 3 | // Installing JSI Bindings as done by 4 | // https://github.com/mrousavy/react-native-mmkv 5 | 6 | /** 7 | * Check if functions installed from JSI are present in global object. 8 | */ 9 | export function isLoaded() { 10 | return typeof mmkvJsiModule?.getStringMMKV === 'function'; 11 | } 12 | 13 | /** 14 | * Install bindings lazily. 15 | * 16 | * Note: You don't need to call this normally. 17 | */ 18 | export function init() { 19 | try { 20 | if (!isLoaded()) { 21 | const installed = mmkvBridgeModule.install(); 22 | if (!installed) throw new Error('JSI bindings were not installed for: MMKVStorage'); 23 | 24 | if (!isLoaded()) { 25 | throw new Error('JSI bindings installation failed for: MMKVStorage'); 26 | } 27 | return installed; 28 | } 29 | return true; 30 | } catch (e) { 31 | console.error('JSI bindings were not installed for: MMKVStorage', e); 32 | return false; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/module/NativeMMKVStorage.ts: -------------------------------------------------------------------------------- 1 | import type { TurboModule } from 'react-native'; 2 | import { TurboModuleRegistry } from 'react-native'; 3 | 4 | export interface Spec extends TurboModule { 5 | install(): boolean; 6 | } 7 | 8 | export default TurboModuleRegistry.getEnforcing('MMKVStorage'); 9 | -------------------------------------------------------------------------------- /src/module/index.ts: -------------------------------------------------------------------------------- 1 | import { MMKVJsiModule } from '../types'; 2 | 3 | //@ts-ignore 4 | const isDebugMode = 5 | global.location && global.location.pathname && global.location.pathname.includes('/debugger-ui'); 6 | 7 | const isTurboModuleEnabled = global.__turboModuleProxy != null; 8 | 9 | export const mmkvBridgeModule: { 10 | /* 11 | * Install JSI bindings 12 | */ 13 | install: () => boolean; 14 | } = !isDebugMode 15 | ? isTurboModuleEnabled 16 | ? require('./NativeMMKVStorage').default 17 | : require('react-native').NativeModules.MMKVStorage 18 | : { 19 | install: () => { 20 | console.warn( 21 | `Remote debugging is not supported by JSI modules. MMKV is running with a memory adapter currently and is fully functional for testing only. Hence any values will not persist on App refresh/reload. ` 22 | ); 23 | require('../../../jest/dist/jest/memoryStore.js').mock(); 24 | return true; 25 | } 26 | }; 27 | 28 | /** 29 | * All jsi functions bound to global object. 30 | * 31 | * The last param `id` is the instance id of the storage instance we want to get/set the value. 32 | * 33 | * `undefined`: It means that instance is not loaded 34 | * 35 | * `null`: Value does not exist or some error occured while getting the value 36 | * 37 | */ 38 | 39 | //@ts-ignore 40 | const mmkvJsiModule: MMKVJsiModule = global; 41 | 42 | export default mmkvJsiModule; 43 | -------------------------------------------------------------------------------- /src/transactions.ts: -------------------------------------------------------------------------------- 1 | import { DataType } from './types'; 2 | 3 | /** 4 | * A mutator function can return a value where needed. For example, 5 | * you can modify the value in `beforewrite` or `onread` transactions. 6 | */ 7 | export type MutatorFunction = (key: string, value?: unknown) => any; 8 | 9 | export type Transaction = { [name: string]: MutatorFunction | null }; 10 | 11 | export type TransactionType = 'beforewrite' | 'onwrite' | 'onread' | 'ondelete'; 12 | 13 | /** 14 | * Listen to a value’s lifecycle and mutate it on the go. 15 | * Transactions lets you register lifecycle functions 16 | * with your storage instance such as `onwrite`, 17 | * `beforewrite`, `onread`, `ondelete`. This allows for a 18 | * better and more managed control over the storage and 19 | * also let’s you build custom indexes with a few lines of code. 20 | * 21 | * Example: 22 | * ```tsx 23 | * import MMKVStorage from "react-native-mmkv-storage"; 24 | * 25 | * const MMKV = new MMKVStorage.Loader().initialize(); 26 | * 27 | * MMKV.transactions.register("object", "onwrite", ({ key, value }) => { 28 | * console.log(MMKV.instanceID, "object:onwrite: ", key, value) 29 | * }); 30 | * ``` 31 | * 32 | * Documentation: https://rnmmkv.vercel.app/#/transactionmanager 33 | */ 34 | export default class transactions { 35 | beforewrite: Transaction; 36 | onwrite: Transaction; 37 | onread: Transaction; 38 | ondelete: MutatorFunction | null; 39 | constructor() { 40 | this.beforewrite = {}; 41 | this.onwrite = {}; 42 | this.onread = {}; 43 | this.ondelete = null; 44 | } 45 | 46 | /** 47 | * Register a lifecycle function for a given data type. 48 | * 49 | * @param type Type of data to register a mutator function for 50 | * @param transaction Type of transaction to listen to 51 | * @param mutator The mutator function 52 | */ 53 | register(type: DataType, transaction: TransactionType, mutator: MutatorFunction) { 54 | if (!transaction || !type || !mutator) throw new Error('All parameters are required'); 55 | 56 | if (transaction === 'ondelete') { 57 | this.ondelete = mutator; 58 | } else { 59 | this[transaction][type] = mutator; 60 | } 61 | 62 | return () => this.unregister(type, transaction); 63 | } 64 | 65 | /** 66 | * Register a lifecycle function for a given data type. 67 | * @param type Type of data to register a mutator function for 68 | * @param transaction Type of transaction to listen to 69 | */ 70 | unregister(type: DataType, transaction: TransactionType) { 71 | if (!type || !transaction) throw new Error('All parameters are required'); 72 | if (transaction === 'ondelete') { 73 | this.ondelete = null; 74 | return; 75 | } 76 | this[transaction][type] = null; 77 | } 78 | /** 79 | * Clear all registered functions. 80 | */ 81 | clear() { 82 | this.beforewrite = {}; 83 | this.onread = {}; 84 | this.onwrite = {}; 85 | this.ondelete = null; 86 | } 87 | 88 | transact(type: DataType, transaction: TransactionType, key: string, value?: T): T | undefined { 89 | const mutator = transaction === 'ondelete' ? this.ondelete : this[transaction][type]; 90 | if (!mutator) return value; 91 | let _value = mutator(key, value); 92 | // In case a mutator function does not return a value or returns undefined, we will return the original value. 93 | return _value === undefined || _value === null ? value : _value; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import { StorageOptions } from './types'; 2 | 3 | export function promisify(fn: Function) { 4 | return function (...args: any) { 5 | return new Promise(resolve => { 6 | resolve(fn(...args)); 7 | }); 8 | }; 9 | } 10 | /** 11 | * Accessible modes for iOS Keychain 12 | */ 13 | export const IOSAccessibleStates = { 14 | WHEN_UNLOCKED: 'AccessibleWhenUnlocked', 15 | AFTER_FIRST_UNLOCK: 'AccessibleAfterFirstUnlock', 16 | /** @deprected in iOS 16+ */ 17 | ALWAYS: 'AccessibleAlways', 18 | WHEN_PASSCODE_SET_THIS_DEVICE_ONLY: 'AccessibleWhenPasscodeSetThisDeviceOnly', 19 | WHEN_UNLOCKED_THIS_DEVICE_ONLY: 'AccessibleWhenUnlockedThisDeviceOnly', 20 | AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY: 'AccessibleAfterFirstUnlockThisDeviceOnly', 21 | /** @deprected in iOS 16+ */ 22 | ALWAYS_THIS_DEVICE_ONLY: 'AccessibleAlwaysThisDeviceOnly' 23 | }; 24 | 25 | /** 26 | * Processing modes for storage. 27 | */ 28 | export const ProcessingModes = { 29 | SINGLE_PROCESS: 1, 30 | MULTI_PROCESS: 2 31 | }; 32 | 33 | export const DATA_TYPES = Object.freeze({ 34 | STRING: 1, 35 | NUMBER: 2, 36 | BOOL: 3, 37 | MAP: 4, 38 | ARRAY: 5 39 | }); 40 | 41 | /** 42 | * Information about all storage instances 43 | */ 44 | export const options: { [name: string]: StorageOptions } = {}; 45 | 46 | export const stringToHex = (input: string) => { 47 | let str = ''; 48 | //@ts-ignore 49 | for (const char of input) { 50 | str += char.charCodeAt(0).toString(16); 51 | } 52 | return str; 53 | }; 54 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["index.ts","./src/module/NativeMMKVStorage.ts"], 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "jsx": "react-native", 6 | "module": "CommonJS", 7 | "moduleResolution": "node", 8 | "lib": ["es2017", "DOM"], 9 | "target": "ES2015", 10 | "declaration": true, 11 | "declarationMap": true, 12 | "outDir": "dist", 13 | "allowSyntheticDefaultImports": true, 14 | "esModuleInterop": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "noFallthroughCasesInSwitch": true, 17 | "noImplicitReturns": false, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "pretty": true, 21 | "resolveJsonModule": true, 22 | "skipLibCheck": true, 23 | "strict": false, 24 | "allowJs": true 25 | }, 26 | } 27 | --------------------------------------------------------------------------------