Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 31 additions & 28 deletions packages/react-native-mmkv/cpp/HybridMMKV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

namespace margelo::nitro::mmkv {

HybridMMKV::HybridMMKV(const Configuration& config) : HybridObject(TAG) {
HybridMMKV::HybridMMKV(const Configuration& config) : _config(config), HybridObject(TAG) {
std::string path = config.path.has_value() ? config.path.value() : "";
std::string encryptionKey = config.encryptionKey.has_value() ? config.encryptionKey.value() : "";
bool hasEncryptionKey = encryptionKey.size() > 0;
Expand All @@ -29,12 +29,12 @@ HybridMMKV::HybridMMKV(const Configuration& config) : HybridObject(TAG) {
}

#ifdef __APPLE__
instance = MMKV::mmkvWithID(config.id, mode, encryptionKeyPtr, pathPtr);
_instance = MMKV::mmkvWithID(config.id, mode, encryptionKeyPtr, pathPtr);
#else
instance = MMKV::mmkvWithID(config.id, DEFAULT_MMAP_SIZE, mode, encryptionKeyPtr, pathPtr);
_instance = MMKV::mmkvWithID(config.id, DEFAULT_MMAP_SIZE, mode, encryptionKeyPtr, pathPtr);
#endif

if (instance == nullptr) [[unlikely]] {
if (_instance == nullptr) [[unlikely]] {
// Check if instanceId is invalid
if (config.id.empty()) [[unlikely]] {
throw std::runtime_error("Failed to create MMKV instance! `id` cannot be empty!");
Expand All @@ -55,11 +55,14 @@ HybridMMKV::HybridMMKV(const Configuration& config) : HybridObject(TAG) {
}
}

Configuration HybridMMKV::getConfig() {
return _config;
}
double HybridMMKV::getSize() {
return instance->actualSize();
return _instance->actualSize();
}
bool HybridMMKV::getIsReadOnly() {
return instance->isReadOnly();
return _instance->isReadOnly();
}

// helper: overload pattern matching for lambdas
Expand All @@ -78,32 +81,32 @@ void HybridMMKV::set(const std::string& key, const std::variant<bool, std::share
// Pattern-match each potential value in std::variant
bool didSet = std::visit(overloaded{[&](bool b) {
// boolean
return instance->set(b, key);
return _instance->set(b, key);
},
[&](const std::shared_ptr<ArrayBuffer>& buf) {
// ArrayBuffer
MMBuffer buffer(buf->data(), buf->size(), MMBufferCopyFlag::MMBufferNoCopy);
return instance->set(std::move(buffer), key);
return _instance->set(std::move(buffer), key);
},
[&](const std::string& string) {
// string
return instance->set(string, key);
return _instance->set(string, key);
},
[&](double number) {
// number
return instance->set(number, key);
return _instance->set(number, key);
}},
value);
if (!didSet) {
throw std::runtime_error("Failed to set value for key \"" + key + "\"!");
}

// Notify on changed
MMKVValueChangedListenerRegistry::notifyOnValueChanged(instance->mmapID(), key);
MMKVValueChangedListenerRegistry::notifyOnValueChanged(_instance->mmapID(), key);
}
std::optional<bool> HybridMMKV::getBoolean(const std::string& key) {
bool hasValue;
bool result = instance->getBool(key, /* defaultValue */ false, &hasValue);
bool result = _instance->getBool(key, /* defaultValue */ false, &hasValue);
if (hasValue) {
return result;
} else {
Expand All @@ -112,7 +115,7 @@ std::optional<bool> HybridMMKV::getBoolean(const std::string& key) {
}
std::optional<std::string> HybridMMKV::getString(const std::string& key) {
std::string result;
bool hasValue = instance->getString(key, result, /* inplaceModification */ true);
bool hasValue = _instance->getString(key, result, /* inplaceModification */ true);
if (hasValue) {
return result;
} else {
Expand All @@ -121,7 +124,7 @@ std::optional<std::string> HybridMMKV::getString(const std::string& key) {
}
std::optional<double> HybridMMKV::getNumber(const std::string& key) {
bool hasValue;
double result = instance->getDouble(key, /* defaultValue */ 0.0, &hasValue);
double result = _instance->getDouble(key, /* defaultValue */ 0.0, &hasValue);
if (hasValue) {
return result;
} else {
Expand All @@ -132,11 +135,11 @@ std::optional<std::shared_ptr<ArrayBuffer>> HybridMMKV::getBuffer(const std::str
MMBuffer result;
#ifdef __APPLE__
// iOS: Convert std::string to NSString* for MMKVCore pod compatibility
bool hasValue = instance->getBytes(@(key.c_str()), result);
bool hasValue = _instance->getBytes(@(key.c_str()), result);
#else
// Android/other platforms: Use std::string directly (converts to
// std::string_view)
bool hasValue = instance->getBytes(key, result);
bool hasValue = _instance->getBytes(key, result);
#endif
if (hasValue) {
return std::make_shared<ManagedMMBuffer>(std::move(result));
Expand All @@ -145,49 +148,49 @@ std::optional<std::shared_ptr<ArrayBuffer>> HybridMMKV::getBuffer(const std::str
}
}
bool HybridMMKV::contains(const std::string& key) {
return instance->containsKey(key);
return _instance->containsKey(key);
}
bool HybridMMKV::remove(const std::string& key) {
bool wasRemoved = instance->removeValueForKey(key);
bool wasRemoved = _instance->removeValueForKey(key);
if (wasRemoved) {
// Notify on changed
MMKVValueChangedListenerRegistry::notifyOnValueChanged(instance->mmapID(), key);
MMKVValueChangedListenerRegistry::notifyOnValueChanged(_instance->mmapID(), key);
}
return wasRemoved;
}
std::vector<std::string> HybridMMKV::getAllKeys() {
return instance->allKeys();
return _instance->allKeys();
}
void HybridMMKV::clearAll() {
auto keysBefore = getAllKeys();
instance->clearAll();
_instance->clearAll();
for (const auto& key : keysBefore) {
// Notify on changed
MMKVValueChangedListenerRegistry::notifyOnValueChanged(instance->mmapID(), key);
MMKVValueChangedListenerRegistry::notifyOnValueChanged(_instance->mmapID(), key);
}
}
void HybridMMKV::recrypt(const std::optional<std::string>& key) {
bool successful = false;
if (key.has_value()) {
// Encrypt with the given key
successful = instance->reKey(key.value());
successful = _instance->reKey(key.value());
} else {
// Remove the encryption key by setting it to a blank string
successful = instance->reKey(std::string());
successful = _instance->reKey(std::string());
}
if (!successful) {
throw std::runtime_error("Failed to recrypt MMKV instance!");
}
}
void HybridMMKV::trim() {
instance->trim();
instance->clearMemoryCache();
_instance->trim();
_instance->clearMemoryCache();
}

Listener HybridMMKV::addOnValueChangedListener(const std::function<void(const std::string& /* key */)>& onValueChanged) {
// Add listener
auto mmkvID = instance->mmapID();
auto listenerID = MMKVValueChangedListenerRegistry::addListener(instance->mmapID(), onValueChanged);
auto mmkvID = _instance->mmapID();
auto listenerID = MMKVValueChangedListenerRegistry::addListener(_instance->mmapID(), onValueChanged);

return Listener([=]() {
// remove()
Expand Down
4 changes: 3 additions & 1 deletion packages/react-native-mmkv/cpp/HybridMMKV.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class HybridMMKV final : public HybridMMKVSpec {

public:
// Properties
Configuration getConfig() override;
double getSize() override;
bool getIsReadOnly() override;

Expand All @@ -41,7 +42,8 @@ class HybridMMKV final : public HybridMMKVSpec {
static MMKVMode getMMKVMode(const Configuration& config);

private:
MMKV* instance;
MMKV* _instance;
Configuration _config;
};

} // namespace margelo::nitro::mmkv
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace margelo::nitro::mmkv {
HybridObject::loadHybridMethods();
// load custom methods/properties
registerHybrids(this, [](Prototype& prototype) {
prototype.registerHybridGetter("config", &HybridMMKVSpec::getConfig);
prototype.registerHybridGetter("size", &HybridMMKVSpec::getSize);
prototype.registerHybridGetter("isReadOnly", &HybridMMKVSpec::getIsReadOnly);
prototype.registerHybridMethod("set", &HybridMMKVSpec::set);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@
#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
#endif

// Forward declaration of `Configuration` to properly resolve imports.
namespace margelo::nitro::mmkv { struct Configuration; }
// Forward declaration of `Listener` to properly resolve imports.
namespace margelo::nitro::mmkv { struct Listener; }

#include "Configuration.hpp"
#include <string>
#include <NitroModules/ArrayBuffer.hpp>
#include <variant>
Expand Down Expand Up @@ -51,6 +54,7 @@ namespace margelo::nitro::mmkv {

public:
// Properties
virtual Configuration getConfig() = 0;
virtual double getSize() = 0;
virtual bool getIsReadOnly() = 0;

Expand Down
2 changes: 1 addition & 1 deletion packages/react-native-mmkv/src/createMMKV/createMMKV.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ let platformContext: MMKVPlatformContext | undefined
export function createMMKV(configuration?: Configuration): MMKV {
if (isTest()) {
// In a test environment, we mock the MMKV instance.
return createMockMMKV()
return createMockMMKV(configuration)
}

if (platformContext == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export function createMMKV(
}

return {
config: config,
clearAll: () => {
const keys = Object.keys(storage())
for (const key of keys) {
Expand Down
7 changes: 6 additions & 1 deletion packages/react-native-mmkv/src/createMMKV/createMockMMKV.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import type { MMKV } from '../specs/MMKV.nitro'
import type { Configuration } from '../specs/MMKVFactory.nitro'

/**
* Mock MMKV instance when used in a Jest/Test environment.
*/
export function createMockMMKV(): MMKV {
export function createMockMMKV(
configuration: Configuration = { id: 'mmkv.default' }
): MMKV {
const config = configuration
const storage = new Map<string, string | boolean | number | ArrayBuffer>()
const listeners = new Set<(key: string) => void>()

Expand All @@ -14,6 +18,7 @@ export function createMockMMKV(): MMKV {
}

return {
config: config,
clearAll: () => {
const keysBefore = storage.keys()
storage.clear()
Expand Down
24 changes: 15 additions & 9 deletions packages/react-native-mmkv/src/specs/MMKV.nitro.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
import type { HybridObject } from 'react-native-nitro-modules'
import type { Configuration } from './MMKVFactory.nitro'

export interface Listener {
remove: () => void
}

export interface MMKV extends HybridObject<{ ios: 'c++'; android: 'c++' }> {
/**
* The {@linkcode Configuration} that was used to create this {@linkcode MMKV}
* instance.
*/
readonly config: Configuration
/**
* Get the current total size of the storage, in bytes.
*/
readonly size: number
/**
* Returns whether this instance is in read-only mode or not.
* If this is `true`, you can only use "get"-functions.
*/
readonly isReadOnly: boolean
/**
* Set a {@linkcode value} for the given {@linkcode key}.
*
Expand Down Expand Up @@ -75,15 +90,6 @@ export interface MMKV extends HybridObject<{ ios: 'c++'; android: 'c++' }> {
* In most applications, this is not needed at all.
*/
trim(): void
/**
* Get the current total size of the storage, in bytes.
*/
readonly size: number
/**
* Returns whether this instance is in read-only mode or not.
* If this is `true`, you can only use "get"-functions.
*/
readonly isReadOnly: boolean
/**
* Adds a value changed listener. The Listener will be called whenever any value
* in this storage instance changes (set or delete).
Expand Down
Loading