alinz / react-native-share-extension

react-native as an engine to drive share extension
MIT License
763 stars 398 forks source link

Share extension app can not run twice in one app(ios). #100

Open EnochL opened 6 years ago

EnochL commented 6 years ago

In IOS, the share extension cannot run twice, because it does not exit in the close method.

RCT_EXPORT_METHOD(close) {
    [extensionContext completeRequestReturningItems:nil
                                  completionHandler:nil];
}

What should i do if i wanna run it twice in one app? Would it be possible to append "exit(0);" to the method?

AndrewHenderson commented 6 years ago

@EnochL Please elaborate on what you mean by "run twice in one app." Perhaps you can explain in further detail your use case.

daviswhitehead commented 6 years ago

I'm seeing the same thing. @EnochL correct me if I'm wrong, what I think you mean by run twice is

Here are what my device logs look like for two of associated crashes 1) on exit and 2) on launch image image

Any ideas for how to resolve this bug?

EnochL commented 6 years ago

@AndrewHenderson i mean "run again".

EnochL commented 6 years ago

here is a way to fix it.

RCT_EXPORT_METHOD(close) {
    [extensionContext completeRequestReturningItems:nil
                                  completionHandler:nil];
}

change to

RCT_EXPORT_METHOD(close) {
    [extensionContext completeRequestReturningItems:nil
                                  completionHandler:nil];
    exit(0);
}
EnochL commented 6 years ago

creating a native module to execute β€œexit(0);” is another way to fix it without changing the code of "react-native-share-extension"

AndrewHenderson commented 6 years ago

@EnochL Nice fix!

I only began experiencing this bug after upgrading my iPhone to iOS 11.3.1. Everything was working properly on 11.3.

Submit a PR! πŸ˜„

AndrewHenderson commented 6 years ago

@EnochL I'm finding that this sometimes crashes my Photos app. I receive a black screen on the simulators and then can't reopen the Photos app. @alinz mentioned that this is a "hard exit and could cause a memory leak."

AndrewHenderson commented 6 years ago

@alinz Everything I've found tells me that the share extension is being closed properly without @EnochL's suggested change.

My current hypothesis is that the React app is not being properly unmounted. So when a user attempts to reopen the Share Extension, the React app crashes, not the extension itself.

Currently exploring this idea.

AndrewHenderson commented 6 years ago

I just noticed that everything is working properly for me in Release Mode. It's only in Debug Mode that the app crashes on reopen in iOS 11.3.1.

It seems like the index.js is called from the Metro Bundler a second time and then the React Share App crashes.

If everything works properly in Release Mode, I see that as a very minor issue.

damir-sirola commented 6 years ago

Be careful about using exit(0); in your code. It was causing the extension to close before the JavaScript code was run but only when the app was archived.

mtzfactory commented 6 years ago

Hi, I'm experiencing the same behaviour... at this moment only in the simulator. Any solution?

i25878427 commented 5 years ago

I am experience this issue on a real device, any idea how to solve this? @AndrewHenderson

AndrewHenderson commented 5 years ago

I still believe it is due to the JS module needing to be unmounted on the JS thread. I believe the cause of the crash is due to the Native Module attempting to remount the JS module, the RN app, second time. After the RN app crashes, the user is able to launch on the third attempt.

I'm researching how this can be done. I'm not an Objective-C developer, so anyone with more experience, please chime in.

i25878427 commented 5 years ago

@AndrewHenderson This my temporary workaround for the problem:

I am using react-native-exit-app which provides me a bridging to "exit(0)" and I used in

componentWillUnmount(){ RNExitApp.exitApp(); }

and not as addition to the Close method which was causing my js to end before time.

It is working for me, maybe it is not the best approach but I am ok with it for the moment.

AndrewHenderson commented 5 years ago

@i25878427 You're saying the following did not work for you?

componentWillUnmount(){
  ShareExtension.close();
}
i25878427 commented 5 years ago

This is working for me:

componentWillUnmount(){ ShareExtension.close(); }

This is not:

RCT_EXPORT_METHOD(close) { [extensionContext completeRequestReturningItems:nil completionHandler:nil]; exit(0); }

AndrewHenderson commented 5 years ago

@i25878427 This works for me:

RCT_EXPORT_METHOD(close) {
  [extensionContext completeRequestReturningItems:nil
    completionHandler:nil];
  exit(0);
}

When I run my share extension directly from Xcode, the debug logs show that the rootTag for my React Native share extension is 1. This is what we want.

When I close the extension and reopen it, the root tag is 11. This is NOT what we want. It tells me that the JS Thread was not killed and the first version of the app is still in memory β€” that React's bookkeeper has the app on file.

2018-10-23 17:18:00.216 [info][tid:main][RCTRootView.m:293] Running application ShareToSnap ({
    initialProps =     {
    };
    rootTag = 11;
})

screen shot 2018-10-23 at 5 35 43 pm

When I adding exit(0), no matter how many times I close and reopen the extension, the rootTag is always 1 β€” this is what we want, since it is the root of the app.

Theory

When the code is run to initialize the app a second time, it's attempting to register using a name that has already been used. Therefore, the app is unable to mount properly and the app crashes.

Sidenote

According to Apple's Docs on Share Extensions, we shouldn't need exit(0). There is no mention of it.

@alinz Maybe you can shed some light on this thread.

AndrewHenderson commented 5 years ago

@isaachinman Have you experienced this bug? It seems like a noteworthy issue.

isaachinman commented 5 years ago

@AndrewHenderson Sorry for the delayed response - I have not seen this bug. My iOS apps are working as expected and can be launched as many times as desired.

rbscott commented 5 years ago

I have been playing around with ReactNativeShareExtension and I think for large apps the second launch might be getting killed by a memory leak. For me, I was running the app repeatedly and each time it would consume more memory (just watched the number in XCode). I fixed this by creating a global RCTBridge and reusing it in the call the shareView. Inside close, I set self.view to nil. I was thinking about putting together a PR, but then I realized it would be a breaking change, and wasn't sure how to get around it. This also fixes #64.

This does break reloading code between launches, to fix that use

[sharedBridge invalidate];
sharedBridge = nil;

Inside of close. This seems to fix the memory leak and support reloading the app.

AndrewHenderson commented 5 years ago

@rbscott Thanks for looking into this issue. In what way would this be a breaking change. Does it change the module's API?

rbscott commented 5 years ago

@AndrewHenderson In order to fix it you have to reuse the sharedBridge, so either the API for shareView passes in a new parameter that is used, or the user of the library must follow the convention to reuse a global RCTBridge. I guess, another approach is to add a class method that defined a shared bridge? What do you think about that approach?

RZulfikri commented 4 years ago

@AndrewHenderson @rbscott Thanks, your solution works for me, but I have my own approach.

  1. I'm using share bridge
  2. I call RNExitApp.exitApp() (from react-native-exit-app) right after call ShareExtension.close()

That solution works for me.

EricWiener commented 4 years ago

Thanks @RZulfikri that worked perfectly for me