Routing of React Native 7.4 project initialization without Framework (Expo) and CI/CD setup

Purpose

As of May 2024, the latest version of React Native (7.4) no longer includes instructions for starting a project without Expo at the top level of the Table of Contents. Additionally, setting up a new project with CI/CD can often be a tedious process. This article aims to provide a guide for starting a new project with React Native 7.4 without Expo and setting up CI/CD.
The following descriptions are highly opinionated, so please refer to them with caution. They are intended to provide some ideas if you are just starting to learn React Native.

Initializing the Project

To start a new React Native project without using Expo, you can use the React Native CLI. Open your terminal and run the following command:

1
npx @react-native-community/cli@latest init sampleproject

During the setup, you will be prompted to install CocoaPods if you are planning to run your project directly in Xcode. Select yes when asked:

1
✔ Do you want to install CocoaPods now? Only needed if you run your project in Xcode directly … yes

Running the iOS Project

Navigate to your project’s directory and install the necessary dependencies:

1
2
3
4
5
cd sampleproject
cd ios
bundle install
bundle exec pod install
npm run ios -- --simulator="iPhone 15 Pro"

Running the Android Project

Switch to the Android directory and run your project:

1
2
cd android
npm run android

Scaffold the Project

Installing Navigation Libraries

For navigation within your React Native app, install the following libraries:

1
2
3
4
5
6
npm install @react-navigation/native @react-navigation/stack
npm install react-native-screens react-native-safe-area-context
npm install react-native-gesture-handler
cd ios
bundle install
bundle exec pod install

Setting Up Navigation in the App

Create your main app component with navigation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import React from "react";
import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
import HomeScreen from "./HomeScreen.tsx";
import { SafeAreaProvider } from "react-native-safe-area-context";

const Stack = createStackNavigator();

function App() {
return (
<SafeAreaProvider>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
</NavigationContainer>
</SafeAreaProvider>
);
}

export default App;

Creating the Home Screen

Create a simple Home Screen component:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import React from "react";
import { View, StyleSheet, Text, ScrollView } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";

function HomeScreen({}: { navigation: any }) {
return (
<SafeAreaView style={styles.safeArea}>
<ScrollView contentContainerStyle={styles.container}>
<View style={styles.formContainer}>
<Text>Hello</Text>
</View>
</ScrollView>
</SafeAreaView>
);
}

const styles = StyleSheet.create({
safeArea: {
flex: 1,
},
container: {
flexGrow: 1,
alignItems: "center",
padding: 20,
backgroundColor: "#f4f4f4",
},
description: {
textAlign: "center",
fontSize: 18,
color: "#555",
marginVertical: 20,
},
formContainer: {
width: "80%",
padding: 20,
backgroundColor: "white",
borderRadius: 8,
boxShadow: "0 4px 8px rgba(0,0,0,0.1)",
alignItems: "center",
marginBottom: 20,
},
});

export default HomeScreen;

Configuring iOS Project

Set up a new bundle ID and provisioning profile in your Apple Developer account.
Add the following entries to your Info.plist and change CFBundleDisplayName to your desired app name:

1
2
3
4
<key>UIRequiresFullScreen</key>
<true/>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>

Update PRODUCT_BUNDLE_IDENTIFIER in project.pbxproj to the bundle id and switch to manual signing in Xcode for the release build.

Open xcode and add icon in Images/AppIcon (Choose single file for iOS).

Configuring Android Project

Change applicationId in build.gradle to bundle id.
Add the following in signingConfigs:

1
2
3
4
5
6
7
8
release {
if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) {
storeFile file(MYAPP_UPLOAD_STORE_FILE)
storePassword MYAPP_UPLOAD_STORE_PASSWORD
keyAlias MYAPP_UPLOAD_KEY_ALIAS
keyPassword MYAPP_UPLOAD_KEY_PASSWORD
}
}

Update the buildTypes release section to use the release signing config:

1
signingConfig signingConfigs.release

Update app_name in android/app/src/main/res/values/strings.xml.

Add keystore information to android/gradle.properties and copy my-upload-key.keystore to android/app.

Supporting CI/CD

Copy remote-build (see Ref 1) and Support/ExportOptions.plist to ios and update ExportOptions.plist with your new bundle ID and provisioning profile name:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>destination</key>
<string>export</string>
<key>manageAppVersionAndBuildNumber</key>
<true/>
<key>method</key>
<string>app-store-connect</string>
<key>signingStyle</key>
<string>manual</string>
<key>provisioningProfiles</key>
<dict>
<key>!!! your bundle id</key>
<string>!!! your profile name</string>
</dict>
<key>stripSwiftSymbols</key>
<true/>
<key>teamID</key>
<string>!!! your team id</string>
<key>uploadSymbols</key>
<true/>
</dict>
</plist>

Set up new GitHub Action files for your project to automate the build and deployment process.

References

  1. https://docs.github.com/en/actions/deployment/deploying-xcode-applications/installing-an-apple-certificate-on-macos-runners-for-xcode-development
  2. https://gist.github.com/huynguyencong/004e98e4d9e7671f93fec280ddb7fc18