react-native-community/discussions-and-proposals

Support Project Catalyst (running iPad apps on macOS)

brunolemos opened this issue Β· 46 comments

Apple has just announced Project Catalyst on WWDC 2019, which allows iOS/iPad apps to run on macOS without modification. This is AWESOME.

We'd love if React Native could build and work correctly on macOS. That should be a supported scenario.

Beta is already available today for developers to test.
EDIT: Stable version of macOS Catalina is already available.

https://techcrunch.com/2019/06/03/ios-apps-will-run-on-macos-with-project-catalyst/

image

Not sure if should open an issue here or on react-native: facebook/react-native#25133

Didn't they also announce iPadOS? How does this affect react-native now that iOS and IPadOS might be two separate targets

Ok, so let's keep the discussion here, it fits better the format.

Few points (which ofc take with a pinch of salt):

  • I feel that the likely outcome is that it will be something closer to react-native-tvos in terms of implementation than it being integrated in the main repo. (same answer for the new ipados)

  • as you mentioned, they literally announced this less than 24hrs ago so I don't see this happening for a while

  • this going into the main repo is mostly a decision that needs to happen internally at Facebook, because they need to evaluate if it fits with their needs first (hence why my first point) - so cc'ing @TheSavior @cpojer @fkgozali so that they can give some insight on their POV

  • Just for reference, there was already some experimentation from @wbroek https://twitter.com/wbroek/status/1102361241811599365?s=20 and similarly, this is what MS has been doing for their macos implementation: https://twitter.com/LinguaBrowse/status/1102504112552374273

I mean.. there is no Facebook.app for the mac right?? And it would be nice to reuse the iOS code, right? wink wink? :p

That it can work I and some more people demonstrated but took some adjustments in the core of React Native; mostly deprecated API's because you must target the latest iOS/Mac.
So fully agree with @kelset that Facebook as to have a need because of the core changes; although with the Lean initiative a lot of old balast will be removed.

Planned to make a POC in the coming weeks with 0.60 to see what is working and needed change

Ya I'm looking for docs and experience on this. How automatic is such "porting" from iPad to Mac?

Relevant, important links while I was researching this:

I disagree about treating ipadOS support in RN like tvOS. The latter is very different, while ipadOS is for all intents and purposes of app makers, the same as iOS 13. Dimensions window support for Split View and Slide Over on iPad in #16152 link above means afaik, I got what I need to fully convert my iPhone RN app to support the iPad's screen. Add splitView so my 1 column iPhone layout become 2 columns, then it should be ready for the new Catalyst conversion for macOS.

Apple's new HIG docs for iPad apps on Mac says explicitly that splitView on iPad will get automatic conversion to equivalent on macOS.

So this is my own plan for bringing my iPhone app (WonderSwipe) to iPad and Mac. Am I missing anything that needs RN core support to make happen, or am I right that all the pieces are already in place as I've outlined above? RNN provides the splitView, while Xcode 11 provides not just iPad layout but also a new Mac option you simply turn on. Will share hiccups I get along the way.

Anyone else thinking/planning something similar, on iPad/Mac adaptations of your RN apps? What else do you see missing inside RN that's needed for these adaptations?

radex commented

I feel that the likely outcome is that it will be something closer to react-native-tvos in terms of implementation than it being integrated in the main repo. (same answer for the new ipados)

I strongly disagree.

tvOS is treated by Apple as a separate OS, with most things in common with iOS and UIKit, but a separate target nonetheless.

iPadOS is only a marketing term. It's not really a separate OS, from a technical standpoint.

UIKit for Mac / Marzipan / Catalyst is a weird beast, but the way Apple treats it, Mac is only a separate device target for the iOS app. It's not listed as a separate OS in code (all platform checks reference iOS, not macOS), and you're not really supposed to create a separate Xcode target for Catalyst apps (the way you are for tvOS or watchOS -- or truly native/AppKit Mac apps), just check the checkbox, add necessary configuration for macOS, and if necessary -- add extra target environment checks in code to ifdef APIs not available on macOS/outside of macOS.

Therefore, the core of RN for Catalyst apps should remain the same, and in the same repo. A react-native-community repo(s) might (and should) spring up for enhancing Catalyst apps (supporting extra features only available for the Mac).

πŸ‘‹ Radek - yeah, again, my was literally just a feeling because I wasn't really able to understand what iPad OS really was back then.

Upon further investigation, and your comment, I can rollback that 100%, yeah it's basically still iOS13 from our perspective πŸ€—

radex commented

Heads up: I began initial for for supporting Catalyst in React Native. More work needs to be done to polish everything to look good, but in general it was an hour or two of work to get it to compile properly:

facebook/react-native#25427

@radex will your commit make it to rn 61? do you think real world projects will build successfully for mac?

radex commented

@brunolemos I think it will make it to RN 61, but it's only the first step -- web socket doesn't work which is annoying for development, the layout and fonts are all broken… You should help out and contribute too :)

Any news about this? Catalina is going to be released this month, will it bring anything useful in scope of this topic?

Tried to build for mac (react-native 0.61.2).

Had build errors related to firebase (invertase/react-native-firebase#2698, firebase/firebase-ios-sdk#3144). Then I removed firebase.

Got this error:

Undefined symbols for architecture x86_64:
  "_OBJC_CLASS_$_RCTSRWebSocket", referenced from:
      l_OBJC_$_CATEGORY_RCTSRWebSocket_$_React in libReact-Core.a(RCTWebSocketModule.o)

Removed the websocket pod but no change. How to pass through this error?

facebook/react-native#25427 did most of the work for apps not using RCTWebSocket, but more help are still needed for other apps like mine. πŸ˜”

radex commented

@brunolemos RCTWebSocket didn't compile for Catalyst, so I ifdef'ed it out of React Native build. If you manage to get to compile, send a PR to react-native repo

@radex can you share more how you managed to do it?

Which just landed in RN 0.61.2

@fungilation cool, is there any information about it somewhere?

@radex i tried to run my application with react-native 0.61.2. but i still get the error described by @brunolemos

Undefined symbols for architecture x86_64:
  "_OBJC_CLASS_$_RCTSRWebSocket", referenced from:
      l_OBJC_$_CATEGORY_RCTSRWebSocket_$_React in libReact-Core.a(RCTWebSocketModule.o)
      objc-class-ref in libReact-Core.a(RCTWebSocketModule.o)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I did the steps you mentioned in the Pull Request.
The last step to change the platforms for libRCTWebSocket.a to iOS was not possible.
The lib was not showing up in my setup.

Can this be the problem?

With my app, while I can build on RN 0.61.2 with the new Mac target checked off in Xcode 11.1. Running the app would crash with this in log:

2019-10-13 01:02:39.076931-0700 wonderswipe[1815:480708]  - <AppMeasurement>[I-ACS036002] Analytics screen reporting is enabled. Call +[FIRAnalytics setScreenName:setScreenClass:] to set the screen name or override the default screen class name. To disable screen reporting, set the flag FirebaseScreenReportingEnabled to NO (boolean) in the Info.plist
2019-10-13 01:02:41.009396-0700 wonderswipe[1815:480508] 
[CodePush] Loading JS bundle from file:///private/var/containers/Bundle/Application/1C135C34-D15C-443E-9D27-D5746CBB3A2F/wonderswipe.app/main.jsbundle
2019-10-13 01:02:41.014 [info][tid:main][RCTRootView.m:293] Running application wonderswipe ({
    initialProps =     {
    };
    rootTag = 1;
})
2019-10-13 01:02:41.540029-0700 wonderswipe[1815:480508] [Crashlytics] Version 3.14.0 (144)
2019-10-13 01:02:41.718366-0700 wonderswipe[1815:480733] 6.5.0 - [Firebase/Analytics][I-ACS023007] Analytics v.60004000 started
2019-10-13 01:02:41.718953-0700 wonderswipe[1815:480733] 6.5.0 - [Firebase/Analytics][I-ACS023008] To enable debug logging set the following application argument: -FIRAnalyticsDebugEnabled (see http://goo.gl/RfcP7r)
2019-10-13 01:02:41.738126-0700 wonderswipe[1815:480734] 6.5.0 - [Firebase/Messaging][I-FCM001000] FIRMessaging Remote Notifications proxy enabled, will swizzle remote notification receiver handlers. If you'd prefer to manually integrate Firebase Messaging, add "FirebaseAppDelegateProxyEnabled" to your Info.plist, and set it to NO. Follow the instructions at:
https://firebase.google.com/docs/cloud-messaging/ios/client#method_swizzling_in_firebase_messaging
to ensure proper integration.
2019-10-13 01:02:41.760808-0700 wonderswipe[1815:480725] [Assert] Unsupported use of UIKit API off the main thread: UIAccessibilityIsAssistiveTouchRunning()
2019-10-13 01:02:41.764113-0700 wonderswipe[1815:480725] [Assert] Unsupported use of UIKit API off the main thread: UIAccessibilityIsGuidedAccessEnabled()
2019-10-13 01:02:42.549130-0700 wonderswipe[1815:480719] Creating client/daemon connection: 85A15761-0B94-4B20-9A18-85AF1F9085F0
2019-10-13 01:02:42.752213-0700 wonderswipe[1815:480719] Got the query meta data reply for: com.apple.MobileAsset.MacinTalkVoiceAssets, response: 0
2019-10-13 01:02:42.756586-0700 wonderswipe[1815:480719] Consumed extension
2019-10-13 01:02:42.804277-0700 wonderswipe[1815:480719] Got the query meta data reply for: com.apple.MobileAsset.MacinTalkVoiceAssets, response: 0
2019-10-13 01:02:43.332164-0700 wonderswipe[1815:480722] [Fabric] failed to download settings Error Domain=FABNetworkError Code=-5 "(null)" UserInfo={status_code=403, type=2, request_id=369edc1957b9d5ae63919d6fc910e561, content_type=application/json; charset=utf-8}
2019-10-13 01:02:43.334224-0700 wonderswipe[1815:480508] Sentry Started -- Version: 4.4.0
2019-10-13 01:02:54.780 [fatal][tid:com.facebook.react.CodePushQueue] NSInvalidArgumentException: *** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[0]
2019-10-13 01:02:54.780741-0700 wonderswipe[1815:480820] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[0]'
*** First throw call stack:
(0x18fbd498c 0x18f8fd0a4 0x18fc2a3f8 0x18fc3375c 0x18fac15dc 0x18fab3470 0x104275b40 0x104271ec8 0x18fbdad90 0x18faaabd0 0x18faab7a8 0x104523ae8 0x104525bf8 0x10452595c 0x105a2d828 0x105a2ec04 0x105a35b74 0x105a36710 0x105a41ae4 0x18f8f1fa4 0x18f8f4ae0)
libc++abi.dylib: terminating with uncaught exception of type NSException

I can build

@fungilation New projects have a huge Podfile referencing a lot of modules, like React-cxxreact, etc. I suppose your setup is still the old one, without this huge Podfile, that's why you can build. Correct?

I don't think the build is working for people using the new setup.

the app would crash with this in log

You have Firebase running, but the Firebase sdk doesn't support targeting mac yet. They are working on it. (invertase/react-native-firebase#2698, firebase/firebase-ios-sdk#3144)

My app was started a while ago so no, my setup isn't "new" but I've converted my podfile to work on RN 0.60/0.61 so it's not an "old setup" either.

My podfile for reference:
platform :ios, '9.0'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'

target 'wonderswipe' do
  # Pods for wonderswipe
  pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector"
  pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec"
  pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired"
  pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety"
  pod 'React', :path => '../node_modules/react-native/'
  pod 'React-Core', :path => '../node_modules/react-native/'
  pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules'
  pod 'React-Core/DevSupport', :path => '../node_modules/react-native/'
  pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS'
  pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation'
  pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob'
  pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image'
  pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS'
  pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network'
  pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings'
  pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text'
  pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration'
  pod 'React-Core/RCTWebSocket', :path => '../node_modules/react-native/'

  pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact'
  pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi'
  pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor'
  pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector'
  pod 'ReactCommon/jscallinvoker', :path => "../node_modules/react-native/ReactCommon"
  pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon"
  pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga'

  pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
  pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
  pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'

  pod 'Firebase/Core', '~> 6.5.0'   # https://rnfirebase.io/docs/v5.x.x/releases/v5.3.x#iOS---Update-Firebase-SDKs
  # pod 'RNFirebase', :path => '../node_modules/react-native-firebase/ios'    # http://invertase.io/react-native-firebase/#/installation-ios
  # # pod 'RNFirebase', :path => '../../ios/RNFirebase.podspec'   # https://github.com/invertase/react-native-firebase/releases/tag/v3.2.0
  # [OPTIONAL PODS] - comment out pods for firebase products you won't be using.
  # pod 'Firebase/Analytics'    # redundant
  # pod 'Firebase/AdMob'
  # pod 'Firebase/Crash'
  # pod 'Firebase/DynamicLinks'
  pod 'Firebase/Auth', '~> 6.5.0'
  pod 'Firebase/RemoteConfig', '~> 6.5.0'
  pod 'Firebase/Database', '~> 6.5.0'
  pod 'Firebase/Firestore', '~> 6.5.0'
  pod 'Firebase/Storage', '~> 6.5.0'
  pod 'Firebase/Messaging', '~> 6.5.0'   # Need setup: http://invertase.io/react-native-firebase/#/installation-ios?id=_3-cloud-messaging-optional
  pod 'Firebase/Performance', '~> 6.5.0'

  # # Crashlytics
  # pod 'Fabric', '~> 1.10.1'
  # pod 'Crashlytics', '~> 3.13.1'

  # Add new pods below this line
  pod 'rn-fetch-blob', :path => '../node_modules/rn-fetch-blob'
  pod 'react-native-netinfo', :path => '../node_modules/@react-native-community/netinfo'
  # pod 'TextToSpeech', :path => '../node_modules/react-native-tts'
  # pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons'
  # pod 'react-native-keep-awake', :path => '../node_modules/react-native-keep-awake'
  # pod 'react-native-orientation', :path => '../node_modules/react-native-orientation'
  # pod 'CodePush', :path => '../node_modules/react-native-code-push'
  # pod 'RNSha256', :path => '../node_modules/react-native-sha256'
  # pod 'react-native-webview', :path => '../node_modules/react-native-webview'
  # pod 'RNCViewpager', :path => '../node_modules/@react-native-community/viewpager'
  # pod 'RNCAsyncStorage', :path => '../node_modules/@react-native-community/async-storage'
  # pod 'RNRate', :path => '../node_modules/react-native-rate/ios'
  # pod 'react-native-safari-view', :path => '../node_modules/react-native-safari-view'
  # pod 'react-native-blur', :path => '../node_modules/@react-native-community/blur'
  # pod 'BVLinearGradient', :path => '../node_modules/react-native-linear-gradient'

  target 'wonderswipeTests' do
    inherit! :search_paths
    # Pods for testing
  end

  use_native_modules!
end

target 'wonderswipe-tvOS' do
  # Pods for wonderswipe-tvOS

  target 'wonderswipe-tvOSTests' do
    inherit! :search_paths
    # Pods for testing
  end
end


# # WORKAROUND: https://github.com/facebook/react-native/issues/17274#issuecomment-356363557
# post_install do |installer|
#   installer.pods_project.targets.each do |target|
#       if target.name == 'yoga'
#           target.build_configurations.each do |config|
#               config.build_settings['GCC_TREAT_WARNINGS_AS_ERRORS'] = 'NO'
#               config.build_settings['GCC_WARN_64_TO_32_BIT_CONVERSION'] = 'NO'
#           end
#       end
#   end
# end

# # WORKAROUND, otherwise need to remove React from pods in Xcdoe on `pod update` - doesn't work w/ multiple "[!] [Xcodeproj] Generated duplicate UUIDs": https://stackoverflow.com/questions/42021796/react-native-xcode-project-product-archive-fails-with-duplicate-symbols-for-arch/46678210#46678210
# post_install do |installer|
#   installer.pods_project.targets.each do |target|
#     if target.name == "React"
#       target.remove_from_project
#     end
#   end
# end

# # WORKAROUND for Multiple commands error when building with Xcode 10 xcodebuild tools: https://github.com/facebook/react-native/issues/20492#issuecomment-422958184
# post_install do |installer|
#   installer.pods_project.targets.each do |target|

#     # The following is needed to ensure the "archive" step works in XCode.
#     # It removes React & Yoga from the Pods project, as it is already included in the main project.
#     # Without this, you'd see errors when you archive like:
#     # "Multiple commands produce ... libReact.a"
#     # "Multiple commands produce ... libyoga.a"

#     targets_to_ignore = %w(React yoga)
    
#     if targets_to_ignore.include? target.name
#       target.remove_from_project
#     end

#   end
# end

I initiated a fresh react native project which has 61.2. Tried to run it with the new Mac target. OS- catalina, xcode - 11.1. Got the websocket error -

Screenshot 2019-10-17 at 11 01 05 am

@kaiyes I was having the same issue and eventually I got the app running by selecting the Pods Project on Xcode -> select the React-Core target -> Build Phases -> Compile Sources -> Change the platform of RCTWebSocketExecutor.m and RCTWebSocketModule.m to iOS only.

Hope it helps!

@ruimagalhaes thanks, that make the build work!

But the app only runs on Release mode, right?

On Development mode I get this:

Screen Shot 2019-10-19 at 14 58 51

Oh my god, it works! Even the swipe gestures using react-native-gesture-handler work great!

Here's DevHub running as a native macOS app:

Kapture 2019-10-19 at 16 00 49

Tweeted a more complete video here: twitter.com/brunolemos/status/1185636022346043392

@brunolemos Correct, since development connects to the packager via a websocket (to enable hot reloading), and websockets don't work yet, you need to build in Release mode for Mac.

To reiterate, to get your app working:

  • make sure you're using RN >=0.61.2 (and macOS Catalina + Xcode 11 obviously)
  • select the Mac target (you also need to select iPad)
    Screen Shot 2019-10-19 at 9 22 13 PM
  • change your scheme's Run > Build configuration to Release
    Screen Shot 2019-10-19 at 9 25 20 PM
  • and change in Pods > React-Core > Build Phases > Compile Sources > RCTWebSocketModule.m to iOS only
    Screen Shot 2019-10-19 at 9 26 26 PM
    If you're getting Undefined symbols for architecture x86_64: "_OBJC_CLASS_$_RCTSRWebSocket" compilation error, repeat the last step (needs to happen every time you update your dependencies).

Thanks a lot @petrbela & @ruimagalhaes . It worked πŸ˜„ A vanilla fresh React-native init project on 61.2, macOs - catalina and xcode 11
Screenshot 2019-10-20 at 8 09 12 am

What needs to be done till web sockets will work in debug scheme on Mac Catalina?

Thats a good question.

My app builds now, but at some point something fails in the js code and i don't know how to debug it in the release version.

In Clojurescript, I was able to work around this issue by implementing my own custom WebSockets module in Swift, I documented the process here

Some of the code above might be of use for JS people, however, I suspect React Native core requires the WebSocket module before you could overload/shim it...

Introducing keyboard shortcuts to show up the dev menu here: facebook/react-native#27479.

Anyone has made it work with the latests 0.62.0-rc.2? I'm hitting facebook/react-native#27845. It would be really awesome to have macOS support soon!

@mgcrea, for now, you can just disable Flipper since it seems to be the cause of the error. You can comment out the lines that mention flipper in the AppDelegate.m file (lines 7 through 24, and lines 30 through 32).

Quick question, how to run with Mac target from console, is there an analog for react-native run-ios but for Mac? Or for now only XCode launch is supported?

@kirill-konshin
there was a PR in the cli recently that added support to run MacOS target form the cli react-native-community/cli#1024

@Naturalclar I just tried react-native@0.52-rc.5 and @react-native-community/cli@4.3.0 and when I ran react-native run-ios --device "XXX" I still got No iOS devices connected. Looks like this PR hasn't been released yet? @robertying please clarify :)

@Naturalclar I just tried react-native@0.52-rc.5 and @react-native-community/cli@4.3.0 and when I ran react-native run-ios --device "XXX" I still got No iOS devices connected. Looks like this PR hasn't been released yet? @robertying please clarify :)

I think they haven’t released a new version containing this commit. I’m not one of the maintainers so I don’t know when they will release it either.

You can use something like patch-package to manually apply the commit diff as a workaround.

Anyone has made it work with the latests 0.62.0-rc.2? I'm hitting facebook/react-native#27845. It would be really awesome to have macOS support soon!

Seems that macos support not planned in nearest future.

Microsoft is hard at work building out the react-native-macos platform, which in my opinion is a far better solution than relying on Project Catalyst. Just recently they merged this PR: microsoft/react-native-macos#291

Microsoft is hard at work building out the react-native-macos platform, which in my opinion is a far better solution than relying on Project Catalyst. Just recently they merged this PR: microsoft/react-native#291

That's just amazing! Agree that project Catalyst has many shortcomings, eg. any macOS specific API is unavailable like handling file drops, using system menu, etc. that makes it currently a pretty poor option to build real applications.

@kevinvangelder I've created react-native(*) native module using $ create-react-native-module MyNativeModule --generate-example and build&run it on MacOS. So in theory I can implement in that module all that I can do in ordinary Objective-C application and connect it to react-native ui. Or I wrong?


@Andreychikov-Vasiliy Yes, in theory you should be able to write a Swift/Obj-C module using macOS APIs. You may run into shortcomings working with the react-native-macos bridge implementation (I don't know how complete it is in it's current state), but if you have issues now I would expect that they'll be resolved in the near future as Microsoft is putting a lot of time and effort into this.

So, for example I already see one limitation:
image
Sorry for maybe stupid question, but if NSAlert doesn't work with facebook/react-native & Catalyst but probably on microsoft/react-native fork it will work (now or in future)?