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

Trouble upgrading to 0.71 while using iOS Scenes #37278

Closed
gavrichards opened this issue May 5, 2023 · 10 comments
Closed

Trouble upgrading to 0.71 while using iOS Scenes #37278

gavrichards opened this issue May 5, 2023 · 10 comments
Labels
Needs: Triage 🔍 Platform: iOS iOS applications. Stale There has been a lack of activity on this issue and it may be closed soon. Type: Upgrade Issue Issues reported from upgrade issue form

Comments

@gavrichards
Copy link

New Version

0.71.7

Old Version

0.70.8

Build Target(s)

iOS on iPhone in local development

Output of react-native info

System:
    OS: macOS 13.3.1
    CPU: (10) arm64 Apple M1 Pro
    Memory: 680.41 MB / 32.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 16.17.0 - ~/.nvm/versions/node/v16.17.0/bin/node
    Yarn: 1.22.19 - ~/.nvm/versions/node/v16.17.0/bin/yarn
    npm: 9.4.1 - ~/.nvm/versions/node/v16.17.0/bin/npm
    Watchman: Not Found
  Managers:
    CocoaPods: 1.12.0 - /Users/gav/.gem/ruby/2.7.2/bin/pod
  SDKs:
    iOS SDK:
      Platforms: DriverKit 22.4, iOS 16.4, macOS 13.3, tvOS 16.4, watchOS 9.4
    Android SDK:
      API Levels: 29, 30, 31, 33
      Build Tools: 28.0.3, 30.0.2, 30.0.3, 31.0.0
      System Images: android-28 | Google APIs ARM 64 v8a, android-29 | Intel x86 Atom_64, android-31 | Google APIs ARM 64 v8a
      Android NDK: Not Found
  IDEs:
    Android Studio: Flamingo 2022.2.1 Patch 1 Flamingo 2022.2.1 Patch 1
    Xcode: 14.3/14E222b - /usr/bin/xcodebuild
  Languages:
    Java: 11.0.16.1 - /usr/bin/javac
  npmPackages:
    @react-native-community/cli: Not Found
    react: 18.2.0 => 18.2.0 
    react-native: 0.71.7 => 0.71.7 
    react-native-macos: Not Found
  npmGlobalPackages:
    *react-native*: Not Found

Issue and Reproduction Steps

I am attempting to upgrade React Native in my application from 0.70.x to 0.71.x.
My app uses iOS Scenes, so that it can support CarPlay.
Therefore I have previously converted AppDelegate to Swift and introduced PhoneScene and CarScene delegates.
My app also has to handle receiving a URL from a push notification, and being launched via Siri Shortcuts with metadata.

For all of this to work, I had to move the setup of RCTBridge and RCTRootView to PhoneScene and CarScene (plus rootViewController and window to PhoneScene).
This is because with Scenes launchOptions in AppDelegate is always empty.
But traditionally that's where useful properties would be held from how the app was launched via a notification or Siri Shortcut.
With Scenes, that data comes via connectionOptions in each Scene delegate. But RCTBridge expects to receive launchOptions, and they differ in format.
Therefore I had to write something to convert launchOptions into connectionOptions, to pass to the bridge.
Therefore the bridge and root view both have to be initiated in the Scene delegate, not AppDelegate.
If those are initiated there, then rootViewController and window have to be too, as they use the bridge/root view.
CarScene also therefore has to do the same as PhoneScene, just without initiating rootViewController and window, as they're not required for CarPlay.

My app also uses react-native-bootsplash, and that has to be initiated after the rootView has been initiated, so therefore that has to happen in PhoneScene.

Here's the problem.
With React Native 0.71, the initiation of RCTBridge, RCTRootView, rootViewController etc is all abstracted away to RCTAppDelegate, so there's no where for us to intervene and do what we’re doing for Scenes, passing launch/connectionOptions and initiating BootSplash.

With everything I've tried so far, I can get the app to build and launch on my device, but I just get a full black screen and can't get any further.

I would appreciate if anyone has any suggestions. I surely can't be the only developer who is using Scenes in their app and wants to use the latest version of React Native.
Thanks!

What follows is how my AppDelegate and PhoneScene look before attempting the upgrade. This all works well.

AppDelegate

import UIKit
import CarPlay
import React

// Added for react-native-google-cast
import GoogleCast

// Added for react-native-firebase
import Firebase

private let kRNConcurrentRoot = "concurrentRoot"

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, RCTBridgeDelegate, UNUserNotificationCenterDelegate {

  var window: UIWindow?
  var bridge: RCTBridge?;
  var rootView: RCTRootView?;

  static var shared: AppDelegate { return UIApplication.shared.delegate as! AppDelegate }

  func sourceURL(for bridge: RCTBridge!) -> URL! {
    #if DEBUG
    return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index");
    #else
    return Bundle.main.url(forResource:"main", withExtension:"jsbundle")
    #endif
  }

  // Called when the app first starts, but with Scenes, launchOptions is now always empty
  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Added for react-native-firebase
    FirebaseApp.configure()

    // Added for react-native-google-cast
    let receiverAppID = kGCKDefaultMediaReceiverApplicationID // or "ABCD1234"
    let criteria = GCKDiscoveryCriteria(applicationID: receiverAppID)
    let options = GCKCastOptions(discoveryCriteria: criteria)
    GCKCastContext.setSharedInstanceWith(options)

    // Added for react-native-push-notification
    // Define UNUserNotificationCenter
    let center = UNUserNotificationCenter.current()
    center.delegate = self

    return true
  }

  // Called when a new scene joins. Also called during app startup on CarPlay.
  func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
    if (connectingSceneSession.role == UISceneSession.Role.carTemplateApplication) {
      let scene =  UISceneConfiguration(name: "CarPlay", sessionRole: connectingSceneSession.role)
      scene.delegateClass = CarSceneDelegate.self
      return scene
    } else {
      let scene =  UISceneConfiguration(name: "Phone", sessionRole: connectingSceneSession.role)
      scene.delegateClass = PhoneSceneDelegate.self
      return scene
    }
  }

  func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
  }

  // I've removed all of the event handlers here for react-native-push-notification to keep this concise and relevant

  /// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
  ///
  /// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
  /// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
  /// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`.
  func concurrentRootEnabled() -> Bool {
      // Switch this bool to turn on and off the concurrent root
      return true
  }

  func prepareInitialProps() -> [AnyHashable : Any]? {
      var initProps: [AnyHashable : Any] = [:]
      #if RCT_NEW_ARCH_ENABLED
      initProps[kRNConcurrentRoot] = NSNumber(value: concurrentRootEnabled())
      #endif
      return initProps
  }
}

PhoneScene

import Foundation
import UIKit
import SwiftUI

class PhoneSceneDelegate: UIResponder, UIWindowSceneDelegate {
  var window: UIWindow?
  func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    guard let appDelegate = (UIApplication.shared.delegate as? AppDelegate) else { return }
    guard let windowScene = (scene as? UIWindowScene) else { return }

    if (appDelegate.bridge == nil) {
      let initProps = appDelegate.prepareInitialProps()

      // This uses react-native-siri-shortcut's convenience initialiser for handling converting connectionOptions to launchOptions
      // https://github.com/Gustash/react-native-siri-shortcut/commit/790635878e3f011c75a0672c6a91ce999eb4a410
      let bridge = RCTBridge(delegate: appDelegate, connectionOptions: connectionOptions)
      let rootView = RCTRootView(bridge: bridge, moduleName: "aiirmobile", initialProperties: initProps)
      rootView.backgroundColor = UIColor.systemBackground

      appDelegate.bridge = bridge
      appDelegate.rootView = rootView
    }

    let rootViewController = UIViewController()
    rootViewController.view = appDelegate.rootView;

    let window = UIWindow(windowScene: windowScene)
    window.rootViewController = rootViewController
    self.window = window
    window.makeKeyAndVisible()

    // Added for react-native-bootsplash
    // Help for using Swift: https://github.com/zoontek/react-native-bootsplash/issues/245
    RNBootSplash.initWithStoryboard("Launch Screen", rootView: appDelegate.rootView!) // <- initialization using the storyboard file name
  }

  func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    RNSSSiriShortcuts.scene(scene, continue: userActivity)
  }
}
@DanielKuhn
Copy link

Hi @gavrichards - while adding CarPlay-support to my react native app (with push notifications support) I am now stuck at exactly this point. Did you manage to find a solution yet?

@alex-vasylchenko
Copy link

Hi, I have the same problem. After adding react-native-carplay, I can't get the launchOptions, so I can't find out exactly how the app was opened when using deepLink. I get userActivity in PhoneScene, but I don't know how to make this data reach the react application

@DanielKuhn
Copy link

DanielKuhn commented Nov 16, 2023

@alex-vasylchenko check out @gavrichards implementation in birkir/react-native-carplay#132 (comment)
He converts the connectionOptions to launchOptions which seems to work

@DanielKuhn
Copy link

DanielKuhn commented Nov 21, 2023

After fiddling around with this topic for quite some time now, always in doubt of what's happening under the hood and seeing ever more questions around starting on CarPlay without having the app running on phone, I took the liberty to create (and document!) an example app which runs independently of the phone app and supports launching on CarPlay directly (without having the phone app running) in this PR: birkir/react-native-carplay#158
Patches welcome, feel free to add comments and improvements.

@KestasVenslauskas
Copy link

@DanielKuhn My problem with carplay & scenes is that my RootComponent is rendered twice with scenes and navigation complays about linking setup called twice. Maybe you know how to avoid that?

@DanielKuhn
Copy link

@KestasVenslauskas I poured all the knowledge I gained into the stand-alone-example, especially into the README of the stand-alone-example.
The RootComponent being rendered twice sounds like your CarScene is still creating a second RootViewController. Avoid this by following the setup outlined in the README (adjusted to the React Native version you're using).

@KestasVenslauskas
Copy link

@DanielKuhn Thank you so much! I just needed additional fix for handling deep links but the fix is mentioned here #35191 (comment)

@react-native-bot
Copy link
Collaborator

This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.

1 similar comment
@react-native-bot
Copy link
Collaborator

This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.

@react-native-bot react-native-bot added Stale There has been a lack of activity on this issue and it may be closed soon. labels Sep 5, 2024
@react-native-bot
Copy link
Collaborator

This issue was closed because it has been stalled for 7 days with no activity.

@react-native-bot react-native-bot closed this as not planned Won't fix, can't repro, duplicate, stale Sep 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs: Triage 🔍 Platform: iOS iOS applications. Stale There has been a lack of activity on this issue and it may be closed soon. Type: Upgrade Issue Issues reported from upgrade issue form
Projects
None yet
Development

No branches or pull requests

5 participants