alinz/react-native-share-extension

using local data (redux-persist / asyncStorage)

Closed this issue · 26 comments

Hello, thank for this awesome library. Currently, I'm working with this sharing feature. But I'm facing one issue, Is there any way to access current redux/async storage from the extension?

thanks

Hi @RZulfikri, I'm also working on an extension which has to integrate with redux (and redux-persist), on iOS you need to have an App Group for sharing persisted data, because essentially the extension is a separate app, with it's own container. If you are using redux-persist with AsyncStorage, you need to swap out AsyncStorage for react-native-fs or some other solution.

@vujevits Thank you for your response, I'm actually facing that issue "separate app, with its own container" issue. That's new for me, we can use react-native-fs to as storage in redux persist?
I'm planning to use this https://github.com/react-native-community/async-storage/blob/LEGACY/docs/advanced/BrownfieldIntegration.md

@RZulfikri https://github.com/leethree/redux-persist-fs-storage is the engine to integrate react-native-fs with redux-persist. Here you can see the code I'm working on at the moment, it's fairly complex because it also implements the migration code from the old persisted store to the storage for the App Group:
https://github.com/felfele/felfele/blob/369-share-to-felfele/src/store/index.ts#L30
Hope it helps.

@vujevits I'll try. Thank you so much.

Update:
I solve this by updating react-native async-storage to handle app group location. Check my branch https://github.com/RZulfikri/async-storage/tree/add/app-group

how to use :

  1. add this line in your AppDelegte.m
#import <RNCAsyncStorage/RNCAsyncStorage.h>

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
   ...
   [[RNCAsyncStorage alloc] initWithGroup: @"your.app.group];
   ...
}
  1. add this line in your MyShareEx.m
#import <RNCAsyncStorage/RNCAsyncStorage.h>

- (UIView*) shareView {
   ...
   [[RNCAsyncStorage alloc] initWithGroup: @"group.medplanner.io"];
   ...
}

@RZulfikri Thanks for posting your solution. I got it working very quickly. Since it's been a few months, do you have any thoughts on solutions that offer longterm stability? i.e. a path forward that isn't a fork of the async-storage repo.

@braincore I'm glad to hear that. Currently, I have no plan for a long term solution since it working, I just hoping that async storage team aware of that app group request.

@RZulfikri @braincore Hi there! Really appreciate you sharing your solution here. Have you guys found any solution that isn't a fork of async-storage? Tried to find information on this within the async storage repo but wasn't successful...

@yannikw23 I not sure yet, I hope that will available in future version react-native-async-storage/async-storage#126. I'm not sure it's a good idea to release my own solution in NPM since it was from async-storage repo and they plan to add the app group too. Or you can do with the first solution, using react-native-fs

@RZulfikri Hey, thanks a lot for your fast response! Yeah, I guess I'll have to find an interim solution now. I am also hoping for async-storage's new release.
In the meantime I'm gonna try the react-native-fs solution. Thank you!

@RZulfikri Hi there! Sorry to bother you again...

I tried to implement your modified AsyncStorarge solution. Unfortunately although I set up an appgroup and added 'initWithGroup', the share extension and the main app don't appear to use the same storage space. The share extension's storage persists whatever I save there just fine. However, the main app does not appear to access the same peristed redux state as it only shows an empty array for the property I am saving content to in the extension.

I implemented it exactly as you did. What am I missing?
Appreciate any kind of advice!

@yannikw23 hi, have you add app group capabilities in your excode? And also make sure that you use the correct branch.

@RZulfikri Yep. Added the group capabilities to both the app and the extension and reconstructed your changes to the AsyncStorage repo in my own fork.
Any news on your plans to publish your implementation on npm? :)

[edited]
@yannikw23 have you follow this part #166 (comment) also with the files?

try to log the storage directory path. here https://github.com/yannikw23/async-storage/blob/LEGACY/ios/RNCAsyncStorage.m#L95 and check the appgroup path, check path in main app and extension.

@RZulfikri Man, thanks so much for helping me!

yes, I have followed everything precisely. however, I forked the current AsnycStorage repo and copied your changes in your fork.

when logging the storage path, it's null in the main app...
image

I have two different entry points to my application. One specifically for the main app and one for the extension. Both reference the same persisted persistedStorage

@RZulfikri Ok, I noticed something: As I forked a later version of AsyncStorage than you, they use a new RCTCreateStorageDirectoryPath method. I was checking for the AppGroup in the old one, which is now only used for migration: https://github.com/yannikw23/async-storage/blob/046465eb983a4c30d88ff440cdf4da48e36b248c/ios/RNCAsyncStorage.m#L86. So, I should rather use this new one: https://github.com/yannikw23/async-storage/blob/046465eb983a4c30d88ff440cdf4da48e36b248c/ios/RNCAsyncStorage.m#L102

Now. If I implement your logic like this

static NSString *RCTCreateStorageDirectoryPath(NSString *storageDir) {
  // We should use the "Application Support/[bundleID]" folder for persistent data storage that's hidden from users
  
  NSString *storageDirectoryPath;
   if (!AppGroupName) {
          storageDirectoryPath = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES).firstObject;
          storageDirectoryPath = [storageDirectoryPath stringByAppendingPathComponent:[[NSBundle mainBundle] bundleIdentifier]]; // Per Apple's docs, all app content in Application Support must be within a subdirectory of the app's bundle identifier
          storageDirectoryPath = [storageDirectoryPath stringByAppendingPathComponent:storageDir];
     } else {
         NSURL * pathUrl = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier: AppGroupName];
         storageDirectoryPath =  pathUrl.path;
         NSLog(@"PATH storageDirectoryPath %@", storageDirectoryPath);
     }
  return storageDirectoryPath;
}

I receive the following error:

'Error storing data', { [Error: Failed to write manifest file.Error Domain=NSCocoaErrorDomain Code=514 "The item couldn’t be saved because the file name is invalid."]
  key: undefined,
  line: 110513,
  column: 24,
  sourceURL: 'http://localhost:8081/index.bundle?platform=ios&dev=true&minify=false' }

and the storageDirectoryPath is still null

[edited]
Screenshot 2020-04-20 at 20 44 58

I try to check my branch, and it seems working, if you used correct app group name will look like this once you log it. You can see the path .../Shared/AppGroup/...

Screenshot 2020-04-20 at 20 35 46

oh ya I already add log by default in my branch
NSLog(@"PATH storageDirectoryPath %@", storageDirectoryPath);

then check your xcode log, did you see the log or not.

and also try to install my branch directly
yarn add https://github.com/RZulfikri/async-storage#add/app-group

I am getting a correct path and app group name now:

2020-04-20 15:46:08.249638+0200 xxx[36384:899663] AppGroupName group.xxx-io
2020-04-20 15:46:08.249886+0200 xxx[36384:899663] PATH storageDirectoryPath /Users/yannik/Library/Developer/CoreSimulator/Devices/03940147-EA46-45EB-A457-3DA873105E36/data/Containers/Shared/AppGroup/88F03B7A-6AE9-485D-99C1-72719A2C3CCB/org.xxx-io/RCTAsyncLocalStorage_V1/manifest.json

Now, when I launch the app, everything is fine. If I save something in the extension and go back to the main app, I receive this error:

Error storing data', { [Error: Failed to write manifest file.Error Domain=NSCocoaErrorDomain Code=4 "The folder “manifest.json” doesn’t exist." UserInfo=
{NSFilePath=/Users/yannik/Library/Developer/CoreSimulator/Devices/03940147-EA46-45EB-A457-3DA873105E36/data/Containers/Shared/AppGroup/88F03B7A-6AE9-485D-99C1-72719A2C3CCB/org.xxx-io/RCTAsyncLocalStorage_V1/manifest.json, NSUserStringVariant=Folder, NSUnderlyingError=0x600002cfef70 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}]

try my branch version directly
your fork seems using version 1.8.1 and my fork using 1.6.1. It seems because of different version implementation. I haven't checked the implementation using the newer version.

IT WORKS!!! This is the function now, for anyone trying to reconstruct @RZulfikri's solution with version v1.8.1 of AsyncStorage:

static NSString *RCTCreateStorageDirectoryPath(NSString *storageDir) {
  // We should use the "Application Support/[bundleID]" folder for persistent data storage that's hidden from users
  
  NSString *storageDirectoryPath;
   if (!AppGroupName) {
          storageDirectoryPath = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES).firstObject;
          storageDirectoryPath = [storageDirectoryPath stringByAppendingPathComponent:[[NSBundle mainBundle] bundleIdentifier]]; // Per Apple's docs, all app content in Application Support must be within a subdirectory of the app's bundle identifier
          storageDirectoryPath = [storageDirectoryPath stringByAppendingPathComponent:storageDir];
     } else {
         NSLog(@"AppGroupName %@", AppGroupName);
         NSURL * pathUrl = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier: AppGroupName];
         storageDirectoryPath =  pathUrl.path;
          storageDirectoryPath = [storageDirectoryPath stringByAppendingPathComponent:storageDir];
         NSLog(@"storageDirectoryPath: %@ appGrroupname: %@", storageDirectoryPath, AppGroupName);
     }
  return storageDirectoryPath;
}

Thank you so much @RZulfikri!! You are an absolute legend!

great!!!, glad to help.

Why was this issue closed? It's still a massive issue...

@ortonomy it's a limitation of AsyncStorage, which can be swapped out for other implementations, like react-native-fs, with a support for app groups.

@RZulfikri your solution cused error in tow files

AppDelegate.m

MyExternalShare.m

No visible @interface for asyncstorage react native

did you know solution for this ?
thnx

@anasalsaadi14 Related to this, I like to advise you to use MMKV storage.

  1. try to find react-native-mmkv there are some component of it (https://github.com/ammarahm-ed/react-native-mmkv-storage, https://github.com/mrousavy/react-native-mmkv)
  2. check native MMKV storage github (https://github.com/Tencent/MMKV), you can find the native MMKVExtension module (https://github.com/Tencent/MMKV/wiki/iOS_tutorial#configuration), just implement that, get value natively and pass it to react-native.

if you implement it like this is much faster than using async storage. hope it helps.

Thanks.