react-native-mmkv 4.0.0-beta.5 → 4.0.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/margelo/nitro/mmkv/HybridMMKVPlatformContext.kt +1 -1
- package/app.plugin.js +1 -0
- package/cpp/HybridMMKV.hpp +47 -0
- package/cpp/HybridMMKVFactory.hpp +24 -0
- package/cpp/MMKVTypes.hpp +50 -0
- package/cpp/MMKVValueChangedListenerRegistry.hpp +43 -0
- package/cpp/ManagedMMBuffer.hpp +40 -0
- package/ios/HybridMMKVPlatformContext.swift +18 -7
- package/lib/__tests__/hooks.test.js +4 -1
- package/lib/createMMKV/createMMKV.mock.js +23 -3
- package/lib/expo-plugin/withMMKV.d.ts +3 -0
- package/lib/expo-plugin/withMMKV.js +17 -0
- package/lib/specs/MMKVFactory.nitro.d.ts +4 -4
- package/lib/specs/MMKVPlatformContext.nitro.d.ts +4 -1
- package/nitrogen/generated/android/c++/JHybridMMKVPlatformContextSpec.cpp +3 -2
- package/nitrogen/generated/android/c++/JHybridMMKVPlatformContextSpec.hpp +1 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/mmkv/HybridMMKVPlatformContextSpec.kt +1 -1
- package/nitrogen/generated/ios/NitroMmkv-Swift-Cxx-Bridge.hpp +25 -0
- package/nitrogen/generated/ios/NitroMmkv-Swift-Cxx-Umbrella.hpp +1 -0
- package/nitrogen/generated/ios/c++/HybridMMKVPlatformContextSpecSwift.hpp +2 -1
- package/nitrogen/generated/ios/swift/HybridMMKVPlatformContextSpec.swift +1 -1
- package/nitrogen/generated/ios/swift/HybridMMKVPlatformContextSpec_cxx.swift +10 -4
- package/nitrogen/generated/shared/c++/HybridMMKVPlatformContextSpec.hpp +2 -1
- package/package.json +3 -3
- package/src/__tests__/hooks.test.tsx +5 -0
- package/src/createMMKV/createMMKV.mock.ts +24 -3
- package/src/expo-plugin/withMMKV.ts +23 -0
- package/src/specs/MMKVFactory.nitro.ts +4 -4
- package/src/specs/MMKVPlatformContext.nitro.ts +4 -1
|
@@ -12,7 +12,7 @@ class HybridMMKVPlatformContext: HybridMMKVPlatformContextSpec() {
|
|
|
12
12
|
return context.filesDir.absolutePath + "/mmkv";
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
override fun getAppGroupDirectory(): String {
|
|
15
|
+
override fun getAppGroupDirectory(): String? {
|
|
16
16
|
// AppGroups do not exist on Android. It's iOS only.
|
|
17
17
|
throw Error("getAppGroupDirectory() is not supported on Android! It's iOS only.")
|
|
18
18
|
}
|
package/app.plugin.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./lib/commonjs/expo-plugin/withMMKV')
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
//
|
|
2
|
+
// HybridMMKV.hpp
|
|
3
|
+
// react-native-mmkv
|
|
4
|
+
//
|
|
5
|
+
// Created by Marc Rousavy on 21.08.2025.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include "Configuration.hpp"
|
|
11
|
+
#include "HybridMMKVSpec.hpp"
|
|
12
|
+
#include "MMKVTypes.hpp"
|
|
13
|
+
|
|
14
|
+
namespace margelo::nitro::mmkv {
|
|
15
|
+
|
|
16
|
+
class HybridMMKV final : public HybridMMKVSpec {
|
|
17
|
+
public:
|
|
18
|
+
explicit HybridMMKV(const Configuration& configuration);
|
|
19
|
+
|
|
20
|
+
public:
|
|
21
|
+
// Properties
|
|
22
|
+
double getSize() override;
|
|
23
|
+
bool getIsReadOnly() override;
|
|
24
|
+
|
|
25
|
+
public:
|
|
26
|
+
// Methods
|
|
27
|
+
void set(const std::string& key, const std::variant<std::string, double, bool, std::shared_ptr<ArrayBuffer>>& value) override;
|
|
28
|
+
std::optional<bool> getBoolean(const std::string& key) override;
|
|
29
|
+
std::optional<std::string> getString(const std::string& key) override;
|
|
30
|
+
std::optional<double> getNumber(const std::string& key) override;
|
|
31
|
+
std::optional<std::shared_ptr<ArrayBuffer>> getBuffer(const std::string& key) override;
|
|
32
|
+
bool contains(const std::string& key) override;
|
|
33
|
+
void remove(const std::string& key) override;
|
|
34
|
+
std::vector<std::string> getAllKeys() override;
|
|
35
|
+
void clearAll() override;
|
|
36
|
+
void recrypt(const std::optional<std::string>& key) override;
|
|
37
|
+
void trim() override;
|
|
38
|
+
Listener addOnValueChangedListener(const std::function<void(const std::string& /* key */)>& onValueChanged) override;
|
|
39
|
+
|
|
40
|
+
private:
|
|
41
|
+
static MMKVMode getMMKVMode(const Configuration& config);
|
|
42
|
+
|
|
43
|
+
private:
|
|
44
|
+
MMKV* instance;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
} // namespace margelo::nitro::mmkv
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//
|
|
2
|
+
// HybridMMKVFactory.hpp
|
|
3
|
+
// react-native-mmkv
|
|
4
|
+
//
|
|
5
|
+
// Created by Marc Rousavy on 21.08.2025.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include "HybridMMKVFactorySpec.hpp"
|
|
11
|
+
|
|
12
|
+
namespace margelo::nitro::mmkv {
|
|
13
|
+
|
|
14
|
+
class HybridMMKVFactory final : public HybridMMKVFactorySpec {
|
|
15
|
+
public:
|
|
16
|
+
HybridMMKVFactory() : HybridObject(TAG) {}
|
|
17
|
+
|
|
18
|
+
public:
|
|
19
|
+
std::string getDefaultMMKVInstanceId() override;
|
|
20
|
+
std::shared_ptr<HybridMMKVSpec> createMMKV(const Configuration& configuration) override;
|
|
21
|
+
void initializeMMKV(const std::string& rootPath) override;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
} // namespace margelo::nitro::mmkv
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//
|
|
2
|
+
// MMKVTypes.h
|
|
3
|
+
// react-native-mmkv
|
|
4
|
+
//
|
|
5
|
+
// Created by Brad Anderson on 10.08.2025.
|
|
6
|
+
// Platform-specific MMKV type unification header
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
#pragma once
|
|
10
|
+
|
|
11
|
+
// Platform-specific MMKV includes
|
|
12
|
+
#ifdef __ANDROID__
|
|
13
|
+
#include <MMKV/MMKV.h>
|
|
14
|
+
|
|
15
|
+
// On Android, bring global namespace types into mmkv namespace for consistency
|
|
16
|
+
namespace mmkv {
|
|
17
|
+
using MMKV = ::MMKV;
|
|
18
|
+
using MMKVMode = ::MMKVMode;
|
|
19
|
+
using MMKVLogLevel = ::MMKVLogLevel;
|
|
20
|
+
|
|
21
|
+
// Constants - bring into mmkv namespace
|
|
22
|
+
constexpr auto MMKVLogDebug = ::MMKVLogDebug;
|
|
23
|
+
constexpr auto MMKVLogInfo = ::MMKVLogInfo;
|
|
24
|
+
constexpr auto MMKVLogWarning = ::MMKVLogWarning;
|
|
25
|
+
constexpr auto MMKVLogError = ::MMKVLogError;
|
|
26
|
+
constexpr auto MMKVLogNone = ::MMKVLogNone;
|
|
27
|
+
|
|
28
|
+
constexpr auto MMKV_SINGLE_PROCESS = ::MMKV_SINGLE_PROCESS;
|
|
29
|
+
constexpr auto MMKV_MULTI_PROCESS = ::MMKV_MULTI_PROCESS;
|
|
30
|
+
constexpr auto MMKV_READ_ONLY = ::MMKVMode::MMKV_READ_ONLY;
|
|
31
|
+
} // namespace mmkv
|
|
32
|
+
|
|
33
|
+
#else
|
|
34
|
+
#include <MMKVCore/MMKV.h>
|
|
35
|
+
// iOS already has everything in mmkv:: namespace
|
|
36
|
+
#endif
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Unified MMKV namespace usage for cross-platform compatibility.
|
|
40
|
+
*
|
|
41
|
+
* After including this header, use:
|
|
42
|
+
* - mmkv::MMKV for the main class
|
|
43
|
+
* - mmkv::MMKVMode for mode enum
|
|
44
|
+
* - mmkv::MMKVLogLevel for log level enum
|
|
45
|
+
* - mmkv::MMBuffer for buffer type
|
|
46
|
+
* - mmkv::MMKV_SINGLE_PROCESS / mmkv::MMKV_MULTI_PROCESS for modes
|
|
47
|
+
* - mmkv::MMKVLogDebug, etc. for log levels
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
using namespace mmkv;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//
|
|
2
|
+
// MMKVValueChangedListenerRegistry.hpp
|
|
3
|
+
// react-native-mmkv
|
|
4
|
+
//
|
|
5
|
+
// Created by Marc Rousavy on 21.08.2025.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#include "MMKVTypes.hpp"
|
|
9
|
+
#include <atomic>
|
|
10
|
+
#include <unordered_map>
|
|
11
|
+
|
|
12
|
+
namespace margelo::nitro::mmkv {
|
|
13
|
+
|
|
14
|
+
using ListenerID = size_t;
|
|
15
|
+
using MMKVID = std::string;
|
|
16
|
+
|
|
17
|
+
struct ListenerSubscription {
|
|
18
|
+
ListenerID id;
|
|
19
|
+
std::function<void(const std::string& /* key */)> callback;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Listeners are tracked across instances - so we need an extra static class for
|
|
24
|
+
* the registry.
|
|
25
|
+
*/
|
|
26
|
+
class MMKVValueChangedListenerRegistry final {
|
|
27
|
+
public:
|
|
28
|
+
MMKVValueChangedListenerRegistry() = delete;
|
|
29
|
+
~MMKVValueChangedListenerRegistry() = delete;
|
|
30
|
+
|
|
31
|
+
public:
|
|
32
|
+
static ListenerID addListener(const std::string& mmkvID, const std::function<void(const std::string& /* key */)>& callback);
|
|
33
|
+
static void removeListener(const std::string& mmkvID, ListenerID id);
|
|
34
|
+
|
|
35
|
+
public:
|
|
36
|
+
static void notifyOnValueChanged(const std::string& mmkvID, const std::string& key);
|
|
37
|
+
|
|
38
|
+
private:
|
|
39
|
+
static std::atomic<ListenerID> _listenersCounter;
|
|
40
|
+
static std::unordered_map<MMKVID, std::vector<ListenerSubscription>> _listeners;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
} // namespace margelo::nitro::mmkv
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//
|
|
2
|
+
// ManagedMMBuffer.h
|
|
3
|
+
// react-native-mmkv
|
|
4
|
+
//
|
|
5
|
+
// Created by Marc Rousavy on 25.03.24.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include "MMKVTypes.hpp"
|
|
11
|
+
#include <NitroModules/ArrayBuffer.hpp>
|
|
12
|
+
|
|
13
|
+
namespace margelo::nitro::mmkv {
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
An ArrayBuffer subclass that manages MMBuffer memory (by ownership).
|
|
17
|
+
*/
|
|
18
|
+
class ManagedMMBuffer : public ArrayBuffer {
|
|
19
|
+
public:
|
|
20
|
+
explicit ManagedMMBuffer(MMBuffer&& buffer) : _buffer(std::move(buffer)) {}
|
|
21
|
+
|
|
22
|
+
public:
|
|
23
|
+
bool isOwner() const noexcept override {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public:
|
|
28
|
+
uint8_t* data() override {
|
|
29
|
+
return static_cast<uint8_t*>(_buffer.getPtr());
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
size_t size() const override {
|
|
33
|
+
return _buffer.length();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private:
|
|
37
|
+
MMBuffer _buffer;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
} // namespace margelo::nitro::mmkv
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// Created by Marc Rousavy on 25.03.24.
|
|
6
6
|
//
|
|
7
7
|
|
|
8
|
+
import Foundation
|
|
8
9
|
import NitroModules
|
|
9
10
|
|
|
10
11
|
class HybridMMKVPlatformContext: HybridMMKVPlatformContextSpec {
|
|
@@ -17,16 +18,26 @@ class HybridMMKVPlatformContext: HybridMMKVPlatformContextSpec {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
func getBaseDirectory() throws -> String {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return basePath
|
|
24
|
-
} else {
|
|
21
|
+
// Get user documents directory
|
|
22
|
+
let paths = FileManager.default.urls(for: Self.directory, in: .userDomainMask)
|
|
23
|
+
guard let documentsPath = paths.first else {
|
|
25
24
|
throw RuntimeError.error(withMessage: "Cannot find base-path to store MMKV files!")
|
|
26
25
|
}
|
|
26
|
+
|
|
27
|
+
// append /mmkv to it
|
|
28
|
+
let basePath = documentsPath.appendingPathComponent("mmkv", conformingTo: .directory)
|
|
29
|
+
return basePath.path
|
|
27
30
|
}
|
|
28
31
|
|
|
29
|
-
func getAppGroupDirectory() -> String {
|
|
30
|
-
|
|
32
|
+
func getAppGroupDirectory() throws -> String? {
|
|
33
|
+
// Read `AppGroupIdentifier` from `Info.plist`
|
|
34
|
+
guard let appGroupID = Bundle.main.object(forInfoDictionaryKey: "AppGroupIdentifier") as? String else {
|
|
35
|
+
return nil
|
|
36
|
+
}
|
|
37
|
+
// Get the URL for the AppGroup
|
|
38
|
+
guard let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupID) else {
|
|
39
|
+
throw RuntimeError.error(withMessage: "Container for AppGroup \"\(appGroupID)\" not accessible")
|
|
40
|
+
}
|
|
41
|
+
return url.path
|
|
31
42
|
}
|
|
32
43
|
}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Button, Text } from 'react-native';
|
|
3
|
-
import { act, fireEvent, render, renderHook, screen, } from '@testing-library/react-native';
|
|
3
|
+
import { act, fireEvent, render, renderHook, screen, cleanup, } from '@testing-library/react-native';
|
|
4
4
|
import { createMMKV, useMMKVNumber, useMMKVString } from '..';
|
|
5
5
|
const mmkv = createMMKV();
|
|
6
6
|
beforeEach(() => {
|
|
7
7
|
mmkv.clearAll();
|
|
8
8
|
mmkv.trim();
|
|
9
9
|
});
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
cleanup();
|
|
12
|
+
});
|
|
10
13
|
test('hooks update when the value is changed directly through the instance', () => {
|
|
11
14
|
const { result } = renderHook(() => useMMKVString('string-key', mmkv));
|
|
12
15
|
expect(result.current[0]).toBeUndefined();
|
|
@@ -4,10 +4,30 @@
|
|
|
4
4
|
export function createMockMMKV() {
|
|
5
5
|
const storage = new Map();
|
|
6
6
|
const listeners = new Set();
|
|
7
|
+
const notifyListeners = (key) => {
|
|
8
|
+
listeners.forEach((listener) => {
|
|
9
|
+
listener(key);
|
|
10
|
+
});
|
|
11
|
+
};
|
|
7
12
|
return {
|
|
8
|
-
clearAll: () =>
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
clearAll: () => {
|
|
14
|
+
const keysBefore = storage.keys();
|
|
15
|
+
storage.clear();
|
|
16
|
+
// Notify all listeners for all keys that were cleared
|
|
17
|
+
for (const key of keysBefore) {
|
|
18
|
+
notifyListeners(key);
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
remove: (key) => {
|
|
22
|
+
const deleted = storage.delete(key);
|
|
23
|
+
if (deleted) {
|
|
24
|
+
notifyListeners(key);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
set: (key, value) => {
|
|
28
|
+
storage.set(key, value);
|
|
29
|
+
notifyListeners(key);
|
|
30
|
+
},
|
|
11
31
|
getString: (key) => {
|
|
12
32
|
const result = storage.get(key);
|
|
13
33
|
return typeof result === 'string' ? result : undefined;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createRunOncePlugin, withGradleProperties } from '@expo/config-plugins';
|
|
2
|
+
const pkg = require('../../package.json');
|
|
3
|
+
const withMMKV = (config) => {
|
|
4
|
+
// remove 32-bit architectures from gradle.properties
|
|
5
|
+
return withGradleProperties(config, (cfg) => {
|
|
6
|
+
// Drop any existing entry…
|
|
7
|
+
cfg.modResults = cfg.modResults.filter((p) => !(p.type === 'property' && p.key === 'reactNativeArchitectures'));
|
|
8
|
+
// …and force 64-bit only.
|
|
9
|
+
cfg.modResults.push({
|
|
10
|
+
type: 'property',
|
|
11
|
+
key: 'reactNativeArchitectures',
|
|
12
|
+
value: 'arm64-v8a,x86_64',
|
|
13
|
+
});
|
|
14
|
+
return cfg;
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
export default createRunOncePlugin(withMMKV, pkg.name, pkg.version);
|
|
@@ -15,8 +15,8 @@ export interface Configuration {
|
|
|
15
15
|
*
|
|
16
16
|
* @example
|
|
17
17
|
* ```ts
|
|
18
|
-
* const userStorage =
|
|
19
|
-
* const globalStorage =
|
|
18
|
+
* const userStorage = createMMKV({ id: `user-${userId}-storage` })
|
|
19
|
+
* const globalStorage = createMMKV({ id: 'global-app-storage' })
|
|
20
20
|
* ```
|
|
21
21
|
*
|
|
22
22
|
* @default 'mmkv.default'
|
|
@@ -27,7 +27,7 @@ export interface Configuration {
|
|
|
27
27
|
|
|
28
28
|
* @example
|
|
29
29
|
* ```ts
|
|
30
|
-
* const temporaryStorage =
|
|
30
|
+
* const temporaryStorage = createMMKV({ path: '/tmp/' })
|
|
31
31
|
* ```
|
|
32
32
|
*
|
|
33
33
|
* @note On iOS, if an `AppGroup` is set in `Info.plist` and `path` is `undefined`, MMKV will use the `AppGroup` directory.
|
|
@@ -44,7 +44,7 @@ export interface Configuration {
|
|
|
44
44
|
*
|
|
45
45
|
* @example
|
|
46
46
|
* ```ts
|
|
47
|
-
* const secureStorage =
|
|
47
|
+
* const secureStorage = createMMKV({ encryptionKey: 'my-encryption-key!' })
|
|
48
48
|
* ```
|
|
49
49
|
*
|
|
50
50
|
* @default undefined
|
|
@@ -9,7 +9,10 @@ export interface MMKVPlatformContext extends HybridObject<{
|
|
|
9
9
|
getBaseDirectory(): string;
|
|
10
10
|
/**
|
|
11
11
|
* Get the MMKV AppGroup's directory.
|
|
12
|
+
* The AppGroup can be set in your App's `Info.plist`, and will enable
|
|
13
|
+
* data sharing between main app, companions (e.g. watch app) and extensions.
|
|
12
14
|
* @platform iOS
|
|
15
|
+
* @default undefined
|
|
13
16
|
*/
|
|
14
|
-
getAppGroupDirectory(): string;
|
|
17
|
+
getAppGroupDirectory(): string | undefined;
|
|
15
18
|
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
#include <string>
|
|
13
|
+
#include <optional>
|
|
13
14
|
|
|
14
15
|
namespace margelo::nitro::mmkv {
|
|
15
16
|
|
|
@@ -42,10 +43,10 @@ namespace margelo::nitro::mmkv {
|
|
|
42
43
|
auto __result = method(_javaPart);
|
|
43
44
|
return __result->toStdString();
|
|
44
45
|
}
|
|
45
|
-
std::string JHybridMMKVPlatformContextSpec::getAppGroupDirectory() {
|
|
46
|
+
std::optional<std::string> JHybridMMKVPlatformContextSpec::getAppGroupDirectory() {
|
|
46
47
|
static const auto method = javaClassStatic()->getMethod<jni::local_ref<jni::JString>()>("getAppGroupDirectory");
|
|
47
48
|
auto __result = method(_javaPart);
|
|
48
|
-
return __result->toStdString();
|
|
49
|
+
return __result != nullptr ? std::make_optional(__result->toStdString()) : std::nullopt;
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
} // namespace margelo::nitro::mmkv
|
|
@@ -20,6 +20,7 @@ namespace NitroMmkv { class HybridMMKVPlatformContextSpec_cxx; }
|
|
|
20
20
|
#include <NitroModules/Result.hpp>
|
|
21
21
|
#include <exception>
|
|
22
22
|
#include <memory>
|
|
23
|
+
#include <optional>
|
|
23
24
|
#include <string>
|
|
24
25
|
|
|
25
26
|
/**
|
|
@@ -28,6 +29,21 @@ namespace NitroMmkv { class HybridMMKVPlatformContextSpec_cxx; }
|
|
|
28
29
|
*/
|
|
29
30
|
namespace margelo::nitro::mmkv::bridge::swift {
|
|
30
31
|
|
|
32
|
+
// pragma MARK: std::optional<std::string>
|
|
33
|
+
/**
|
|
34
|
+
* Specialized version of `std::optional<std::string>`.
|
|
35
|
+
*/
|
|
36
|
+
using std__optional_std__string_ = std::optional<std::string>;
|
|
37
|
+
inline std::optional<std::string> create_std__optional_std__string_(const std::string& value) noexcept {
|
|
38
|
+
return std::optional<std::string>(value);
|
|
39
|
+
}
|
|
40
|
+
inline bool has_value_std__optional_std__string_(const std::optional<std::string>& optional) noexcept {
|
|
41
|
+
return optional.has_value();
|
|
42
|
+
}
|
|
43
|
+
inline std::string get_std__optional_std__string_(const std::optional<std::string>& optional) noexcept {
|
|
44
|
+
return *optional;
|
|
45
|
+
}
|
|
46
|
+
|
|
31
47
|
// pragma MARK: std::shared_ptr<HybridMMKVPlatformContextSpec>
|
|
32
48
|
/**
|
|
33
49
|
* Specialized version of `std::shared_ptr<HybridMMKVPlatformContextSpec>`.
|
|
@@ -48,5 +64,14 @@ namespace margelo::nitro::mmkv::bridge::swift {
|
|
|
48
64
|
inline Result_std__string_ create_Result_std__string_(const std::exception_ptr& error) noexcept {
|
|
49
65
|
return Result<std::string>::withError(error);
|
|
50
66
|
}
|
|
67
|
+
|
|
68
|
+
// pragma MARK: Result<std::optional<std::string>>
|
|
69
|
+
using Result_std__optional_std__string__ = Result<std::optional<std::string>>;
|
|
70
|
+
inline Result_std__optional_std__string__ create_Result_std__optional_std__string__(const std::optional<std::string>& value) noexcept {
|
|
71
|
+
return Result<std::optional<std::string>>::withValue(value);
|
|
72
|
+
}
|
|
73
|
+
inline Result_std__optional_std__string__ create_Result_std__optional_std__string__(const std::exception_ptr& error) noexcept {
|
|
74
|
+
return Result<std::optional<std::string>>::withError(error);
|
|
75
|
+
}
|
|
51
76
|
|
|
52
77
|
} // namespace margelo::nitro::mmkv::bridge::swift
|
|
@@ -15,6 +15,7 @@ namespace NitroMmkv { class HybridMMKVPlatformContextSpec_cxx; }
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
#include <string>
|
|
18
|
+
#include <optional>
|
|
18
19
|
|
|
19
20
|
#include "NitroMmkv-Swift-Cxx-Umbrella.hpp"
|
|
20
21
|
|
|
@@ -65,7 +66,7 @@ namespace margelo::nitro::mmkv {
|
|
|
65
66
|
auto __value = std::move(__result.value());
|
|
66
67
|
return __value;
|
|
67
68
|
}
|
|
68
|
-
inline std::string getAppGroupDirectory() override {
|
|
69
|
+
inline std::optional<std::string> getAppGroupDirectory() override {
|
|
69
70
|
auto __result = _swiftPart.getAppGroupDirectory();
|
|
70
71
|
if (__result.hasError()) [[unlikely]] {
|
|
71
72
|
std::rethrow_exception(__result.error());
|
|
@@ -15,7 +15,7 @@ public protocol HybridMMKVPlatformContextSpec_protocol: HybridObject {
|
|
|
15
15
|
|
|
16
16
|
// Methods
|
|
17
17
|
func getBaseDirectory() throws -> String
|
|
18
|
-
func getAppGroupDirectory() throws -> String
|
|
18
|
+
func getAppGroupDirectory() throws -> String?
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/// See ``HybridMMKVPlatformContextSpec``
|
|
@@ -122,14 +122,20 @@ open class HybridMMKVPlatformContextSpec_cxx {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
@inline(__always)
|
|
125
|
-
public final func getAppGroupDirectory() -> bridge.
|
|
125
|
+
public final func getAppGroupDirectory() -> bridge.Result_std__optional_std__string__ {
|
|
126
126
|
do {
|
|
127
127
|
let __result = try self.__implementation.getAppGroupDirectory()
|
|
128
|
-
let __resultCpp =
|
|
129
|
-
|
|
128
|
+
let __resultCpp = { () -> bridge.std__optional_std__string_ in
|
|
129
|
+
if let __unwrappedValue = __result {
|
|
130
|
+
return bridge.create_std__optional_std__string_(std.string(__unwrappedValue))
|
|
131
|
+
} else {
|
|
132
|
+
return .init()
|
|
133
|
+
}
|
|
134
|
+
}()
|
|
135
|
+
return bridge.create_Result_std__optional_std__string__(__resultCpp)
|
|
130
136
|
} catch (let __error) {
|
|
131
137
|
let __exceptionPtr = __error.toCpp()
|
|
132
|
-
return bridge.
|
|
138
|
+
return bridge.create_Result_std__optional_std__string__(__exceptionPtr)
|
|
133
139
|
}
|
|
134
140
|
}
|
|
135
141
|
}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
#include <string>
|
|
19
|
+
#include <optional>
|
|
19
20
|
|
|
20
21
|
namespace margelo::nitro::mmkv {
|
|
21
22
|
|
|
@@ -49,7 +50,7 @@ namespace margelo::nitro::mmkv {
|
|
|
49
50
|
public:
|
|
50
51
|
// Methods
|
|
51
52
|
virtual std::string getBaseDirectory() = 0;
|
|
52
|
-
virtual std::string getAppGroupDirectory() = 0;
|
|
53
|
+
virtual std::optional<std::string> getAppGroupDirectory() = 0;
|
|
53
54
|
|
|
54
55
|
protected:
|
|
55
56
|
// Hybrid Setup
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-mmkv",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.7",
|
|
4
4
|
"description": "react-native-mmkv",
|
|
5
5
|
"main": "lib/index",
|
|
6
6
|
"module": "lib/index",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"ios/**/*.cpp",
|
|
24
24
|
"ios/**/*.swift",
|
|
25
25
|
"cpp/**/*.h",
|
|
26
|
-
"cpp/**/*.
|
|
26
|
+
"cpp/**/*.hpp",
|
|
27
27
|
"cpp/**/*.cpp",
|
|
28
28
|
"app.plugin.js",
|
|
29
29
|
"nitro.json",
|
|
@@ -60,6 +60,7 @@
|
|
|
60
60
|
"registry": "https://registry.npmjs.org/"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
|
+
"@expo/config-plugins": "^10.1.2",
|
|
63
64
|
"@react-native/eslint-config": "0.81.0",
|
|
64
65
|
"@testing-library/react-native": "^13.3.1",
|
|
65
66
|
"@types/jest": "^29.5.12",
|
|
@@ -67,7 +68,6 @@
|
|
|
67
68
|
"eslint": "^8.57.0",
|
|
68
69
|
"eslint-config-prettier": "^9.1.0",
|
|
69
70
|
"eslint-plugin-prettier": "^5.2.1",
|
|
70
|
-
"jest": "^30.0.5",
|
|
71
71
|
"nitro-codegen": "*",
|
|
72
72
|
"prettier": "^3.3.3",
|
|
73
73
|
"react": "19.1.0",
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
render,
|
|
7
7
|
renderHook,
|
|
8
8
|
screen,
|
|
9
|
+
cleanup,
|
|
9
10
|
} from '@testing-library/react-native'
|
|
10
11
|
import { createMMKV, useMMKVNumber, useMMKVString } from '..'
|
|
11
12
|
|
|
@@ -16,6 +17,10 @@ beforeEach(() => {
|
|
|
16
17
|
mmkv.trim()
|
|
17
18
|
})
|
|
18
19
|
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
cleanup()
|
|
22
|
+
})
|
|
23
|
+
|
|
19
24
|
test('hooks update when the value is changed directly through the instance', () => {
|
|
20
25
|
const { result } = renderHook(() => useMMKVString('string-key', mmkv))
|
|
21
26
|
|
|
@@ -7,10 +7,31 @@ export function createMockMMKV(): MMKV {
|
|
|
7
7
|
const storage = new Map<string, string | boolean | number | ArrayBuffer>()
|
|
8
8
|
const listeners = new Set<(key: string) => void>()
|
|
9
9
|
|
|
10
|
+
const notifyListeners = (key: string) => {
|
|
11
|
+
listeners.forEach((listener) => {
|
|
12
|
+
listener(key)
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
|
|
10
16
|
return {
|
|
11
|
-
clearAll: () =>
|
|
12
|
-
|
|
13
|
-
|
|
17
|
+
clearAll: () => {
|
|
18
|
+
const keysBefore = storage.keys()
|
|
19
|
+
storage.clear()
|
|
20
|
+
// Notify all listeners for all keys that were cleared
|
|
21
|
+
for (const key of keysBefore) {
|
|
22
|
+
notifyListeners(key)
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
remove: (key) => {
|
|
26
|
+
const deleted = storage.delete(key)
|
|
27
|
+
if (deleted) {
|
|
28
|
+
notifyListeners(key)
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
set: (key, value) => {
|
|
32
|
+
storage.set(key, value)
|
|
33
|
+
notifyListeners(key)
|
|
34
|
+
},
|
|
14
35
|
getString: (key) => {
|
|
15
36
|
const result = storage.get(key)
|
|
16
37
|
return typeof result === 'string' ? result : undefined
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ConfigPlugin } from '@expo/config-plugins'
|
|
2
|
+
import { createRunOncePlugin, withGradleProperties } from '@expo/config-plugins'
|
|
3
|
+
|
|
4
|
+
const pkg = require('../../package.json')
|
|
5
|
+
|
|
6
|
+
const withMMKV: ConfigPlugin<{}> = (config) => {
|
|
7
|
+
// remove 32-bit architectures from gradle.properties
|
|
8
|
+
return withGradleProperties(config, (cfg) => {
|
|
9
|
+
// Drop any existing entry…
|
|
10
|
+
cfg.modResults = cfg.modResults.filter(
|
|
11
|
+
(p) => !(p.type === 'property' && p.key === 'reactNativeArchitectures')
|
|
12
|
+
)
|
|
13
|
+
// …and force 64-bit only.
|
|
14
|
+
cfg.modResults.push({
|
|
15
|
+
type: 'property',
|
|
16
|
+
key: 'reactNativeArchitectures',
|
|
17
|
+
value: 'arm64-v8a,x86_64',
|
|
18
|
+
})
|
|
19
|
+
return cfg
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default createRunOncePlugin(withMMKV, pkg.name, pkg.version)
|
|
@@ -17,8 +17,8 @@ export interface Configuration {
|
|
|
17
17
|
*
|
|
18
18
|
* @example
|
|
19
19
|
* ```ts
|
|
20
|
-
* const userStorage =
|
|
21
|
-
* const globalStorage =
|
|
20
|
+
* const userStorage = createMMKV({ id: `user-${userId}-storage` })
|
|
21
|
+
* const globalStorage = createMMKV({ id: 'global-app-storage' })
|
|
22
22
|
* ```
|
|
23
23
|
*
|
|
24
24
|
* @default 'mmkv.default'
|
|
@@ -29,7 +29,7 @@ export interface Configuration {
|
|
|
29
29
|
|
|
30
30
|
* @example
|
|
31
31
|
* ```ts
|
|
32
|
-
* const temporaryStorage =
|
|
32
|
+
* const temporaryStorage = createMMKV({ path: '/tmp/' })
|
|
33
33
|
* ```
|
|
34
34
|
*
|
|
35
35
|
* @note On iOS, if an `AppGroup` is set in `Info.plist` and `path` is `undefined`, MMKV will use the `AppGroup` directory.
|
|
@@ -46,7 +46,7 @@ export interface Configuration {
|
|
|
46
46
|
*
|
|
47
47
|
* @example
|
|
48
48
|
* ```ts
|
|
49
|
-
* const secureStorage =
|
|
49
|
+
* const secureStorage = createMMKV({ encryptionKey: 'my-encryption-key!' })
|
|
50
50
|
* ```
|
|
51
51
|
*
|
|
52
52
|
* @default undefined
|
|
@@ -8,7 +8,10 @@ export interface MMKVPlatformContext
|
|
|
8
8
|
getBaseDirectory(): string
|
|
9
9
|
/**
|
|
10
10
|
* Get the MMKV AppGroup's directory.
|
|
11
|
+
* The AppGroup can be set in your App's `Info.plist`, and will enable
|
|
12
|
+
* data sharing between main app, companions (e.g. watch app) and extensions.
|
|
11
13
|
* @platform iOS
|
|
14
|
+
* @default undefined
|
|
12
15
|
*/
|
|
13
|
-
getAppGroupDirectory(): string
|
|
16
|
+
getAppGroupDirectory(): string | undefined
|
|
14
17
|
}
|