Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support AppGroups #705

Merged
merged 6 commits into from
Jul 22, 2024
Merged
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
1 change: 0 additions & 1 deletion package/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,5 @@ module.exports = {
ignorePatterns: ['scripts', 'lib'],
rules: {
'prettier/prettier': ['warn'],
'@typescript-eslint/consistent-type-imports': 'warn',
},
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.mrousavy.mmkv;

import androidx.annotation.Nullable;

import com.facebook.react.bridge.ReactApplicationContext;

public class MmkvPlatformContextModule extends NativeMmkvPlatformContextSpec {
Expand All @@ -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;
}
}
20 changes: 19 additions & 1 deletion package/ios/MmkvPlatformContextModule.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
1 change: 0 additions & 1 deletion package/src/MMKV.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, ((key: string) => void)[]>();

Expand Down
6 changes: 3 additions & 3 deletions package/src/NativeMmkv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
11 changes: 11 additions & 0 deletions package/src/NativeMmkvPlatformContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
22 changes: 20 additions & 2 deletions package/src/createMMKV.ts
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion package/src/createMMKV.web.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down
2 changes: 1 addition & 1 deletion package/src/hooks.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down
Loading