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

feat(macos): support react-native-macos #1002

Open
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

shirakaba
Copy link
Contributor

@shirakaba shirakaba commented Jul 22, 2024

Fixes #1001

Description

Adds react-native-macos support 🥳

Steps to verify

Follow the updated setup instructions in examples/demo/README.md to run the demo app. See the following screenshots for expected behaviour with the two providers:

Screenshot 2024-07-22 at 13 16 54

Screenshot 2024-07-22 at 13 17 40

Copy link

changeset-bot bot commented Jul 22, 2024

🦋 Changeset detected

Latest commit: b3da68b

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
react-native-app-auth Minor
rnaa-demo Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link

vercel bot commented Jul 22, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
react-native-app-auth ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 30, 2024 6:02am

examples/demo/metro.config.js Outdated Show resolved Hide resolved
examples/demo/package.json Outdated Show resolved Hide resolved

#if TARGET_OS_MACCATALYST
id<OIDExternalUserAgent> externalUserAgent = nil;
#elif TARGET_OS_IOS
id<OIDExternalUserAgent> externalUserAgent = iosCustomBrowser != nil ? [self getCustomBrowser: iosCustomBrowser] : [self getExternalUserAgentWithPresentingViewController:presentingViewController
prefersEphemeralSession:prefersEphemeralSession];
#elif TARGET_OS_OSX
id<OIDExternalUserAgent> externalUserAgent = nil;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was unsure about this externalUserAgent = nil, but it does turn out that under-the-hood for the APIs we're calling, the AppAuth library makes an appropriate OIDExternalUserAgentMac for you so there's no need to make one yourself.

Comment on lines 582 to 584
#if TARGET_OS_IOS || TARGET_OS_MACCATALYST
[UIApplication.sharedApplication endBackgroundTask:rnAppAuthTaskId];
rnAppAuthTaskId = UIBackgroundTaskInvalid;
#elif TARGET_OS_OSX
// Brings this app to the foreground.
[[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows)];
#endif
Copy link
Contributor Author

Choose a reason for hiding this comment

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

macOS AppKit apps don't get backgrounded, so we don't need to declare background tasks (but do benefit from refocusing the app).

Comment on lines -12 to +13
s.platform = :ios, '10.0'
s.ios.deployment_target = '10.0'
s.osx.deployment_target = '10.15'
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Technically, AppAuth lets us deploy down to the following versions:

s.ios.deployment_target = "9.0"
s.osx.deployment_target = "10.12"

I've chosen 10.15 for this library just to align with the react-native-macos template's minimum deployment target.

@shirakaba shirakaba changed the title Feat/macos/support react native macos feat(macos): support react-native-macos Jul 22, 2024
@shirakaba
Copy link
Contributor Author

shirakaba commented Jul 22, 2024

Thanks @Saadnajmi for your great guidance! I'll address those aspects once I'm free (think today I'll be working on other things, however). As we discussed on Twitter, react-native-test-app may be the way forwards for integrating an out-of-tree-platform's example app into this repo, and rnx-kit may be able to help out with the Metro config.

@shirakaba
Copy link
Contributor Author

shirakaba commented Jul 27, 2024

@Saadnajmi I've now migrated the PR to use react-native-test-app, which resolves my issues with the Metro bundler. I've also aligned to a common minor of React Native, and have addressed all the ifdefs.

However, the demo app requires a custom AppDelegate to make it conform to RNAppAuthAuthorizationFlowManager. How would I set that up?

@Saadnajmi
Copy link

@Saadnajmi I've now migrated the PR to use react-native-test-app, which resolves my issues with the Metro bundler. I've also aligned to a common minor of React Native, and have addressed all the ifdefs.

However, the demo app requires a custom AppDelegate to make it conform to RNAppAuthAuthorizationFlowManager. How would I set that up?

I don't think RNTA supports custom App Delegates, so that would be a feature request (@tido64 fyi). It does support expo config plugins, does that let you modify the app delegate? https://github.com/microsoft/react-native-test-app/wiki/Config-Plugins

Though I'm realizing for macOS, that means you need 0.74 😅

@shirakaba
Copy link
Contributor Author

Unfortunately the one implementing Expo config plugins support for react-native-macos is none other than myself, so I can report that it’s not ready yet 😅

I’ve actually already completed the implementation of Expo config plugins, but need to rewrite the tests, which I was hoping to get at this evening. Still though, it would depend on Expo reviewing and merging it, and even then would only make it into SDK 51 (which expects RN 0.74 🥲) or later. So I think we’d either need a new feature in RNTestApp or go without it in this case.

@shirakaba
Copy link
Contributor Author

shirakaba commented Jul 27, 2024

Okay, I've ripped out RNTA again and now have a mix of react-native@0.72.4 and react-native-macos@0.73.30.

  • I've tried raising react-native to v0.73 to get them both on v0.73 and couldn't get iOS to build (even when using the react-native iOS template from React Native Community CLI). Some issue to do with examples/demo/node_modules/@react-native/codegen/lib/generators/modules/GenerateModuleObjCpp/serializeMethod.js#495:

    "NativeAppState.getConstants()" may only return an object literal: {...}.

  • I've also tried dropping react-native-macos to v0.72 to get them both on v0.72 and couldn't get macOS to build (tried generating the react-native-macos template for v0.72 and the CLI threw errors, so couldn't proceed). Some genericNodeError from react-native-macos-init with no information:

    The react-native-macos-init error
    scratch/RNSACExample npx react-native-macos-init --version 0.72.21
    Reading application name from package.json…
    Reading react-native version from node_modules…
    Reading react-native-macos version from node_modules…
    Installing react-native-macos@0.72.21…
    Command failed: yarn add -s "react-native-macos@0.72.21" Error: Command failed: yarn add -s "react-native-macos@0.72.21"
        at genericNodeError (node:internal/errors:984:15)
        at wrappedFn (node:internal/errors:538:14)
        at checkExecSyncError (node:child_process:890:11)
        at Object.execSync (node:child_process:962:15)
        at Object.<anonymous> (/Users/jamie/.npm/_npx/20f1505fae2cef5b/node_modules/react-native-macos-init/lib-commonjs/cli.js:247:29)
        at Generator.next (<anonymous>)
        at fulfilled (/Users/jamie/.npm/_npx/20f1505fae2cef5b/node_modules/react-native-macos-init/lib-commonjs/cli.js:10:58)
        at runNextTicks (node:internal/process/task_queues:60:5)
        at process.processImmediate (node:internal/timers:454:9) {
      status: 1,
      signal: null,
      output: [
        null,
        <Buffer 1b 5b 33 31 6d 1b 5b 31 6d 55 6e 6b 6e 6f 77 6e 20 53 79 6e 74 61 78 20 45 72 72 6f 72 1b 5b 32 32 6d 1b 5b 33 39 6d 3a 20 55 6e 73 75 70 70 6f 72 74 ... 183 more bytes>,
        <Buffer >
      ],
      pid: 3382,
      stdout: <Buffer 1b 5b 33 31 6d 1b 5b 31 6d 55 6e 6b 6e 6f 77 6e 20 53 79 6e 74 61 78 20 45 72 72 6f 72 1b 5b 32 32 6d 1b 5b 33 39 6d 3a 20 55 6e 73 75 70 70 6f 72 74 ... 183 more bytes>,
      stderr: <Buffer >
    }
    
    

This is the best I can do! Would be able to get them both on the same version if RNTA supported custom AppDelegates, however.

@shirakaba shirakaba marked this pull request as ready for review July 27, 2024 14:17
@shirakaba shirakaba requested a review from Saadnajmi July 27, 2024 14:18
@tido64
Copy link

tido64 commented Jul 29, 2024

Would be able to get them both on the same version if RNTA supported custom AppDelegates, however.

I don't know if this helps, but I've had this branch for a while that I've just submitted: microsoft/react-native-test-app#2160

@shirakaba
Copy link
Contributor Author

shirakaba commented Aug 5, 2024

@tido64 Thanks so much for that PR; it should make everything easier! Sorry I wasn't able to review any closer – I had my hands full last week. I'll try it out once I get back to this PR.

Copy link
Member

@robwalkerco robwalkerco left a comment

Choose a reason for hiding this comment

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

Thanks for the great PR ❤️

I've spotted a few very minor issues that would be great if you could address before we merge.

Copy link
Member

Choose a reason for hiding this comment

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

The current version of CocoaPods is 1.15.2 - https://github.com/CocoaPods/CocoaPods/releases

Can this change be reverted, or is there a specific reason for this change?

Copy link
Member

Choose a reason for hiding this comment

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

It would be very helpful if you could add/update any setup instructions to cover macOS as well on docs/docs/introduction.md and README.md.

Copy link
Member

Choose a reason for hiding this comment

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

It would be great if this could also use COCOAPODS: 1.15.2 instead of 1.14.3

@shirakaba
Copy link
Contributor Author

shirakaba commented Aug 16, 2024

@robwalkerco Thank you for the review! Pending tasks in my mind:

  • Adopt React Native Test App again (now that it supports custom AppDelegates)
  • Update to React Native v0.74 (as it recently became available for React Native macOS)
  • Use the same minor version of React Native for both react-native and react-native-macos
  • As per review: Use CocoaPods 1.15.2
  • As per review: Add any setup instructions to cover macOS into docs/docs/introduction.md and README.md.

I've been a little burnt out lately so may not be able to jump straight on this but do hope to push this over the line soon.

.yarnrc Outdated
# yarn lockfile v1


yarn-path ".yarn/releases/yarn-1.19.1.cjs"
Copy link
Contributor

Choose a reason for hiding this comment

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

If we want to use a non-global version of Yarn, we should look at enabling corepack instead of checking in the binary. Since this is unrelated to this specific PR, it would be best to revert this change and open a separate PR for this specifically.

Copy link
Contributor Author

@shirakaba shirakaba Aug 18, 2024

Choose a reason for hiding this comment

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

I was actually hoping to just install using global Yarn v1 here, but installation always failed with the following error:

expected workspace package to exist for "eslint"

I looked up the error and it recommended this as the fix. If you know another way to get yarn to successfully install this repo's packages, I'd be happy to implement that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I managed to successfully install with global Yarn v1, so I've removed this.

@shirakaba
Copy link
Contributor Author

shirakaba commented Aug 18, 2024

I've done a little more work on this to see if this can be migrated to React Native Test App, which would be helpful for adding Windows support in future.

@tido64 I'm struggling to use macos.withAppDelegate() to set up the necessary custom AppDelegate. We need to do two things:

  1. Make AppDelegate conform to RNAppAuthAuthorizationFlowManager. This is possible by referencing the following examples/demo/config-plugin.js via "plugins": ["./config-plugin.js"] in examples/demo/app.json:
const { createRunOncePlugin, } = require("@expo/config-plugins");
const { mergeContents } = require("@expo/config-plugins/build/utils/generateCode");
const { macos } = require("react-native-test-app/plugins/index");

function withCustomAppDelegate(config) {
  return macos.withAppDelegate(config, (config) => {
    // See:
    // - examples/demo/node_modules/react-native-test-app/macos/ReactTestApp/AppDelegate.swift
    // - https://github.com/microsoft/react-native-test-app/pull/2160#issue-2435799831
    config.modResults.contents = mergeContents({
      tag: "config-plugin.js",
      src: config.modResults.contents,
      newSrc: "extension AppDelegate: RNAppAuthAuthorizationFlowManager {}",
      anchor: /extension AppDelegate {/,
      offset: 0,
      comment: "//",
    }).contents;

    return config;
  });
}

module.exports = createRunOncePlugin(
  withCustomAppDelegate,
  "test-plugin.js",
  "UNVERSIONED"
);
  1. Express the following Obj-C as Swift in AppDelegate.swift:
#import <React/RCTLinkingManager.h>

// ... a few lines later:

- (void)applicationWillFinishLaunching:(NSNotification *)notification {
  [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self
                           andSelector:@selector(getURL:withReplyEvent:)
                         forEventClass:kInternetEventClass
                            andEventID:kAEGetURL];
}

- (void)getURL:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)reply
{
  NSString* urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
  NSURL *url = [NSURL URLWithString:urlString];

  if ([self.authorizationFlowManagerDelegate resumeExternalUserAgentFlowWithURL:url]) {
    return;
  }
  
  [RCTLinkingManager getUrlEventHandler:event withReplyEvent:reply];
}

... Unfortunately, the current AppDelegate has multiple occurences of the string "applicationWillFinishLaunching", so it is impossible to come up with a non-fragile RegExp pattern to use with it (particularly as the config plugin does not support multi-line regexes). The Expo Config AppDelegate Plugin moreover seems to no-op if there's more than one match.

Largely, this is down to the mergeContents() method from Expo Config Plugins being a very limited API to begin with. I suppose there's no need to use mergeContents(), actually – maybe we can just do a bit of manual string manipulation ourselves inside withAppDelegate 🤔

@shirakaba
Copy link
Contributor Author

shirakaba commented Aug 18, 2024

@robwalkerco I'm proceeding to try to get this onto React Native Test App so that adding support for Windows in future will be simpler and to improve long-term maintainability (we'd be able to just bump the version of RNTA to upgrade all example apps). Would you be happy with me migrating the repo to use RNTA as part of this PR, or should it be a follow-up PR?

(Noting that I wasn't having much luck getting this working without RNTA – testing the react-native and react-native-macos example apps required manually flipping a variable in metro.config.js depending on which one wanted to bundle at the given time)

@tido64
Copy link

tido64 commented Aug 19, 2024

... Unfortunately, the current AppDelegate has multiple occurences of the string "applicationWillFinishLaunching", so it is impossible to come up with a non-fragile RegExp pattern to use with it (particularly as the config plugin does not support multi-line regexes). The Expo Config AppDelegate Plugin moreover seems to no-op if there's more than one match.

I added some anchor points. I hope this helps: microsoft/react-native-test-app#2188

@robwalkerco
Copy link
Member

Would you be happy with me migrating the repo to use RNTA as part of this PR, or should it be a follow-up PR?

@shirakaba It would be preferable if adding RNTA could be handled in a separate PR. That should also help to get this initial macOS PR merged more quickly for you.

@shirakaba
Copy link
Contributor Author

shirakaba commented Oct 30, 2024

@robwalkerco I've:

  • rebased onto main
  • matched the minor version of react-native (v0.72) used in this repo
  • removed RNTA
  • removed the local yarn
  • removed the need for a applePlatform = "macos" config flag in metro.config.js so that we can use the same bundler to run all platforms at once
  • updated the README.md
  • confirmed the app still works on iOS
image image

Is there any blocker to merging this now?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

react-native-macos support
5 participants