Skip to content

Mac Catalyst patches - #34026

Closed
Arkkeeper wants to merge 8 commits into
react:mainfrom
Arkkeeper:main
Closed

Mac Catalyst patches#34026
Arkkeeper wants to merge 8 commits into
react:mainfrom
Arkkeeper:main

Conversation

@Arkkeeper

Copy link
Copy Markdown
Contributor

Summary

This PR adds a new method called __apply_mac_catalyst_patches to scripts/react_native_pods.rb. If it is enabled in the Podfile, it will apply three patches necessary for successful building not only for iOS and tvOS targets, but also for macOS using Apple's Mac Catalyst technology.

These 3 patches are:

  • Fixing bundle signing issues by altering CODE_SIGN_IDENTITY
  • Explicitly setting dead code stripping flag in project.pbxproj
  • Modifying library search paths

The details were discussed here reactwg/react-native-releases#21 (comment)

Changelog

[iOS] [Added] - Add Mac Catalyst compatibility (can be enabled in Podfile)

Test Plan

  1. Go to project settings in Xcode, to General tab. Enable "iPad" and "Mac Catalyst" checkboxes
  2. Go to "Signing & Capabilities" tab, ensure that a correct bundle id and development team are set
  3. Edit Podfile, uncomment __apply_mac_catalyst_patches(installer) line
  4. Run pod install in ios directory
  5. Get back to Xcode, select "My Mac (Mac Catalyst)" as a target device
  6. Build & run

@facebook-github-botfacebook-github-bot added CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. labels Jun 18, 2022
@react-native-botreact-native-bot added Platform: iOS iOS applications. Type: Enhancement A new feature or enhancement of an existing feature. labels Jun 18, 2022
@analysis-bot

analysis-bot commented Jun 18, 2022

Copy link
Copy Markdown
PlatformEngineArchSize (bytes)Diff
androidhermesarm64-v8a7,830,116-232
androidhermesarmeabi-v7a7,216,498-208
androidhermesx868,139,019-258
androidhermesx86_648,120,749-269
androidjscarm64-v8a9,695,700-244
androidjscarmeabi-v7a8,451,163-196
androidjscx869,646,317-255
androidjscx86_6410,244,130-274

Base commit: d6c08bd
Branch: main

@mikehardymikehardy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These look like exactly the compromise discussed in the road to 0.69 discussion - make it easy for people to do, but not enabled by default since side effects may break non-catalyst folks. Looks great to me from that perspective and is technically what I've been doing to get my catalyst builds to work. Nice!

@analysis-bot

analysis-bot commented Jun 18, 2022

Copy link
Copy Markdown
PlatformEngineArchSize (bytes)Diff
ios-universaln/a--

Base commit: d6c08bd
Branch: main

@mikehardy

Copy link
Copy Markdown
Contributor

Hey @Arkkeeper - I followed some curiosity in a related thread and there may be a smaller / more minimal intervention for the DEAD_CODE_STRIPPING portion of this fix. You might try it, it appears to work based on success reports

#32016 (comment)

#32016 (comment)

With apologies, I dug it up but have not tried it myself yet for the macCatalyst case...

@cortinico

Copy link
Copy Markdown
Contributor

Looks great to me from that perspective and is technically what I've been doing to get my catalyst builds to work. Nice!

Code looks good to me. Thanks @mikehardy for cross-checking it.
I'd like @cipolleschi to pick this up and drive this forward.

What I can say on the code itself, is that we've been moving a lot of our ruby code from scripts/react_native_pods.rb to scripts/cocoapods (like this PR: #33982).
So ideally having this code added directly there + tests would be preferrable. You can find other tests there that can be used as inspiration.

@Arkkeeper

Arkkeeper commented Jun 22, 2022

Copy link
Copy Markdown
ContributorAuthor

@cortinico thanks for your comment, I've modified this PR according to it

@mikehardymikehardy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now looks even better to me :-)

@facebook-github-bot

Copy link
Copy Markdown
Contributor

@cortinico has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@cipolleschicipolleschi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Arkkeeper, thanks a lot for your PR. Great work and thanks to the effort of supporting also Catalyst!

I apologise for the delay, but I was on PTO last week.

I left some comments and I suggested some changes to improve the code structure and the testing. Let me know what do you think about them!

Comment on lines +307 to +309
firstTarget = prepare_target("FirstTarget")
secondTarget = prepare_target("SecondTarget")
thirdTarget = prepare_target("ThirdTarget")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
firstTarget=prepare_target("FirstTarget")
secondTarget=prepare_target("SecondTarget")
thirdTarget=prepare_target("ThirdTarget")
first_target=prepare_target("FirstTarget")
second_target=prepare_target("SecondTarget")
third_target=prepare_target("ThirdTarget")

[nit] let's stick to the snake_case for everything in Ruby. It is a personal preference, but mixing the cases makes it harder to collaborate.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's also regards test_fixLibrarySearchPaths_correctlySetsTheSearchPathsForAllProjects. I've renamed both of them.

Comment on lines +331 to +333
if target.respond_to?(:product_type) and target.product_type == "com.apple.product-type.bundle"
target.build_configurations.each do |config|
assert_equal(config.build_settings["CODE_SIGN_IDENTITY[sdk=macosx*]"], "-")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These lines are replicating the logic of the fix. It is not really testing that the code executes correctly. For example, this test will pass also when "CODE_SIGN_IDENTITY[sdk=macosx*]" is set to "-" for all the target.

Can I suggest a different approach?

We already have the references to all the the targets (firstTarget, secondTarget, thirdTarget). Why don't we assert the targets directly?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thing that I don't think is correct: TargetMock does not respond to :product_type.
The TargetMock class should be enhanced to have this property and the property must be set accordingly.

I'm pretty sure that this assert is never really executed. Could you double check that, please?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are still executing the same logic in the tests.
How do you feel about asserting something like this:

first_target.build_configurations.eachdo |config|
assert_nil(config.build_settings["CODE_SIGN_IDENTITY[sdk=macosx*]"])endsecond_target.build_configurations.eachdo |config|
assert_nil(config.build_settings["CODE_SIGN_IDENTITY[sdk=macosx*]"])endthird_target.build_configurations.eachdo |config|
assert_equal(assert_equal(config.build_settings["CODE_SIGN_IDENTITY[sdk=macosx*]"],"-"))end

?

In this way we don't replicate the logic but we assert on the expected outcome of the cod execution.

Comment threadscripts/cocoapods/utils.rb Outdated
config.build_settings['LIBRARY_SEARCH_PATHS'] = ['$(SDKROOT)/usr/lib/swift', '$(SDKROOT)/System/iOSSupport/usr/lib/swift', '$(inherited)']
end
end
aggregate_target.user_project.save

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] Perhaps it is not idiomatic, but could we use save()? We use the () everywhere else and it is better to be consistent.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, our internal linter says that there are trailing whitespaces at this line. Can you remove them?

Comment threadtemplate/ios/Podfile Outdated
react_native_post_install(installer)
__apply_Xcode_12_5_M1_post_install_workaround(installer)
# Enable the next line in order to apply patches necessary for Mac Catalyst builds
#__apply_mac_catalyst_patches(installer)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll move the invocation of __apply_mac_catalyst_patches(installer) inside the react_native_post_install, actually removing the __apply_mac_catalyst_patches. Given that not all the projects need these, we can update the react_native_post_install function to accept a boolean apply_catalyst_patch with a default value of false.

The rationale here is that:

  1. it helps discoverability. At the end of the refactoring effort, the react_native_pods script will have only two function: use_react_native and react_native_post_install. These will be documented and people will know about the existence of this flag. Otherwise, they have to read the whole script file to learn bout its existence.
  2. it makes it easier for us to maintain the script. When, in the future, we will have to remove it, we could just delete all the code, and deprecate the flag. No other change will be required by the apps for a while, until we actually remove the flag.

Note that this change may foce you to also update the RNTester app.

How do you feel about this? Does it make sense?

end
end
end

@cipolleschicipolleschiJun 27, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our internal linter says that there are trailing whitespaces at this line. Can you remove them?

end

assert_equal(user_project_mock.save_invocation_count, 1)
end

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our internal linter says that there are trailing whitespaces at this line. Can you remove them?

Comment threadscripts/cocoapods/utils.rb Outdated
config.build_settings['PRESERVE_DEAD_CODE_INITS_AND_TERMS'] = 'YES'
# Modify library search paths
config.build_settings['LIBRARY_SEARCH_PATHS'] = ['$(SDKROOT)/usr/lib/swift', '$(SDKROOT)/System/iOSSupport/usr/lib/swift', '$(inherited)']
end

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our internal linter says that there are trailing whitespaces at this line. Can you remove them?

@Arkkeeper

Copy link
Copy Markdown
ContributorAuthor

@cipolleschi thank you for your proposals, I've tried to implement them all.

@cortinico

Copy link
Copy Markdown
Contributor

Could you resolve the conflict on packages/rn-tester/Podfile@Arkkeeper ?

@cortinicocortinico reopened this Jun 29, 2022
@cortinico

Copy link
Copy Markdown
Contributor

There seems to be an issue with the CI:

undefined local variable or method `mac_catalyst_enabled' for #Pod::Podfile:0x00007fe38f92e990

@facebook-github-bot

Copy link
Copy Markdown
Contributor

@cortinico has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

Comment threadpackages/rn-tester/Podfile Outdated
project 'RNTesterPods.xcodeproj'

fabric_enabled = true
mac_catalyst_enabled = false

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is not required! ;)

Comment threadpackages/rn-tester/Podfile Outdated
post_install do |installer|
react_native_post_install(installer, @prefix_path)
mac_catalyst_enabled = false
react_native_post_install(installer, mac_catalyst_enabled, @prefix_path)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of defining a variable, which I think is done just to have the name of the parameter for a code-as-documentation purpose, why don't we use named parameters from Ruby?

We can define a function with this syntax:

deffoo(named_param: false)end

and then invoke the function with this syntax:

foo(:named_param=>true)

with this, we can emove the variable declaration and update this code as it follows:

Suggested change
react_native_post_install(installer, mac_catalyst_enabled, @prefix_path)
react_native_post_install(installer, @prefix_path, :mac_catalyst_enabled => false)

Comment on lines +331 to +333
if target.respond_to?(:product_type) and target.product_type == "com.apple.product-type.bundle"
target.build_configurations.each do |config|
assert_equal(config.build_settings["CODE_SIGN_IDENTITY[sdk=macosx*]"], "-")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are still executing the same logic in the tests.
How do you feel about asserting something like this:

first_target.build_configurations.eachdo |config|
assert_nil(config.build_settings["CODE_SIGN_IDENTITY[sdk=macosx*]"])endsecond_target.build_configurations.eachdo |config|
assert_nil(config.build_settings["CODE_SIGN_IDENTITY[sdk=macosx*]"])endthird_target.build_configurations.eachdo |config|
assert_equal(assert_equal(config.build_settings["CODE_SIGN_IDENTITY[sdk=macosx*]"],"-"))end

?

In this way we don't replicate the logic but we assert on the expected outcome of the cod execution.

Comment threadscripts/react_native_pods.rb Outdated
Comment on lines +145 to +147
if mac_catalyst_enabled
ReactNativePodsUtils.apply_mac_catalyst_patches(installer)
end

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] We can use a conditional modifier here:

Suggested change
ifmac_catalyst_enabled
ReactNativePodsUtils.apply_mac_catalyst_patches(installer)
end
ReactNativePodsUtils.apply_mac_catalyst_patches(installer)ifmac_catalyst_enabled

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume, the double assert_equal in third_target.build_configurations.each block is redundant, right?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, sorry... bad copy-and-paste... 🤦

Comment threadscripts/react_native_pods.rb Outdated
end

def react_native_post_install(installer, react_native_path = "../node_modules/react-native")
def react_native_post_install(installer, mac_catalyst_enabled, react_native_path = "../node_modules/react-native")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would use a named parameter here, to have self-documenting code. What do you think?

Suggested change
defreact_native_post_install(installer,mac_catalyst_enabled,react_native_path="../node_modules/react-native")
defreact_native_post_install(installer,react_native_path="../node_modules/react-native",mac_catalyst_enabled: false)

Comment threadtemplate/ios/Podfile Outdated
Comment on lines +36 to +38
mac_catalyst_enabled = false

react_native_post_install(installer, mac_catalyst_enabled)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we use the named parameter approach, we can avoid declaring the variable! ;)

@cipolleschi

cipolleschi commented Jun 30, 2022

Copy link
Copy Markdown
Contributor

Great job! 👏 I did another round of review, leaving also a couple of nitpicks, if you want to use them. I think that what we actually need to fix are the tests, because at the moment we are testing the code by knowing how it executes instead of trying to assert the final state of the system. What do you think?

@Arkkeeper

Copy link
Copy Markdown
ContributorAuthor

@cipolleschi thank you again for your comments and patience, I've commited the proposed edits.

@cipolleschi

Copy link
Copy Markdown
Contributor

Thasnks a lot for the effort! 👏 Amazing job! :D

@facebook-github-bot

Copy link
Copy Markdown
Contributor

@cortinico has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator.

@react-native-bot

Copy link
Copy Markdown
Collaborator

This pull request was successfully merged by @Arkkeeper in 2fb6a33.

When will my fix make it into a release? | Upcoming Releases

@react-native-botreact-native-bot added the Merged This PR has been merged. label Jun 30, 2022
@mikehardy

Copy link
Copy Markdown
Contributor

A bit of a comment thread resurrection here but I just want to mention that I just got around to integrating this mac_catalyst_enabled flag in my test script, and it works great, I was able to eliminate most of the podfile workarounds I had - quite nice

Only thing I can think of now is that it may make sense to also integrate one more build setting, but it's also an obvious one for developers. I do it like so in my script

pbxproj flag ios/rnfbdemo.xcodeproj --target rnfbdemo SUPPORTS_MACCATALYST YES

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA SignedThis label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.MergedThis PR has been merged.Platform: iOSiOS applications.Shared with MetaApplied via automation to indicate that an Issue or Pull Request has been shared with the team.Type: EnhancementA new feature or enhancement of an existing feature.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@Arkkeeper@analysis-bot@mikehardy@cortinico@facebook-github-bot@cipolleschi@react-native-bot