-
Notifications
You must be signed in to change notification settings - Fork 109
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
Draft: Add example and documentation for stand-alone apps with support for launching on vehicle client #158
base: master
Are you sure you want to change the base?
Conversation
Clone the original example as stand-alone-example in order to demonstrate launching an app using this package directly on the CarPlay-/AndroidAuto-client without having the app running on the phone. The stand-alone-example only renders a placeholder text on phone and contains all it's CarPlay-logic inside hooks which listen for - CarPlay connection changes - state changes (via useReducer) The stand-alone-example CarPlay-app features a TabTemplate as root template containing a top-level ListTemplate with two browsable items. Selecting a top-level item pushes a new (sub-level) ListTemplate onto the CarPlay-stack, which contains two non-browsable items. Selecting a sub-level item presents a CarPlay modal.
Do not create multiple bridges in PhoneScene. Reuse the AppDelegates rootView and window.
When launching the CarPlay scene directly on the CarPlay-client, create a bridge for the AppDelegate. Again check for an already existing bridge in order to not create multiple bridges: If the app is already running on the phone, a bridge will already be present on the AppDelegate. If not: create one for the CarPlay scene.
This adds and uses an alternative approach to initializing the app provided by @gavrichards: - Do not call RCTAppDelegate's application:didFinishLaunchingWithOptions but instead cherry-pick the code from RCTAppDelegate's application:didFinishLaunchingWithOptions except for window and rootViewController creation - move window and rootViewController creation to PhoneScene since they're not needed in stand-alone CarScene - call initAppFromScene() both in PhoneScene and CarScene to init the app. This approach works for both startup scenarios, both on Phone and on CarPlay-client. Bonus: The rootView property is stored in AppDelegate, so it can be used in PhoneScene (i.e. to pass to RNBootSplash if used) Caveat: The code in initAppFromScene() needs to be adjusted to the specific version of react native you are using! The version used in stand-alone-example is currently 0.71.13, so the code is taken from that versions RCTAppDelegate and converted to Swift (with a little help from ChatGPT for the C++ block, so no guarantee that it works! I'm not on RN's new architecture yet).
This is still a draft, since it covers only CarPlay for now. The Android part is simply a copy of the original example. For a while I thought I'd be fine with the solution from @mitchdowney over at Podverse outlined in this PR (still present but commented out as "Approach 1" in the new stand-alone-example app) but after a while I found that it produced unpredictable and buggy results in combinations of phone app running/not running or starting on CarPlay first, then on phone, then killing phone again, etc... Finally, after inspiration from @gavrichards ("Approach 2" in the new stand-alone-example app, initially outlined in this issue) and a lot of native debugging on the console of the physical device (since you don't have either Xcode nor React Native logs available when in CarPlay stand-alone mode!) I found this solution to work reliably in all circumstances / launch orders / lifecycle states. Thanks to @gavrichards for the groundwork around Feel free to comment and add improvements. |
External display connection invokes scene:willConnectTo:options again with the session.role .windowExternalDisplayNonInteractive: https://developer.apple.com/documentation/uikit/windows_and_screens/presenting_content_on_a_connected_display This needs to be explicitly handled or rejected, otherwise the app crashes with the error: Terminating app due to uncaught exception 'UIViewControllerHierarchyInconsistency', reason: 'A view can only be associated with at most one view controller at a time!"
@DanielKuhn For what it is worth, I tried re-opening the discussion about compatibility of RN with scenes here. |
Even this works & spawns the RN app but lots of apps have logic embeded inside components withing the App. So this is usefull only to show some "initial" termplate. |
I'm using this approach to render a "stand-alone" CarPlay-app. All logic for the app is encapsulated within a single component with hooks handling the CarPlay-events as outlined in the example I'm also controlling react-native-track-player via these hooks in my production app. |
@DanielKuhn Yes but this is not a real world example. In my case I use other player providers that are working only after render(). In this case I have to introduce |
It's a very real world example with a couple 100k users 😄 |
You are right about that app should not render anything. But I get weird results while app is started. |
@DanielKuhn hi, have you already tried to update to react-native 0.74? |
@DanielKuhn do you encounter app crash after re-connecting to carplay and clicking on any item that has onClick callback? |
@alex-vasylchenko I haven't updated to 0.74 yet, but as I mentioned on Discord: Every RN upgrade requires an adaptation of the @KestasVenslauskas I'm using version 2.3.0 still and I do observe blinking list elements occasionally, but only in the CarPlay Simulator, not on real devices. No crashes either. |
@DanielKuhn Did you manage to run the app from Android Auto directly (without running the app on mobile device first)? There is nothing but "RNCarPlay loading..." screen in such scenario. |
I'm not an Android developer, therefore I cannot say whether it is even possible to start a react native app in headless mode directly from Android Auto. Actually that's the main reason this pull request is still a draft. What we did in our production app was to require the "draw over apps"-permission in Android, so that the app can be started while the phone is actually locked. This way, while not headless like in iOS, the app can still handle all events coming from Android Auto. |
Has anyone applied these changes to RN 0.74.x-based project? |
I am also looking for an example of these changes for RN 0.74, due to the changes they've made to |
Has anyone experienced issues with iOS 17.6 when the app starts only when phone screen turns on? It's like it's blocked from initializing until user touches the phone... |
@gavrichards I tried recreating this method inside our custom AppDelegate but did not succeed. What works though is exposing I upgraded my standalone-example to RN 0.75 in this branch. It is based on this PR which upgrades the regular example to RN 0.75 first. Check out the call to Let me know what you think of this (ugly but working) solution. |
@DanielKuhn this is excellent, thank you so much for sharing your solution. I'm so glad there's someone else out there trying to do the same niche things I am, I'd be pretty stuck otherwise! One bit I'm struggling with when trying to replicate your solution is the introduction of "RCTColorSpaceUtils". Any idea what might be going on here? EDIT: oh - is that a 0.75 specific thing? I'm still only trying to update to 0.74 at the moment. |
@gavrichards yes, it was introduced in 0.75. The root view factory approach should work in both 0.74 and 0.75. |
I opened a PR requesting to expose |
@gavrichards The rn maintainers say we should instantiate a factory ourselves: facebook/react-native#46211 |
Still no luck running this on new arch? |
CarPlay and Android Auto apps are expected to be able to get launched without the user having to open the respective app on the phone first - or even worse: have the phone app running at all times in order to use the CarPlay-app properly.
The example iOS-app featured in apps/example does not support launching on the CarPlay-client directly and is heavily intertwined with the phone app by presenting each CarPlay-template via a corresponding screen in the phone app and navigating to each screen when pushing the template onto the CarPlay-stack.
Since a lot of people who are using this package are wondering how to start their CarPlay-scene without having the app running in the background, I think there should be an example which illustrates this workflow and documents what's happening behind the scenes (pun intended).
This PR is an approach to addressing this issue by adding a simple CarPlay-only example app which can either get launched with the phone app open or, more importantly, WITHOUT opening the app on phone first.