Skip to content

Commit

Permalink
Support yarn 4.5.0 and start migrating ipad example app to expo
Browse files Browse the repository at this point in the history
  • Loading branch information
hellostu committed Oct 14, 2024
1 parent 5189711 commit 9bbbd92
Show file tree
Hide file tree
Showing 22 changed files with 11,366 additions and 6,688 deletions.
Binary file added .yarn/install-state.gz
Binary file not shown.
925 changes: 925 additions & 0 deletions .yarn/releases/yarn-4.5.0.cjs

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
nodeLinker: node-modules

yarnPath: .yarn/releases/yarn-4.5.0.cjs
1 change: 0 additions & 1 deletion apps/external-display-example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ In the output, you'll find options to open the app in a
- [development build](https://docs.expo.dev/develop/development-builds/introduction/)
- [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/)
- [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/)
- [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo

You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction).

Expand Down
2 changes: 1 addition & 1 deletion apps/external-display-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"expo-system-ui": "~3.0.7",
"react": "18.2.0",
"react-native": "0.74.5",
"react-native-external-display": "0.6.6",
"react-native-external-display": "workspace:*",
"react-native-gesture-handler": "^2.20.0",
"react-native-webview": "^13.12.3"
},
Expand Down
13 changes: 3 additions & 10 deletions apps/external-display-example/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
{
"extends": "expo/tsconfig.base",
"extends": "../../node_modules/expo/tsconfig.base",
"compilerOptions": {
"strict": true,
"paths": {
"@/*": [
"./*"
]
"@/*": ["./*"]
}
},
"include": [
"**/*.ts",
"**/*.tsx",
".expo/types/**/*.ts",
"expo-env.d.ts"
]
"include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"]
}
18 changes: 18 additions & 0 deletions apps/ipad-multiscenes-headless-example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
node_modules/
.expo/
dist/
npm-debug.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
*.orig.*
web-build/

ios/
android/

# macOS
.DS_Store

925 changes: 925 additions & 0 deletions apps/ipad-multiscenes-headless-example/.yarn/releases/yarn-4.5.0.cjs

Large diffs are not rendered by default.

50 changes: 50 additions & 0 deletions apps/ipad-multiscenes-headless-example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# IPadMultiScenesHeadlessExample

## Usage

To use this example, you can use Split View feature by default, or use Stage Manager (iOS 16.0+).

To enable Stage Manager on iPad simulator:

- Option 1 - Setting: Choose Home Screen & Multitasking. Tap Stage Manager at the bottom of the Home Screen & Multitasking screen.
- Option 2 - Command: `xcrun simctl spawn booted defaults write -g SBChamoisWindowingEnabled -bool true`

Please visit [Multiple Scenes support on iPad targets](../../docs/IOSMultipleScenesSupport.md) for more details.

## Expo

This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app).

## Get started

1. Install dependencies

```bash
yarn install
```

2. Run expo prebuild

```bash
yarn expo prebuild --platform ios
```

3. Run the iOS App. Ensure you have an iPad simulator open.

```bash
yarn ios
```

In the output, you'll find options to open the app in a

- [development build](https://docs.expo.dev/develop/development-builds/introduction/)
- [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/)

You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction).

## Learn more

To learn more about developing your project with Expo, look at the following resources:

- [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides).
- [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web.
25 changes: 25 additions & 0 deletions apps/ipad-multiscenes-headless-example/app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"expo": {
"name": "ipad-multiscenes-headless-example",
"slug": "ipad-multiscenes-headless-example",
"version": "1.0.0",
"orientation": "portrait",
"scheme": "myapp",
"userInterfaceStyle": "automatic",
"splash": {
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.externaldisplay.ipad-example",
"infoPlist": {
"UIApplicationSceneManifest": {
"UIApplicationSupportsMultipleScenes": true
},
"UIRequiresFullScreen": false
}
},
"plugins": ["./withMultipleSceneSupport.js"]
}
}
6 changes: 6 additions & 0 deletions apps/ipad-multiscenes-headless-example/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
};
};
5 changes: 5 additions & 0 deletions apps/ipad-multiscenes-headless-example/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { registerRootComponent } from 'expo'

import App from './src/App'

export default registerRootComponent(App)
26 changes: 26 additions & 0 deletions apps/ipad-multiscenes-headless-example/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "ipad-multiscenes-headless-example",
"version": "1.0.0",
"scripts": {
"start": "expo start",
"ios": "expo run:ios",
"android": "expo run:android"
},
"main": "./index.tsx",
"dependencies": {
"expo": "~51.0.28",
"expo-system-ui": "~3.0.7",
"react": "18.2.0",
"react-native": "0.74.5",
"react-native-external-display": "workspace:*",
"react-native-gesture-handler": "^2.20.0",
"react-native-webview": "^13.12.3"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@types/react": "~18.2.45",
"typescript": "~5.3.3"
},
"private": true,
"packageManager": "[email protected]"
}
44 changes: 44 additions & 0 deletions apps/ipad-multiscenes-headless-example/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react'
import { Text } from 'react-native'
import ExternalDisplay, {
useExternalDisplay,
SceneManager,
} from 'react-native-external-display'
import SimpleBrowser from './SimpleBrowser'

export default function App() {
const info = useExternalDisplay()

return Object.keys(info).map((id, index) => {
const screen = info[id]
if (screen.type == SceneManager.types.EXTERNAL_DISPLAY) {
return (
<ExternalDisplay
key={id}
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
}}
fallbackInMainScreen={false}
screen={id}
>
<Text
style={{
textAlign: 'center',
color: '#fff',
verticalAlign: 'middle',
}}
>
I&apos;m External Display!
</Text>
</ExternalDisplay>
)
}
return (
<ExternalDisplay key={id} fallbackInMainScreen={false} screen={id}>
<SimpleBrowser screenId={id} screenIndex={index} screen={screen} />
</ExternalDisplay>
)
})
}
66 changes: 66 additions & 0 deletions apps/ipad-multiscenes-headless-example/src/SimpleBrowser.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React, { useState, useRef } from 'react'
import { View, Text, TextInput, Button } from 'react-native'
import WebView from 'react-native-webview'
import { SceneManager } from 'react-native-external-display'

const defaultURLs = [
'https://reactnative.dev',
'https://google.com',
'https://apple.com',
'https://github.com',
'https://www.npmjs.com',
]

type Props = {
screen: {}
screenId: string
screenIndex: number
}

export default function SimpleBrowser(props: Props) {
const { screenId, screenIndex } = props

const [url, setUrl] = useState(
defaultURLs[Math.floor(Math.random() * defaultURLs.length)],
)
const webview = useRef(null)

return (
<View style={{ flex: 1 }}>
<View style={{ alignItems: 'center', padding: 10 }}>
<Text style={{ color: 'white', fontSize: 20, textAlign: 'center' }}>
{`ID: ${screenId} (${screenIndex})`}
</Text>
</View>

<View style={{ flexDirection: 'row', alignItems: 'center', padding: 4 }}>
<Text style={{ color: 'white' }}>URL:</Text>
<TextInput
style={{ flex: 1, color: 'white' }}
value={url}
onChangeText={setUrl}
autoCapitalize="none"
/>
<Button
title="New Window"
onPress={() => {
SceneManager.requestScene({
userInfo: { testField: 'testValue' },
})
}}
/>
<Button
title="Close"
onPress={() => SceneManager.closeScene(screenId)}
/>
</View>
<WebView
ref={webview}
source={{ uri: url }}
style={{ flex: 1 }}
allowsInlineMediaPlayback
allowsFullscreenVideo={false}
/>
</View>
)
}
10 changes: 10 additions & 0 deletions apps/ipad-multiscenes-headless-example/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../../node_modules/expo/tsconfig.base",
"compilerOptions": {
"strict": true,
"paths": {
"@/*": ["./*"]
}
},
"include": ["**/*.ts", "**/*.tsx"]
}
98 changes: 98 additions & 0 deletions apps/ipad-multiscenes-headless-example/withMultipleSceneSupport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { ConfigPlugin, IOSConfig, BaseMods, withMod } from 'expo/config-plugins'
import fs from 'fs'

/**
* A plugin which adds new base modifiers to the prebuild config.
*/
export function withAppDelegateBaseMod(config) {
return (
BaseMods.withGeneratedBaseMods <
'appDelegate' >
(config,
{
platform: 'ios',
providers: {
// Append a custom rule to supply AppDelegate data to mods on `mods.ios.appDelegate`
appDelegate:
BaseMods.provider <
IOSConfig.Paths.AppDelegateProjectFile >
{
// Get the local filepath that should be passed to the `read` method.
getFilePath({ modRequest: { projectRoot } }) {
return IOSConfig.Paths.getAppDelegateFilePath(projectRoot)
},
// Read the input file from the filesystem.
async read(filePath) {
return IOSConfig.Paths.getFileInfo(filePath)
},
// Write the resulting output to the filesystem.
async write(filePath: string, { modResults: { contents } }) {
// Modify the AppDelegate.m/mm file's contents
const modifiedContents = modifyAppDelegate(contents)
await fs.promises.writeFile(filePath, modifiedContents)
},
},
},
})
)
}

/**
* Function to modify the AppDelegate.m/mm file contents.
*/
function modifyAppDelegate(contents) {
const importStatement = `#import "RNExternalDisplayUtils.h"`

// Add the import statement if it's not already present
if (!contents.includes(importStatement)) {
contents = `${importStatement}\n${contents}`
}

const method = `
- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options API_AVAILABLE(ios(13.0)) {
UISceneConfiguration * configuration =
[RNExternalAppDelegateUtil application:application
configurationForConnectingSceneSession:connectingSceneSession
options:options
sceneOptions:@{
@"headless": @YES
}
];
return configuration;
}
`

// Add the method if it's not already present
if (
!contents.includes(
'application:(UIApplication *)application configurationForConnectingSceneSession',
)
) {
contents = contents.replace('@end', `${method}\n@end`)
}

return contents
}

/**
* (Utility) Provides the AppDelegate file for modification.
*/
export const withAppDelegate: ConfigPlugin<
Mod<IOSConfig.Paths.AppDelegateProjectFile>,
> = (config, action) => {
return withMod(config, {
platform: 'ios',
mod: 'appDelegate',
action,
})
}

// (Example) Log the contents of the AppDelegate mod results.
export const withSimpleAppDelegateMod = (config) => {
return withAppDelegate(config, (config) => {
console.log('modify AppDelegate:', config.modResults)
return config
})
}

export default withAppDelegateBaseMod
Loading

0 comments on commit 9bbbd92

Please sign in to comment.