diff --git a/package/.eslintrc.js b/package/.eslintrc.js index e93749ea..4021535b 100644 --- a/package/.eslintrc.js +++ b/package/.eslintrc.js @@ -4,6 +4,5 @@ module.exports = { ignorePatterns: ['scripts', 'lib'], rules: { 'prettier/prettier': ['warn'], - '@typescript-eslint/consistent-type-imports': 'warn', }, }; diff --git a/package/android/src/main/java/com/mrousavy/mmkv/MmkvPlatformContextModule.java b/package/android/src/main/java/com/mrousavy/mmkv/MmkvPlatformContextModule.java index 4035db7a..f1857f39 100644 --- a/package/android/src/main/java/com/mrousavy/mmkv/MmkvPlatformContextModule.java +++ b/package/android/src/main/java/com/mrousavy/mmkv/MmkvPlatformContextModule.java @@ -1,5 +1,7 @@ package com.mrousavy.mmkv; +import androidx.annotation.Nullable; + import com.facebook.react.bridge.ReactApplicationContext; public class MmkvPlatformContextModule extends NativeMmkvPlatformContextSpec { @@ -14,4 +16,11 @@ public MmkvPlatformContextModule(ReactApplicationContext reactContext) { public String getBaseDirectory() { return context.getFilesDir().getAbsolutePath() + "/mmkv"; } + + @Nullable + @Override + public String getAppGroupDirectory() { + // AppGroups do not exist on Android. It's iOS only. + return null; + } } diff --git a/package/ios/MmkvPlatformContextModule.mm b/package/ios/MmkvPlatformContextModule.mm index eecee126..71e80fe2 100644 --- a/package/ios/MmkvPlatformContextModule.mm +++ b/package/ios/MmkvPlatformContextModule.mm @@ -24,7 +24,7 @@ - (NSString*)getBaseDirectory { NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); #endif NSString* documentPath = (NSString*)[paths firstObject]; - if ([documentPath length] > 0) { + if (documentPath.length > 0) { NSString* basePath = [documentPath stringByAppendingPathComponent:@"mmkv"]; return basePath; } else { @@ -34,4 +34,22 @@ - (NSString*)getBaseDirectory { } } +- (NSString*)getAppGroupDirectory { + NSString* appGroup = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"AppGroup"]; + if (appGroup == nil) { + // No AppGroup set in Info.plist. + return nil; + } + NSURL* groupDir = + [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:appGroup]; + if (groupDir == nil) { + // We have an AppGroup set in Info.plist, but the path is not readable! + @throw [NSException exceptionWithName:@"AppGroupNotAccessible" + reason:@"An AppGroup was set in Info.plist, but it is not " + @"accessible via NSFileManager!" + userInfo:@{@"appGroup" : appGroup}]; + } + return groupDir.path; +} + @end diff --git a/package/src/MMKV.ts b/package/src/MMKV.ts index a32e0f89..84ee69a6 100644 --- a/package/src/MMKV.ts +++ b/package/src/MMKV.ts @@ -4,7 +4,6 @@ import { createMockMMKV } from './createMMKV.mock'; import { isTest } from './PlatformChecker'; import type { Configuration } from './NativeMmkv'; import type { Listener, MMKVInterface, NativeMMKV } from './Types'; -export type { Configuration } from './NativeMmkv'; const onValueChangedListeners = new Map void)[]>(); diff --git a/package/src/NativeMmkv.ts b/package/src/NativeMmkv.ts index 6035fc7f..0c7e329e 100644 --- a/package/src/NativeMmkv.ts +++ b/package/src/NativeMmkv.ts @@ -42,9 +42,9 @@ export interface Configuration { * 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. - * In this case `path` property will be ignored. - * See more on MMKV configuration [here](https://github.com/Tencent/MMKV/wiki/iOS_tutorial#configuration). + * @note On iOS, if an `AppGroup` is set in `Info.plist` and `path` is `undefined`, MMKV will use the `AppGroup` directory. + * App Groups allow you to share MMKV storage between apps, widgets and extensions within the same AppGroup bundle. + * For more information, see [the `Configuration` section](https://github.com/Tencent/MMKV/wiki/iOS_tutorial#configuration). * * @default undefined */ diff --git a/package/src/NativeMmkvPlatformContext.ts b/package/src/NativeMmkvPlatformContext.ts index 0048fe18..aa4e284f 100644 --- a/package/src/NativeMmkvPlatformContext.ts +++ b/package/src/NativeMmkvPlatformContext.ts @@ -7,6 +7,17 @@ export interface Spec extends TurboModule { * Gets the base directory of the documents storage */ getBaseDirectory(): string; + /** + * Get the App Group directory if it exists, or `undefined` otherwise. + * + * The App Group directory will be used instead of a custom path to use the same + * MMKV instance between the iOS app, Widgets, and other companion apps. + * + * To set an App Group, add a `AppGroup` field to `Info.plist` + * + * @platform ios + */ + getAppGroupDirectory(): string | undefined; } let module: Spec | null; diff --git a/package/src/createMMKV.ts b/package/src/createMMKV.ts index a5cf1a88..f607847d 100644 --- a/package/src/createMMKV.ts +++ b/package/src/createMMKV.ts @@ -1,10 +1,28 @@ -import type { Configuration } from './MMKV'; -import { getMMKVTurboModule } from './NativeMmkv'; +import { Platform } from 'react-native'; +import { getMMKVTurboModule, type Configuration } from './NativeMmkv'; import type { NativeMMKV } from './Types'; +import { getMMKVPlatformContextTurboModule } from './NativeMmkvPlatformContext'; export const createMMKV = (config: Configuration): NativeMMKV => { const module = getMMKVTurboModule(); + if (Platform.OS === 'ios') { + if (config.path == null) { + try { + // If no `path` was supplied, we check if an `AppGroup` was set in Info.plist + const appGroupDirectory = + getMMKVPlatformContextTurboModule().getAppGroupDirectory(); + if (appGroupDirectory != null) { + // If we have an `AppGroup` in Info.plist, use that as a path. + config.path = appGroupDirectory; + } + } catch (e) { + // We cannot throw errors here because it is a sync C++ TurboModule func. idk why. + console.error(e); + } + } + } + const instance = module.createMMKV(config); if (__DEV__) { if (typeof instance !== 'object' || instance == null) { diff --git a/package/src/createMMKV.web.ts b/package/src/createMMKV.web.ts index 2756fb54..d93dde80 100644 --- a/package/src/createMMKV.web.ts +++ b/package/src/createMMKV.web.ts @@ -1,5 +1,5 @@ /* global localStorage */ -import type { Configuration } from './MMKV'; +import type { Configuration } from './NativeMmkv'; import type { NativeMMKV } from './Types'; import { createTextEncoder } from './createTextEncoder'; diff --git a/package/src/hooks.ts b/package/src/hooks.ts index 79c34c59..e2a80a85 100644 --- a/package/src/hooks.ts +++ b/package/src/hooks.ts @@ -1,6 +1,6 @@ import { useRef, useState, useMemo, useCallback, useEffect } from 'react'; -import type { Configuration } from './MMKV'; import { MMKV } from './MMKV'; +import type { Configuration } from './NativeMmkv'; function isConfigurationEqual( left?: Configuration,