@clix-so/react-native-sdk 0.0.2-beta.2 → 0.0.2-beta.3
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/clix-react-native-sdk.podspec +19 -0
- package/ios/ClixAppGroupDirectory.h +4 -0
- package/ios/ClixAppGroupDirectory.m +75 -0
- package/lib/module/native/getAppGroupDirectory.js +32 -0
- package/lib/module/native/getAppGroupDirectory.js.map +1 -0
- package/lib/module/services/StorageService.js +30 -2
- package/lib/module/services/StorageService.js.map +1 -1
- package/lib/typescript/src/native/getAppGroupDirectory.d.ts +2 -0
- package/lib/typescript/src/native/getAppGroupDirectory.d.ts.map +1 -0
- package/lib/typescript/src/services/StorageService.d.ts +1 -0
- package/lib/typescript/src/services/StorageService.d.ts.map +1 -1
- package/package.json +3 -1
- package/src/native/getAppGroupDirectory.ts +44 -0
- package/src/services/StorageService.ts +27 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = 'clix-react-native-sdk'
|
|
7
|
+
s.version = package['version']
|
|
8
|
+
s.summary = package['description'] || 'Clix React Native SDK'
|
|
9
|
+
s.description = package['description'] || 'Clix React Native SDK'
|
|
10
|
+
s.homepage = package['homepage']
|
|
11
|
+
s.license = { :type => package['license'], :text => 'See LICENSE file' }
|
|
12
|
+
s.author = package['author']
|
|
13
|
+
s.platform = :ios, '14.0'
|
|
14
|
+
s.source = { :git => 'https://github.com/clix-so/clix-react-native-sdk.git', :tag => "v#{package['version']}" }
|
|
15
|
+
s.source_files = 'ios/**/*.{h,m,mm,swift}'
|
|
16
|
+
s.requires_arc = true
|
|
17
|
+
|
|
18
|
+
s.dependency 'React-Core'
|
|
19
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
#import "ClixAppGroupDirectory.h"
|
|
3
|
+
#import <React/RCTLog.h>
|
|
4
|
+
|
|
5
|
+
@implementation ClixAppGroupDirectory
|
|
6
|
+
|
|
7
|
+
RCT_EXPORT_MODULE();
|
|
8
|
+
|
|
9
|
+
+ (BOOL)requiresMainQueueSetup
|
|
10
|
+
{
|
|
11
|
+
return NO;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
static NSString *const kClixDefaultBundleIdentifier = @"com.clix.default";
|
|
15
|
+
static NSString *const kClixAppGroupPrefix = @"group.clix.";
|
|
16
|
+
|
|
17
|
+
+ (NSString *)resolvePrimaryBundleIdentifier
|
|
18
|
+
{
|
|
19
|
+
static NSString *cachedIdentifier = nil;
|
|
20
|
+
static dispatch_once_t onceToken;
|
|
21
|
+
dispatch_once(&onceToken, ^{
|
|
22
|
+
NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
|
|
23
|
+
if (bundleIdentifier == nil || bundleIdentifier.length == 0) {
|
|
24
|
+
id infoValue =
|
|
25
|
+
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"];
|
|
26
|
+
if ([infoValue isKindOfClass:[NSString class]]) {
|
|
27
|
+
NSString *value = (NSString *)infoValue;
|
|
28
|
+
if (value.length > 0) {
|
|
29
|
+
bundleIdentifier = value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (bundleIdentifier == nil || bundleIdentifier.length == 0) {
|
|
35
|
+
bundleIdentifier = kClixDefaultBundleIdentifier;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
cachedIdentifier = bundleIdentifier;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return cachedIdentifier;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
+ (NSString *)bundleIdBasedAppGroupId:(NSString *)bundleIdentifier
|
|
45
|
+
{
|
|
46
|
+
if (bundleIdentifier == nil || bundleIdentifier.length == 0) {
|
|
47
|
+
return nil;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return [NSString stringWithFormat:@"%@%@", kClixAppGroupPrefix, bundleIdentifier];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
+ (NSString *)resolveAppGroupIdentifier
|
|
54
|
+
{
|
|
55
|
+
NSString *bundleIdentifier = [ClixAppGroupDirectory resolvePrimaryBundleIdentifier];
|
|
56
|
+
return [ClixAppGroupDirectory bundleIdBasedAppGroupId:bundleIdentifier];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getAppGroupDirectory)
|
|
60
|
+
{
|
|
61
|
+
NSString *identifier = [ClixAppGroupDirectory resolveAppGroupIdentifier];
|
|
62
|
+
if (identifier == nil || identifier.length == 0) {
|
|
63
|
+
return nil;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
NSURL *url = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:identifier];
|
|
67
|
+
if (url == nil) {
|
|
68
|
+
RCTLogWarn(@"[Clix] Failed to resolve App Group path for identifier '%@'. Check your entitlements and make sure the group exists.", identifier);
|
|
69
|
+
return nil;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return url.path;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import { NativeModules, Platform } from 'react-native';
|
|
4
|
+
import { ClixLogger } from "../utils/logging/ClixLogger.js";
|
|
5
|
+
const MODULE_NAME = 'ClixAppGroupDirectory';
|
|
6
|
+
const resolveModule = () => {
|
|
7
|
+
const nativeModule = NativeModules[MODULE_NAME];
|
|
8
|
+
if (!nativeModule) {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
return nativeModule;
|
|
12
|
+
};
|
|
13
|
+
export const getAppGroupDirectory = () => {
|
|
14
|
+
if (Platform.OS !== 'ios') {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
const nativeModule = resolveModule();
|
|
18
|
+
if (!nativeModule || typeof nativeModule.getAppGroupDirectory !== 'function') {
|
|
19
|
+
ClixLogger.debug('App Group directory native module is unavailable. Falling back to default MMKV path.');
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const directory = nativeModule.getAppGroupDirectory();
|
|
24
|
+
if (typeof directory === 'string' && directory.length > 0) {
|
|
25
|
+
return directory;
|
|
26
|
+
}
|
|
27
|
+
} catch (error) {
|
|
28
|
+
ClixLogger.warn('Failed to resolve App Group directory', error);
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=getAppGroupDirectory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["NativeModules","Platform","ClixLogger","MODULE_NAME","resolveModule","nativeModule","undefined","getAppGroupDirectory","OS","debug","directory","length","error","warn"],"sourceRoot":"../../../src","sources":["native/getAppGroupDirectory.ts"],"mappings":";;AAAA,SAASA,aAAa,EAAEC,QAAQ,QAAQ,cAAc;AACtD,SAASC,UAAU,QAAQ,gCAA6B;AAMxD,MAAMC,WAAW,GAAG,uBAAuB;AAE3C,MAAMC,aAAa,GAAGA,CAAA,KAA+C;EACnE,MAAMC,YAAY,GAAIL,aAAa,CAA6BG,WAAW,CAAC;EAC5E,IAAI,CAACE,YAAY,EAAE;IACjB,OAAOC,SAAS;EAClB;EACA,OAAOD,YAAY;AACrB,CAAC;AAED,OAAO,MAAME,oBAAoB,GAAGA,CAAA,KAA0B;EAC5D,IAAIN,QAAQ,CAACO,EAAE,KAAK,KAAK,EAAE;IACzB,OAAOF,SAAS;EAClB;EAEA,MAAMD,YAAY,GAAGD,aAAa,CAAC,CAAC;EACpC,IACE,CAACC,YAAY,IACb,OAAOA,YAAY,CAACE,oBAAoB,KAAK,UAAU,EACvD;IACAL,UAAU,CAACO,KAAK,CACd,sFACF,CAAC;IACD,OAAOH,SAAS;EAClB;EAEA,IAAI;IACF,MAAMI,SAAS,GAAGL,YAAY,CAACE,oBAAoB,CAAC,CAAC;IACrD,IAAI,OAAOG,SAAS,KAAK,QAAQ,IAAIA,SAAS,CAACC,MAAM,GAAG,CAAC,EAAE;MACzD,OAAOD,SAAS;IAClB;EACF,CAAC,CAAC,OAAOE,KAAK,EAAE;IACdV,UAAU,CAACW,IAAI,CAAC,uCAAuC,EAAED,KAAK,CAAC;EACjE;EAEA,OAAON,SAAS;AAClB,CAAC","ignoreList":[]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import * as MMKVModule from 'react-native-mmkv';
|
|
4
|
+
import { getAppGroupDirectory } from "../native/getAppGroupDirectory.js";
|
|
4
5
|
import { ClixLogger } from "../utils/logging/ClixLogger.js";
|
|
5
6
|
|
|
6
7
|
// Support both v2/v3 (MMKV class) and v4 (createMMKV function)
|
|
@@ -9,6 +10,27 @@ export class StorageService {
|
|
|
9
10
|
constructor(projectId) {
|
|
10
11
|
this.storage = this.initializeCompat(projectId);
|
|
11
12
|
}
|
|
13
|
+
getStoragePath() {
|
|
14
|
+
try {
|
|
15
|
+
const directory = getAppGroupDirectory();
|
|
16
|
+
if (!directory) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* NOTE(nyanxyz):
|
|
22
|
+
* On iOS the mmkv directory is located at {App Group Directory}/mmkv.
|
|
23
|
+
* RN MMKV v2 used {App Group Directory}/mmkv, but v3 switched to {App Group Directory}.
|
|
24
|
+
* We need to keep the directory aligned so iOS and RN can share storage.
|
|
25
|
+
* Therefore we explicitly target the mmkv folder inside the App Group directory.
|
|
26
|
+
* @see https://akshayjadhav.hashnode.dev/how-to-access-react-native-mmkv-in-a-ios-widget-react-native-expo
|
|
27
|
+
*/
|
|
28
|
+
return `${directory}/mmkv`;
|
|
29
|
+
} catch (error) {
|
|
30
|
+
ClixLogger.warn('Failed to resolve App Group directory', error);
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
12
34
|
initializeCompat(projectId) {
|
|
13
35
|
const storageId = `clix.${projectId}`;
|
|
14
36
|
|
|
@@ -16,7 +38,10 @@ export class StorageService {
|
|
|
16
38
|
if (typeof MMKVModule.createMMKV === 'function') {
|
|
17
39
|
return MMKVModule.createMMKV({
|
|
18
40
|
id: storageId,
|
|
19
|
-
encryptionKey: undefined
|
|
41
|
+
encryptionKey: undefined,
|
|
42
|
+
// Add encryption if needed
|
|
43
|
+
path: this.getStoragePath(),
|
|
44
|
+
mode: 'multi-process'
|
|
20
45
|
});
|
|
21
46
|
}
|
|
22
47
|
// v2/v3 API (MMKV class)
|
|
@@ -26,7 +51,10 @@ export class StorageService {
|
|
|
26
51
|
} = MMKVModule;
|
|
27
52
|
return new MMKV({
|
|
28
53
|
id: storageId,
|
|
29
|
-
encryptionKey: undefined
|
|
54
|
+
encryptionKey: undefined,
|
|
55
|
+
// Add encryption if needed
|
|
56
|
+
path: this.getStoragePath(),
|
|
57
|
+
mode: 'multi-process'
|
|
30
58
|
});
|
|
31
59
|
} else {
|
|
32
60
|
throw new Error('No compatible MMKV storage API found');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["MMKVModule","ClixLogger","StorageService","constructor","projectId","storage","initializeCompat","storageId","createMMKV","id","encryptionKey","
|
|
1
|
+
{"version":3,"names":["MMKVModule","getAppGroupDirectory","ClixLogger","StorageService","constructor","projectId","storage","initializeCompat","getStoragePath","directory","undefined","error","warn","storageId","createMMKV","id","encryptionKey","path","mode","MMKV","Error","removeCompat","key","remove","delete","set","value","encoded","JSON","stringify","get","data","getString","decoded","parse","jsonError","debug","clear","clearAll","getAllKeys","keys","Array","from"],"sourceRoot":"../../../src","sources":["services/StorageService.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getAppGroupDirectory.d.ts","sourceRoot":"","sources":["../../../../src/native/getAppGroupDirectory.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,oBAAoB,QAAO,MAAM,GAAG,SA0BhD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StorageService.d.ts","sourceRoot":"","sources":["../../../../src/services/StorageService.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"StorageService.d.ts","sourceRoot":"","sources":["../../../../src/services/StorageService.ts"],"names":[],"mappings":"AAcA,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAe;gBAElB,SAAS,EAAE,MAAM;IAI7B,OAAO,CAAC,cAAc;IAsBtB,OAAO,CAAC,gBAAgB;IA0BxB;;OAEG;IACH,OAAO,CAAC,YAAY;IAWpB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAoBnC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAuBlC,MAAM,CAAC,GAAG,EAAE,MAAM;IAUlB,KAAK;IASL,UAAU;CASX"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clix-so/react-native-sdk",
|
|
3
|
-
"version": "0.0.2-beta.
|
|
3
|
+
"version": "0.0.2-beta.3",
|
|
4
4
|
"description": "Clix - Mobile push for builders",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"files": [
|
|
16
16
|
"src",
|
|
17
17
|
"lib",
|
|
18
|
+
"ios",
|
|
19
|
+
"clix-react-native-sdk.podspec",
|
|
18
20
|
"!**/__tests__",
|
|
19
21
|
"!**/__fixtures__",
|
|
20
22
|
"!**/__mocks__",
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { NativeModules, Platform } from 'react-native';
|
|
2
|
+
import { ClixLogger } from '../utils/logging/ClixLogger';
|
|
3
|
+
|
|
4
|
+
type ClixAppGroupDirectoryModule = {
|
|
5
|
+
getAppGroupDirectory(): string | null;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const MODULE_NAME = 'ClixAppGroupDirectory';
|
|
9
|
+
|
|
10
|
+
const resolveModule = (): ClixAppGroupDirectoryModule | undefined => {
|
|
11
|
+
const nativeModule = (NativeModules as Record<string, unknown>)[MODULE_NAME];
|
|
12
|
+
if (!nativeModule) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
return nativeModule as ClixAppGroupDirectoryModule;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const getAppGroupDirectory = (): string | undefined => {
|
|
19
|
+
if (Platform.OS !== 'ios') {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const nativeModule = resolveModule();
|
|
24
|
+
if (
|
|
25
|
+
!nativeModule ||
|
|
26
|
+
typeof nativeModule.getAppGroupDirectory !== 'function'
|
|
27
|
+
) {
|
|
28
|
+
ClixLogger.debug(
|
|
29
|
+
'App Group directory native module is unavailable. Falling back to default MMKV path.'
|
|
30
|
+
);
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const directory = nativeModule.getAppGroupDirectory();
|
|
36
|
+
if (typeof directory === 'string' && directory.length > 0) {
|
|
37
|
+
return directory;
|
|
38
|
+
}
|
|
39
|
+
} catch (error) {
|
|
40
|
+
ClixLogger.warn('Failed to resolve App Group directory', error);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return undefined;
|
|
44
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as MMKVModule from 'react-native-mmkv';
|
|
2
|
+
import { getAppGroupDirectory } from '../native/getAppGroupDirectory';
|
|
2
3
|
import { ClixLogger } from '../utils/logging/ClixLogger';
|
|
3
4
|
|
|
4
5
|
// Support both v2/v3 (MMKV class) and v4 (createMMKV function)
|
|
@@ -18,6 +19,28 @@ export class StorageService {
|
|
|
18
19
|
this.storage = this.initializeCompat(projectId);
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
private getStoragePath(): string | undefined {
|
|
23
|
+
try {
|
|
24
|
+
const directory = getAppGroupDirectory();
|
|
25
|
+
if (!directory) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* NOTE(nyanxyz):
|
|
31
|
+
* On iOS the mmkv directory is located at {App Group Directory}/mmkv.
|
|
32
|
+
* RN MMKV v2 used {App Group Directory}/mmkv, but v3 switched to {App Group Directory}.
|
|
33
|
+
* We need to keep the directory aligned so iOS and RN can share storage.
|
|
34
|
+
* Therefore we explicitly target the mmkv folder inside the App Group directory.
|
|
35
|
+
* @see https://akshayjadhav.hashnode.dev/how-to-access-react-native-mmkv-in-a-ios-widget-react-native-expo
|
|
36
|
+
*/
|
|
37
|
+
return `${directory}/mmkv`;
|
|
38
|
+
} catch (error) {
|
|
39
|
+
ClixLogger.warn('Failed to resolve App Group directory', error);
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
21
44
|
private initializeCompat(projectId: string) {
|
|
22
45
|
const storageId = `clix.${projectId}`;
|
|
23
46
|
|
|
@@ -26,6 +49,8 @@ export class StorageService {
|
|
|
26
49
|
return MMKVModule.createMMKV({
|
|
27
50
|
id: storageId,
|
|
28
51
|
encryptionKey: undefined, // Add encryption if needed
|
|
52
|
+
path: this.getStoragePath(),
|
|
53
|
+
mode: 'multi-process',
|
|
29
54
|
});
|
|
30
55
|
}
|
|
31
56
|
// v2/v3 API (MMKV class)
|
|
@@ -34,6 +59,8 @@ export class StorageService {
|
|
|
34
59
|
return new MMKV({
|
|
35
60
|
id: storageId,
|
|
36
61
|
encryptionKey: undefined, // Add encryption if needed
|
|
62
|
+
path: this.getStoragePath(),
|
|
63
|
+
mode: 'multi-process',
|
|
37
64
|
});
|
|
38
65
|
} else {
|
|
39
66
|
throw new Error('No compatible MMKV storage API found');
|