Skip to content

Commit

Permalink
feat: return outcome of set and recrypt.
Browse files Browse the repository at this point in the history
This commits adds a boolean return value from the underlying MMKV library to the JS environment for the set and recrypt calls.
  • Loading branch information
antondalgren committed Sep 26, 2023
1 parent f564552 commit a1572f4
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 27 deletions.
18 changes: 10 additions & 8 deletions android/src/main/cpp/MmkvHostObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,19 @@ jsi::Value MmkvHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& pro
"MMKV::set: First argument ('key') has to be of type string!");
}

bool result = false;
auto keyName = arguments[0].getString(runtime).utf8(runtime);

if (arguments[1].isBool()) {
// bool
instance->set(arguments[1].getBool(), keyName);
result = instance->set(arguments[1].getBool(), keyName);
} else if (arguments[1].isNumber()) {
// number
instance->set(arguments[1].getNumber(), keyName);
result = instance->set(arguments[1].getNumber(), keyName);
} else if (arguments[1].isString()) {
// string
auto stringValue = arguments[1].getString(runtime).utf8(runtime);
instance->set(stringValue, keyName);
result = instance->set(stringValue, keyName);
} else if (arguments[1].isObject()) {
// object
auto object = arguments[1].asObject(runtime);
Expand All @@ -91,7 +92,7 @@ jsi::Value MmkvHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& pro
auto bufferValue = typedArray.getBuffer(runtime);
mmkv::MMBuffer buffer(bufferValue.data(runtime), bufferValue.size(runtime),
mmkv::MMBufferCopyFlag::MMBufferNoCopy);
instance->set(buffer, keyName);
result = instance->set(buffer, keyName);
} else {
// unknown object
throw jsi::JSError(
Expand All @@ -104,7 +105,7 @@ jsi::Value MmkvHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& pro
"MMKV::set: 'value' argument is not of type bool, number, string or buffer!");
}

return jsi::Value::undefined();
return jsi::Value(result);
});
}

Expand Down Expand Up @@ -269,19 +270,20 @@ jsi::Value MmkvHostObject::get(jsi::Runtime& runtime, const jsi::PropNameID& pro
1, // encryptionKey
[this](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments,
size_t count) -> jsi::Value {
bool result;
if (arguments[0].isUndefined()) {
// reset encryption key to "no encryption"
instance->reKey(std::string());
result = instance->reKey(std::string());
} else if (arguments[0].isString()) {
// reKey(..) with new encryption-key
auto encryptionKey = arguments[0].getString(runtime).utf8(runtime);
instance->reKey(encryptionKey);
result = instance->reKey(encryptionKey);
} else {
throw jsi::JSError(
runtime,
"First argument ('encryptionKey') has to be of type string (or undefined)!");
}
return jsi::Value::undefined();
return jsi::Value(result);
});
}

Expand Down
7 changes: 6 additions & 1 deletion example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default function App() {
const [text, setText] = React.useState<string>('');
const [key, setKey] = React.useState<string>('');
const [keys, setKeys] = React.useState<string[]>([]);
const [success, setSuccess] = React.useState<boolean | undefined>();

const [example, setExample] = useMMKVString('yeeeet');

Expand All @@ -19,7 +20,8 @@ export default function App() {
}
try {
console.log('setting...');
storage.set(key, text);
const result = storage.set(key, text);
setSuccess(result);
console.log('set.');
} catch (e) {
console.error('Error:', e);
Expand Down Expand Up @@ -86,6 +88,9 @@ export default function App() {
onChangeText={setText}
/>
</View>
<View style={styles.row}>
{success && <Text>Saved: {success ? 'successfully' : 'failed'}</Text>}
</View>
<Button onPress={save} title="Save to MMKV" />
<Button onPress={read} title="Read from MMKV" />
</View>
Expand Down
19 changes: 10 additions & 9 deletions ios/MmkvHostObject.mm
Original file line number Diff line number Diff line change
Expand Up @@ -82,18 +82,18 @@
throw jsi::JSError(runtime,
"MMKV::set: First argument ('key') has to be of type string!");
}

bool result = false;
auto keyName = convertJSIStringToNSString(runtime, arguments[0].getString(runtime));
if (arguments[1].isBool()) {
// Set as boolean
[instance setBool:arguments[1].getBool() forKey:keyName];
result = [instance setBool:arguments[1].getBool() forKey:keyName];
} else if (arguments[1].isNumber()) {
// Set as number (double in JS)
[instance setDouble:arguments[1].getNumber() forKey:keyName];
result = [instance setDouble:arguments[1].getNumber() forKey:keyName];
} else if (arguments[1].isString()) {
// Set as UTF-8 string
auto stringValue = convertJSIStringToNSString(runtime, arguments[1].getString(runtime));
[instance setString:stringValue forKey:keyName];
result = [instance setString:stringValue forKey:keyName];
} else if (arguments[1].isObject()) {
// object
auto object = arguments[1].asObject(runtime);
Expand All @@ -103,7 +103,7 @@
auto bufferValue = typedArray.getBuffer(runtime);
auto data = [[NSData alloc] initWithBytes:bufferValue.data(runtime)
length:bufferValue.length(runtime)];
[instance setData:data forKey:keyName];
result = [instance setData:data forKey:keyName];
} else {
// unknown object
throw jsi::JSError(
Expand All @@ -115,7 +115,7 @@
runtime, "Second argument ('value') has to be of type bool, number or string!");
}

return jsi::Value::undefined();
return jsi::Value(result);
});
}

Expand Down Expand Up @@ -273,21 +273,22 @@
1, // encryptionKey
[this](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments,
size_t count) -> jsi::Value {
bool result;
if (arguments[0].isUndefined()) {
// reset encryption key to "no encryption"
[instance reKey:nil];
result = [instance reKey:nil];
} else if (arguments[0].isString()) {
// reKey(..) with new encryption-key
NSString* encryptionKey =
convertJSIStringToNSString(runtime, arguments[0].getString(runtime));
NSData* encryptionKeyBytes = [encryptionKey dataUsingEncoding:NSUTF8StringEncoding];
[instance reKey:encryptionKeyBytes];
result = [instance reKey:encryptionKeyBytes];
} else {
throw jsi::JSError(
runtime,
"First argument ('encryptionKey') has to be of type string (or undefined)!");
}
return jsi::Value::undefined();
return jsi::Value(result);
});
}

Expand Down
15 changes: 8 additions & 7 deletions src/MMKV.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ export interface MMKVConfiguration {
* ```ts
* const temporaryStorage = new MMKV({ path: '/tmp/' })
* ```
*
* _Notice_: On iOS you can set the AppGroup bundle property to share the same storage between your app and its extensions.
*
* _Notice_: On iOS you can set the AppGroup bundle property to share the same storage between your app and its extensions.
* In this case `path` property will be ignored.
* See more on MMKV configuration [here](https://github.com/Tencent/MMKV/wiki/iOS_tutorial#configuration).
*/
Expand All @@ -55,7 +55,7 @@ interface MMKVInterface {
/**
* Set a value for the given `key`.
*/
set: (key: string, value: boolean | string | number | Uint8Array) => void;
set: (key: string, value: boolean | string | number | Uint8Array) => boolean;
/**
* Get the boolean value for the given `key`, or `undefined` if it does not exist.
*
Expand Down Expand Up @@ -105,7 +105,7 @@ interface MMKVInterface {
*
* Encryption keys can have a maximum length of 16 bytes.
*/
recrypt: (key: string | undefined) => void;
recrypt: (key: string | undefined) => boolean;
/**
* Adds a value changed listener. The Listener will be called whenever any value
* in this storage instance changes (set or delete).
Expand Down Expand Up @@ -179,11 +179,12 @@ export class MMKV implements MMKVInterface {
}
}

set(key: string, value: boolean | string | number | Uint8Array): void {
set(key: string, value: boolean | string | number | Uint8Array): boolean {
const func = this.getFunctionFromCache('set');
func(key, value);
const result = func(key, value);

this.onValuesChanged([key]);
return result;
}
getBoolean(key: string): boolean | undefined {
const func = this.getFunctionFromCache('getBoolean');
Expand Down Expand Up @@ -223,7 +224,7 @@ export class MMKV implements MMKVInterface {

this.onValuesChanged(keys);
}
recrypt(key: string | undefined): void {
recrypt(key: string | undefined): boolean {
const func = this.getFunctionFromCache('recrypt');
return func(key);
}
Expand Down
6 changes: 5 additions & 1 deletion src/createMMKV.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ export const createMockMMKV = (): NativeMMKV => {
return {
clearAll: () => storage.clear(),
delete: (key) => storage.delete(key),
set: (key, value) => storage.set(key, value),
set: (key, value) => {
storage.set(key, value);
return true;
},
getString: (key) => {
const result = storage.get(key);
return typeof result === 'string' ? result : undefined;
Expand All @@ -28,6 +31,7 @@ export const createMockMMKV = (): NativeMMKV => {
contains: (key) => storage.has(key),
recrypt: () => {
console.warn('Encryption is not supported in mocked MMKV instances!');
return true;
},
};
};
3 changes: 2 additions & 1 deletion src/createMMKV.web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const createMMKV = (config: MMKVConfiguration): NativeMMKV => {
if (config.path != null) {
throw new Error("MMKV: 'path' is not supported on Web!");
}

// canUseDOM check prevents spam in Node server environments, such as Next.js server side props.
if (!hasAccessToLocalStorage() && canUseDOM) {
console.warn(
Expand Down Expand Up @@ -91,6 +91,7 @@ export const createMMKV = (config: MMKVConfiguration): NativeMMKV => {
delete: (key) => storage().removeItem(prefixedKey(key)),
set: (key, value) => {
storage().setItem(prefixedKey(key), value.toString());
return true;
},
getString: (key) => storage().getItem(prefixedKey(key)) ?? undefined,
getNumber: (key) => {
Expand Down

0 comments on commit a1572f4

Please sign in to comment.