diff --git a/packages/cli/src/lib/plugins/logConfig.ts b/packages/cli/src/lib/plugins/logConfig.ts index 95b5d01ad..b0ad52cc2 100644 --- a/packages/cli/src/lib/plugins/logConfig.ts +++ b/packages/cli/src/lib/plugins/logConfig.ts @@ -18,6 +18,7 @@ function filterConfig(config: Config) { // but in our case we don't install `@react-native-community/cli-platform-*` as a dependencies // so the config.platforms key is empty, which makes autolinking treat it as a dependency. delete filtered.dependencies['react-native']; + delete filtered.dependencies['react-native-tvos']; const dependencies: Record = {}; Object.keys(filtered.dependencies).forEach((item) => { if (isValidRNDependency(filtered.dependencies[item])) { diff --git a/packages/platform-android/src/lib/commands/buildAndroid/command.ts b/packages/platform-android/src/lib/commands/buildAndroid/command.ts index be7a23d3c..7c8738b2a 100644 --- a/packages/platform-android/src/lib/commands/buildAndroid/command.ts +++ b/packages/platform-android/src/lib/commands/buildAndroid/command.ts @@ -6,10 +6,11 @@ import { buildAndroid, options } from './buildAndroid.js'; export function registerBuildCommand( api: PluginApi, - pluginConfig: AndroidProjectConfig | undefined + pluginConfig: AndroidProjectConfig | undefined, + commandName: string ) { api.registerCommand({ - name: 'build:android', + name: commandName, description: 'Builds your app for Android platform.', action: async (args) => { const androidConfig = getValidProjectConfig( diff --git a/packages/platform-android/src/lib/commands/generateKeystore.ts b/packages/platform-android/src/lib/commands/generateKeystore.ts index b26dfa1ef..fbd5c9ba9 100644 --- a/packages/platform-android/src/lib/commands/generateKeystore.ts +++ b/packages/platform-android/src/lib/commands/generateKeystore.ts @@ -17,10 +17,11 @@ import { getValidProjectConfig } from './getValidProjectConfig.js'; export function registerCreateKeystoreCommand( api: PluginApi, - pluginConfig: AndroidProjectConfig | undefined + pluginConfig: AndroidProjectConfig | undefined, + commandName: string ) { api.registerCommand({ - name: 'create-keystore:android', + name: commandName, description: 'Creates a keystore file for signing Android release builds.', action: async (args) => { const androidConfig = getValidProjectConfig( diff --git a/packages/platform-android/src/lib/commands/runAndroid/command.ts b/packages/platform-android/src/lib/commands/runAndroid/command.ts index e5f805a95..28a057406 100644 --- a/packages/platform-android/src/lib/commands/runAndroid/command.ts +++ b/packages/platform-android/src/lib/commands/runAndroid/command.ts @@ -6,10 +6,11 @@ import { runAndroid, runOptions } from './runAndroid.js'; export function registerRunCommand( api: PluginApi, - pluginConfig: AndroidProjectConfig | undefined + pluginConfig: AndroidProjectConfig | undefined, + commandName: string ) { api.registerCommand({ - name: 'run:android', + name: commandName, description: 'Builds your app and starts it on a connected Android emulator or a device.', action: async (args) => { diff --git a/packages/platform-android/src/lib/commands/runAndroid/runAndroid.ts b/packages/platform-android/src/lib/commands/runAndroid/runAndroid.ts index 613935427..8711f191a 100644 --- a/packages/platform-android/src/lib/commands/runAndroid/runAndroid.ts +++ b/packages/platform-android/src/lib/commands/runAndroid/runAndroid.ts @@ -235,7 +235,8 @@ async function runOnDevice({ loader.stop( `Failed: installing and launching the app on ${color.bold( device.readableName - )}` + )}`, + 1 ); } } diff --git a/packages/platform-android/src/lib/commands/signAndroid/command.ts b/packages/platform-android/src/lib/commands/signAndroid/command.ts index 810e9e2c6..fff6373bb 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/command.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/command.ts @@ -60,9 +60,9 @@ const OPTIONS = [ }, ]; -export const registerSignCommand = (api: PluginApi) => { +export const registerSignCommand = (api: PluginApi, commandName: string) => { api.registerCommand({ - name: 'sign:android', + name: commandName, description: 'Sign the Android app with modified JS bundle.', args: ARGUMENTS, options: OPTIONS, diff --git a/packages/platform-android/src/lib/platformAndroid.ts b/packages/platform-android/src/lib/platformAndroid.ts index ea08c04c0..b29d819fb 100644 --- a/packages/platform-android/src/lib/platformAndroid.ts +++ b/packages/platform-android/src/lib/platformAndroid.ts @@ -11,10 +11,10 @@ type PluginConfig = AndroidProjectConfig; export const platformAndroid = (pluginConfig?: PluginConfig) => (api: PluginApi): PlatformOutput => { - registerBuildCommand(api, pluginConfig); - registerRunCommand(api, pluginConfig); - registerCreateKeystoreCommand(api, pluginConfig); - registerSignCommand(api); + registerBuildCommand(api, pluginConfig, 'build:android'); + registerRunCommand(api, pluginConfig, 'run:android'); + registerCreateKeystoreCommand(api, pluginConfig, 'create-keystore:keystore'); + registerSignCommand(api, 'sign:android'); return { name: '@rnef/platform-android', @@ -31,4 +31,27 @@ export const platformAndroid = }; }; +export const platformAndroidTV = + (pluginConfig?: PluginConfig) => + (api: PluginApi): PlatformOutput => { + registerBuildCommand(api, pluginConfig, 'build:android:tv'); + registerRunCommand(api, pluginConfig, 'run:android:tv'); + registerCreateKeystoreCommand(api, pluginConfig, 'create-keystore:keystore:tv'); + registerSignCommand(api, 'sign:android:tv'); + + return { + name: '@rnef/platform-android-tv', + description: 'RNEF plugin for everything Android TV.', + autolinkingConfig: { + get project() { + const androidConfig = getValidProjectConfig( + api.getProjectRoot(), + pluginConfig + ); + return { ...androidConfig }; + }, + }, + }; + }; + export default platformAndroid; diff --git a/packages/platform-android/template-tv/android/app/src/main/AndroidManifest.xml b/packages/platform-android/template-tv/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..fcd5291b6 --- /dev/null +++ b/packages/platform-android/template-tv/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/platform-apple-helpers/src/lib/utils/codegen.ts b/packages/platform-apple-helpers/src/lib/utils/codegen.ts index 700b2e58d..613457b21 100644 --- a/packages/platform-apple-helpers/src/lib/utils/codegen.ts +++ b/packages/platform-apple-helpers/src/lib/utils/codegen.ts @@ -31,7 +31,7 @@ async function runCodegen(options: CodegenOptions) { '-o', options.sourceDir, '-t', - options.platformName, + options.platformName === 'tvos' ? 'ios' : options.platformName, ]); } catch (error) { throw new RnefError('Failed to run React Native codegen script', { diff --git a/packages/platform-ios/src/lib/platformIOS.ts b/packages/platform-ios/src/lib/platformIOS.ts index f7775c0bb..acbb7df2e 100644 --- a/packages/platform-ios/src/lib/platformIOS.ts +++ b/packages/platform-ios/src/lib/platformIOS.ts @@ -23,14 +23,14 @@ export const platformIOS = action: async (args) => { intro('Building iOS app'); const projectRoot = api.getProjectRoot(); - const iosConfig = getValidProjectConfig( + const projectConfig = getValidProjectConfig( 'ios', projectRoot, pluginConfig ); await createBuild({ platformName: 'ios', - projectConfig: iosConfig, + projectConfig, args: args as BuildFlags, projectRoot, reactNativePath: api.getReactNativePath(), @@ -48,14 +48,14 @@ export const platformIOS = action: async (args) => { intro('Running iOS app'); const projectRoot = api.getProjectRoot(); - const iosConfig = getValidProjectConfig( + const projectConfig = getValidProjectConfig( 'ios', projectRoot, pluginConfig ); await createRun({ platformName: 'ios', - projectConfig: iosConfig, + projectConfig, args: args as RunFlags, projectRoot, remoteCacheProvider: await api.getRemoteCacheProvider(), @@ -75,12 +75,82 @@ export const platformIOS = description: 'RNEF plugin for everything iOS.', autolinkingConfig: { get project() { - const iosConfig = getValidProjectConfig( + const projectConfig = getValidProjectConfig( 'ios', api.getProjectRoot(), pluginConfig ); - return { ...iosConfig }; + return { ...projectConfig }; + }, + }, + }; + }; + +export const platformTVOS = + (pluginConfig?: IOSProjectConfig) => + (api: PluginApi): PlatformOutput => { + api.registerCommand({ + name: 'build:ios:tv', + description: 'Build tvOS app.', + action: async (args) => { + intro('Building tvOS app'); + const projectRoot = api.getProjectRoot(); + const projectConfig = getValidProjectConfig( + 'tvos', + projectRoot, + pluginConfig + ); + await createBuild({ + platformName: 'tvos', + projectConfig, + args: args as BuildFlags, + projectRoot, + reactNativePath: api.getReactNativePath(), + fingerprintOptions: api.getFingerprintOptions(), + remoteCacheProvider: await api.getRemoteCacheProvider(), + }); + outro('Success 🎉.'); + }, + options: buildOptions, + }); + + api.registerCommand({ + name: 'run:ios:tv', + description: 'Run tvOS app.', + action: async (args) => { + intro('Running tvOS app'); + const projectRoot = api.getProjectRoot(); + const projectConfig = getValidProjectConfig( + 'tvos', + projectRoot, + pluginConfig + ); + await createRun({ + platformName: 'tvos', + projectConfig, + args: args as RunFlags, + projectRoot, + remoteCacheProvider: await api.getRemoteCacheProvider(), + fingerprintOptions: api.getFingerprintOptions(), + reactNativePath: api.getReactNativePath(), + }); + outro('Success 🎉.'); + }, + // @ts-expect-error: fix `simulator` is not defined in `RunFlags` + options: runOptions, + }); + + return { + name: '@rnef/platform-tvos', + description: 'RNEF plugin for everything tvOS.', + autolinkingConfig: { + get project() { + const projectConfig = getValidProjectConfig( + 'tvos', + api.getProjectRoot(), + pluginConfig + ); + return { ...projectConfig }; }, }, }; diff --git a/packages/platform-ios/template-tv/.github/workflows/remote-build-tvos.yml b/packages/platform-ios/template-tv/.github/workflows/remote-build-tvos.yml new file mode 100644 index 000000000..7bd9a0169 --- /dev/null +++ b/packages/platform-ios/template-tv/.github/workflows/remote-build-tvos.yml @@ -0,0 +1,67 @@ +name: Remote Build tvOS + +on: + push: + branches: + - main + pull_request: + branches: + - '**' + +concurrency: + group: remote-build-tvos-${{ github.ref }} + cancel-in-progress: true + +jobs: + build-device: + runs-on: macos-latest + + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Setup Node + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm install + + - name: RNEF Remote Build - tvOS device + uses: callstackincubator/ios@v2 + with: + destination: device + github-token: ${{ secrets.GITHUB_TOKEN }} + scheme: SCHEME_FOR_DEVICES # replace with preferred scheme + configuration: Release # replace with preferred configuration + certificate-base64: ${{ secrets.APPLE_BUILD_CERTIFICATE_BASE64 }} + certificate-password: ${{ secrets.APPLE_BUILD_CERTIFICATE_PASSWORD }} + provisioning-profile-base64: ${{ secrets.APPLE_BUILD_PROVISIONING_PROFILE_BASE64 }} + provisioning-profile-name: 'PROVISIONING_PROFILE_NAME' # replace with actual profile name + keychain-password: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }} + + build-simulator: + runs-on: macos-latest + + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Setup Node + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: '20' + cache: 'npm' + + - name: Install dependencies + run: npm install + + - name: RNEF Remote Build - tvOS simulator + uses: callstackincubator/ios@v2 + with: + destination: simulator + github-token: ${{ secrets.GITHUB_TOKEN }} + scheme: SCHEME_FOR_SIMULATORS # replace with preferred scheme + configuration: Debug # replace with preferred configuration diff --git a/packages/platform-ios/template-tv/package.json b/packages/platform-ios/template-tv/package.json new file mode 100644 index 000000000..8cf2784f3 --- /dev/null +++ b/packages/platform-ios/template-tv/package.json @@ -0,0 +1,12 @@ +{ + "name": "rnef-platform-ios-template-tv", + "scripts": { + "tvos": "rnef run:ios:tv" + }, + "dependencies": { + "react-native": "npm:react-native-tvos@0.80.1-0" + }, + "devDependencies": { + "@rnef/platform-ios": "^0.8.6" + } +} diff --git a/packages/platform-ios/template-tv/tvos/.xcode.env b/packages/platform-ios/template-tv/tvos/.xcode.env new file mode 100644 index 000000000..3d5782c71 --- /dev/null +++ b/packages/platform-ios/template-tv/tvos/.xcode.env @@ -0,0 +1,11 @@ +# This `.xcode.env` file is versioned and is used to source the environment +# used when running script phases inside Xcode. +# To customize your local environment, you can create an `.xcode.env.local` +# file that is not versioned. + +# NODE_BINARY variable contains the PATH to the node executable. +# +# Customize the NODE_BINARY variable here. +# For example, to use nvm with brew, add the following line +# . "$(brew --prefix nvm)/nvm.sh" --no-use +export NODE_BINARY=$(command -v node) diff --git a/packages/platform-ios/template-tv/tvos/HelloWorld.xcodeproj/project.pbxproj b/packages/platform-ios/template-tv/tvos/HelloWorld.xcodeproj/project.pbxproj new file mode 100644 index 000000000..00ed1bfae --- /dev/null +++ b/packages/platform-ios/template-tv/tvos/HelloWorld.xcodeproj/project.pbxproj @@ -0,0 +1,475 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 0C80B921A6F3F58F76C31292 /* libPods-HelloWorld.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-HelloWorld.a */; }; + 13B07FBC1A68108700A75B9A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.swift */; }; + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 13B07F961A680F5B00A75B9A /* HelloWorld.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloWorld.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 13B07FB01A68108700A75B9A /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = HelloWorld/AppDelegate.swift; sourceTree = ""; }; + 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = HelloWorld/Images.xcassets; sourceTree = ""; }; + 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = HelloWorld/Info.plist; sourceTree = ""; }; + 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = HelloWorld/PrivacyInfo.xcprivacy; sourceTree = ""; }; + 3B4392A12AC88292D35C810B /* Pods-HelloWorld.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorld.debug.xcconfig"; path = "Target Support Files/Pods-HelloWorld/Pods-HelloWorld.debug.xcconfig"; sourceTree = ""; }; + 5709B34CF0A7D63546082F79 /* Pods-HelloWorld.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorld.release.xcconfig"; path = "Target Support Files/Pods-HelloWorld/Pods-HelloWorld.release.xcconfig"; sourceTree = ""; }; + 5DCACB8F33CDC322A6C60F78 /* libPods-HelloWorld.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-HelloWorld.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = HelloWorld/LaunchScreen.storyboard; sourceTree = ""; }; + ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0C80B921A6F3F58F76C31292 /* libPods-HelloWorld.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 13B07FAE1A68108700A75B9A /* HelloWorld */ = { + isa = PBXGroup; + children = ( + 13B07FB01A68108700A75B9A /* AppDelegate.swift */, + 13B07FB51A68108700A75B9A /* Images.xcassets */, + 13B07FB61A68108700A75B9A /* Info.plist */, + 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */, + 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */, + ); + name = HelloWorld; + sourceTree = ""; + }; + 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { + isa = PBXGroup; + children = ( + ED297162215061F000B7C4FE /* JavaScriptCore.framework */, + 5DCACB8F33CDC322A6C60F78 /* libPods-HelloWorld.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 832341AE1AAA6A7D00B99B32 /* Libraries */ = { + isa = PBXGroup; + children = ( + ); + name = Libraries; + sourceTree = ""; + }; + 83CBB9F61A601CBA00E9B192 = { + isa = PBXGroup; + children = ( + 13B07FAE1A68108700A75B9A /* HelloWorld */, + 832341AE1AAA6A7D00B99B32 /* Libraries */, + 83CBBA001A601CBA00E9B192 /* Products */, + 2D16E6871FA4F8E400B85C8A /* Frameworks */, + BBD78D7AC51CEA395F1C20DB /* Pods */, + ); + indentWidth = 2; + sourceTree = ""; + tabWidth = 2; + usesTabs = 0; + }; + 83CBBA001A601CBA00E9B192 /* Products */ = { + isa = PBXGroup; + children = ( + 13B07F961A680F5B00A75B9A /* HelloWorld.app */, + ); + name = Products; + sourceTree = ""; + }; + BBD78D7AC51CEA395F1C20DB /* Pods */ = { + isa = PBXGroup; + children = ( + 3B4392A12AC88292D35C810B /* Pods-HelloWorld.debug.xcconfig */, + 5709B34CF0A7D63546082F79 /* Pods-HelloWorld.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 13B07F861A680F5B00A75B9A /* HelloWorld */ = { + isa = PBXNativeTarget; + buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "HelloWorld" */; + buildPhases = ( + C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */, + 13B07F871A680F5B00A75B9A /* Sources */, + 13B07F8C1A680F5B00A75B9A /* Frameworks */, + 13B07F8E1A680F5B00A75B9A /* Resources */, + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, + 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */, + E235C05ADACE081382539298 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = HelloWorld; + productName = HelloWorld; + productReference = 13B07F961A680F5B00A75B9A /* HelloWorld.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 83CBB9F71A601CBA00E9B192 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1210; + TargetAttributes = { + 13B07F861A680F5B00A75B9A = { + LastSwiftMigration = 1120; + }; + }; + }; + buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "HelloWorld" */; + compatibilityVersion = "Xcode 12.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 83CBB9F61A601CBA00E9B192; + productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 13B07F861A680F5B00A75B9A /* HelloWorld */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 13B07F8E1A680F5B00A75B9A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */, + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/.xcode.env.local", + "$(SRCROOT)/.xcode.env", + ); + name = "Bundle React Native code and images"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "set -e\nif [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\nsource \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\nsource \"$PODS_ROOT/../.xcode.env.local\"\nfi\nexport CONFIG_CMD=\"dummy-workaround-value\"\nexport CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('@rnef/cli/package.json')) + '/dist/src/bin.js'\")\"\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; + }; + 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-HelloWorld/Pods-HelloWorld-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-HelloWorld/Pods-HelloWorld-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-HelloWorld/Pods-HelloWorld-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-HelloWorld-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-HelloWorld/Pods-HelloWorld-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-HelloWorld/Pods-HelloWorld-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-HelloWorld/Pods-HelloWorld-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 13B07F871A680F5B00A75B9A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 13B07FBC1A68108700A75B9A /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 13B07F941A680F5B00A75B9A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-HelloWorld.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = 1; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = HelloWorld/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + TVOS_DEPLOYMENT_TARGET = 15.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = HelloWorld; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 13B07F951A680F5B00A75B9A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-HelloWorld.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = 1; + INFOPLIST_FILE = HelloWorld/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + TVOS_DEPLOYMENT_TARGET = 15.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = HelloWorld; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + 83CBBA201A601CBA00E9B192 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++20"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + TVOS_DEPLOYMENT_TARGET = 15.1; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); + LIBRARY_SEARCH_PATHS = ( + "\"$(SDKROOT)/usr/lib/swift\"", + "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(inherited)\"", + ); + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DFOLLY_NO_CONFIG", + "-DFOLLY_MOBILE=1", + "-DFOLLY_USE_LIBCPP=1", + "-DFOLLY_CFG_NO_COROUTINES=1", + "-DFOLLY_HAVE_CLOCK_GETTIME=1", + ); + SDKROOT = appletvos; + }; + name = Debug; + }; + 83CBBA211A601CBA00E9B192 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++20"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + TVOS_DEPLOYMENT_TARGET = 15.1; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); + LIBRARY_SEARCH_PATHS = ( + "\"$(SDKROOT)/usr/lib/swift\"", + "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(inherited)\"", + ); + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DFOLLY_NO_CONFIG", + "-DFOLLY_MOBILE=1", + "-DFOLLY_USE_LIBCPP=1", + "-DFOLLY_CFG_NO_COROUTINES=1", + "-DFOLLY_HAVE_CLOCK_GETTIME=1", + ); + SDKROOT = appletvos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "HelloWorld" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 13B07F941A680F5B00A75B9A /* Debug */, + 13B07F951A680F5B00A75B9A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "HelloWorld" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83CBBA201A601CBA00E9B192 /* Debug */, + 83CBBA211A601CBA00E9B192 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; +} diff --git a/packages/platform-ios/template-tv/tvos/HelloWorld.xcodeproj/xcshareddata/xcschemes/HelloWorld.xcscheme b/packages/platform-ios/template-tv/tvos/HelloWorld.xcodeproj/xcshareddata/xcschemes/HelloWorld.xcscheme new file mode 100644 index 000000000..766cb1773 --- /dev/null +++ b/packages/platform-ios/template-tv/tvos/HelloWorld.xcodeproj/xcshareddata/xcschemes/HelloWorld.xcscheme @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/platform-ios/template-tv/tvos/HelloWorld/AppDelegate.swift b/packages/platform-ios/template-tv/tvos/HelloWorld/AppDelegate.swift new file mode 100644 index 000000000..0d3565bd3 --- /dev/null +++ b/packages/platform-ios/template-tv/tvos/HelloWorld/AppDelegate.swift @@ -0,0 +1,48 @@ +import UIKit +import React +import React_RCTAppDelegate +import ReactAppDependencyProvider + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + var window: UIWindow? + + var reactNativeDelegate: ReactNativeDelegate? + var reactNativeFactory: RCTReactNativeFactory? + + func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil + ) -> Bool { + let delegate = ReactNativeDelegate() + let factory = RCTReactNativeFactory(delegate: delegate) + delegate.dependencyProvider = RCTAppDependencyProvider() + + reactNativeDelegate = delegate + reactNativeFactory = factory + + window = UIWindow(frame: UIScreen.main.bounds) + + factory.startReactNative( + withModuleName: "HelloWorld", + in: window, + launchOptions: launchOptions + ) + + return true + } +} + +class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate { + override func sourceURL(for bridge: RCTBridge) -> URL? { + self.bundleURL() + } + + override func bundleURL() -> URL? { +#if DEBUG + RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") +#else + Bundle.main.url(forResource: "main", withExtension: "jsbundle") +#endif + } +} diff --git a/packages/platform-ios/template-tv/tvos/HelloWorld/Images.xcassets/AppIcon.appiconset/Contents.json b/packages/platform-ios/template-tv/tvos/HelloWorld/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..81213230d --- /dev/null +++ b/packages/platform-ios/template-tv/tvos/HelloWorld/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,53 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/packages/platform-ios/template-tv/tvos/HelloWorld/Images.xcassets/Contents.json b/packages/platform-ios/template-tv/tvos/HelloWorld/Images.xcassets/Contents.json new file mode 100644 index 000000000..2d92bd53f --- /dev/null +++ b/packages/platform-ios/template-tv/tvos/HelloWorld/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/platform-ios/template-tv/tvos/HelloWorld/Info.plist b/packages/platform-ios/template-tv/tvos/HelloWorld/Info.plist new file mode 100644 index 000000000..470677157 --- /dev/null +++ b/packages/platform-ios/template-tv/tvos/HelloWorld/Info.plist @@ -0,0 +1,52 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + HelloWorld + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + LSRequiresIPhoneOS + + NSAppTransportSecurity + + + NSAllowsArbitraryLoads + + NSAllowsLocalNetworking + + + NSLocationWhenInUseUsageDescription + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/packages/platform-ios/template-tv/tvos/HelloWorld/LaunchScreen.storyboard b/packages/platform-ios/template-tv/tvos/HelloWorld/LaunchScreen.storyboard new file mode 100644 index 000000000..2605cb433 --- /dev/null +++ b/packages/platform-ios/template-tv/tvos/HelloWorld/LaunchScreen.storyboard @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/platform-ios/template-tv/tvos/HelloWorld/PrivacyInfo.xcprivacy b/packages/platform-ios/template-tv/tvos/HelloWorld/PrivacyInfo.xcprivacy new file mode 100644 index 000000000..41b8317f0 --- /dev/null +++ b/packages/platform-ios/template-tv/tvos/HelloWorld/PrivacyInfo.xcprivacy @@ -0,0 +1,37 @@ + + + + + NSPrivacyAccessedAPITypes + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPITypeReasons + + C617.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPITypeReasons + + CA92.1 + + + + NSPrivacyAccessedAPIType + NSPrivacyAccessedAPICategorySystemBootTime + NSPrivacyAccessedAPITypeReasons + + 35F9.1 + + + + NSPrivacyCollectedDataTypes + + NSPrivacyTracking + + + diff --git a/packages/platform-ios/template-tv/tvos/Podfile b/packages/platform-ios/template-tv/tvos/Podfile new file mode 100644 index 000000000..5516fb02d --- /dev/null +++ b/packages/platform-ios/template-tv/tvos/Podfile @@ -0,0 +1,35 @@ +# Resolve react_native_pods.rb with node to allow for hoisting +require Pod::Executable.execute_command('node', ['-p', + 'require.resolve( + "react-native/scripts/react_native_pods.rb", + {paths: [process.argv[1]]}, + )', __dir__]).strip + +platform :tvos, min_ios_version_supported +prepare_react_native_project! + +linkage = ENV['USE_FRAMEWORKS'] +if linkage != nil + Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green + use_frameworks! :linkage => linkage.to_sym +end + +target 'HelloWorld' do + config = use_native_modules!(['npx', 'rnef', 'config', '-p', 'ios']) + + use_react_native!( + :path => config[:reactNativePath], + # An absolute path to your application root. + :app_path => "#{Pod::Config.instance.installation_root}/.." + ) + + post_install do |installer| + # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + # :ccache_enabled => true + ) + end +end