├── android ├── app │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── values-night │ │ │ │ │ └── colors.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ ├── ic_launcher_round.webp │ │ │ │ │ └── ic_launcher_foreground.webp │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ ├── ic_launcher_round.webp │ │ │ │ │ └── ic_launcher_foreground.webp │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ ├── ic_launcher_round.webp │ │ │ │ │ └── ic_launcher_foreground.webp │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ ├── ic_launcher_round.webp │ │ │ │ │ └── ic_launcher_foreground.webp │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ ├── ic_launcher_round.webp │ │ │ │ │ └── ic_launcher_foreground.webp │ │ │ │ ├── drawable-hdpi │ │ │ │ │ └── splashscreen_logo.png │ │ │ │ ├── drawable-mdpi │ │ │ │ │ └── splashscreen_logo.png │ │ │ │ ├── drawable-xhdpi │ │ │ │ │ └── splashscreen_logo.png │ │ │ │ ├── drawable-xxhdpi │ │ │ │ │ └── splashscreen_logo.png │ │ │ │ ├── drawable-xxxhdpi │ │ │ │ │ └── splashscreen_logo.png │ │ │ │ ├── values │ │ │ │ │ ├── colors.xml │ │ │ │ │ ├── strings.xml │ │ │ │ │ └── styles.xml │ │ │ │ ├── drawable │ │ │ │ │ ├── ic_launcher_background.xml │ │ │ │ │ └── rn_edit_text_material.xml │ │ │ │ └── mipmap-anydpi-v26 │ │ │ │ │ ├── ic_launcher.xml │ │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── cqjack │ │ │ │ └── expoappboilerplate │ │ │ │ ├── MainApplication.kt │ │ │ │ └── MainActivity.kt │ │ └── debug │ │ │ └── AndroidManifest.xml │ ├── debug.keystore │ ├── proguard-rules.pro │ └── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── .gitignore ├── settings.gradle ├── build.gradle ├── gradle.properties ├── gradlew.bat └── gradlew ├── hooks ├── useColorScheme.ts ├── useColorScheme.web.ts ├── useThemeColor.ts └── useSuperwall.ts ├── assets ├── images │ ├── icon.png │ ├── favicon.png │ ├── react-logo.png │ ├── adaptive-icon.png │ ├── react-logo@2x.png │ ├── react-logo@3x.png │ ├── splash-icon.png │ └── partial-react-logo.png └── fonts │ └── SpaceMono-Regular.ttf ├── ios ├── expoappboilerplate │ ├── Images.xcassets │ │ ├── Contents.json │ │ ├── SplashScreenLogo.imageset │ │ │ ├── image.png │ │ │ ├── image@2x.png │ │ │ ├── image@3x.png │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ ├── App-Icon-1024x1024@1x.png │ │ │ └── Contents.json │ │ └── SplashScreenBackground.colorset │ │ │ └── Contents.json │ ├── noop-file.swift │ ├── expoappboilerplate-Bridging-Header.h │ ├── AppDelegate.h │ ├── expoappboilerplate.entitlements │ ├── main.m │ ├── Supporting │ │ └── Expo.plist │ ├── PrivacyInfo.xcprivacy │ ├── AppDelegate.mm │ ├── Info.plist │ └── SplashScreen.storyboard ├── Podfile.properties.json ├── expoappboilerplate.xcworkspace │ └── contents.xcworkspacedata ├── .gitignore ├── .xcode.env ├── Podfile └── expoappboilerplate.xcodeproj │ ├── xcshareddata │ └── xcschemes │ │ └── expoappboilerplate.xcscheme │ └── project.pbxproj ├── types ├── env.d.ts └── superwall.ts ├── components ├── ui │ ├── TabBarBackground.tsx │ ├── IconSymbol.ios.tsx │ ├── TabBarBackground.ios.tsx │ └── IconSymbol.tsx ├── __tests__ │ ├── ThemedText-test.tsx │ └── __snapshots__ │ │ └── ThemedText-test.tsx.snap ├── ThemedView.tsx ├── HapticTab.tsx ├── ExternalLink.tsx ├── PaywallButton.tsx ├── HelloWave.tsx ├── Collapsible.tsx ├── ThemedText.tsx └── ParallaxScrollView.tsx ├── .env ├── .env.example ├── app ├── onboarding │ ├── _layout.tsx │ ├── index.tsx │ ├── final.tsx │ ├── solution.tsx │ ├── features.tsx │ └── problem.tsx ├── +not-found.tsx ├── (tabs) │ ├── _layout.tsx │ ├── index.tsx │ └── explore.tsx └── _layout.tsx ├── tsconfig.json ├── .gitignore ├── config └── superwall.ts ├── constants └── Colors.ts ├── eas.json ├── .cursorrules ├── app.json ├── README.md ├── package.json ├── services └── superwall.ts ├── contexts └── OnboardingContext.tsx └── scripts └── reset-project.js /android/app/src/main/res/values-night/colors.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /hooks/useColorScheme.ts: -------------------------------------------------------------------------------- 1 | export { useColorScheme } from 'react-native'; 2 | -------------------------------------------------------------------------------- /assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/assets/images/icon.png -------------------------------------------------------------------------------- /android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/android/app/debug.keystore -------------------------------------------------------------------------------- /assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/assets/images/favicon.png -------------------------------------------------------------------------------- /assets/images/react-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/assets/images/react-logo.png -------------------------------------------------------------------------------- /assets/images/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/assets/images/adaptive-icon.png -------------------------------------------------------------------------------- /assets/images/react-logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/assets/images/react-logo@2x.png -------------------------------------------------------------------------------- /assets/images/react-logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/assets/images/react-logo@3x.png -------------------------------------------------------------------------------- /assets/images/splash-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/assets/images/splash-icon.png -------------------------------------------------------------------------------- /assets/fonts/SpaceMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/assets/fonts/SpaceMono-Regular.ttf -------------------------------------------------------------------------------- /assets/images/partial-react-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/assets/images/partial-react-logo.png -------------------------------------------------------------------------------- /ios/expoappboilerplate/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "expo" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /ios/Podfile.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo.jsEngine": "hermes", 3 | "EX_DEV_CLIENT_NETWORK_INSPECTOR": "true", 4 | "newArchEnabled": "true" 5 | } 6 | -------------------------------------------------------------------------------- /ios/expoappboilerplate/noop-file.swift: -------------------------------------------------------------------------------- 1 | // 2 | // @generated 3 | // A blank Swift file must be created for native modules with Swift files to work correctly. 4 | // 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /types/env.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@env' { 2 | export const EXPO_PUBLIC_SUPERWALL_API_KEY_IOS: string; 3 | export const EXPO_PUBLIC_SUPERWALL_API_KEY_ANDROID: string; 4 | } -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /ios/expoappboilerplate/expoappboilerplate-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/splashscreen_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/android/app/src/main/res/drawable-hdpi/splashscreen_logo.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/splashscreen_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/android/app/src/main/res/drawable-mdpi/splashscreen_logo.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/android/app/src/main/res/drawable-xhdpi/splashscreen_logo.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/splashscreen_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/android/app/src/main/res/drawable-xxhdpi/splashscreen_logo.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/splashscreen_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/android/app/src/main/res/drawable-xxxhdpi/splashscreen_logo.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /ios/expoappboilerplate/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | 5 | @interface AppDelegate : EXAppDelegateWrapper 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp -------------------------------------------------------------------------------- /components/ui/TabBarBackground.tsx: -------------------------------------------------------------------------------- 1 | // This is a shim for web and Android where the tab bar is generally opaque. 2 | export default undefined; 3 | 4 | export function useBottomTabOverflow() { 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /ios/expoappboilerplate/Images.xcassets/SplashScreenLogo.imageset/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/ios/expoappboilerplate/Images.xcassets/SplashScreenLogo.imageset/image.png -------------------------------------------------------------------------------- /ios/expoappboilerplate/Images.xcassets/SplashScreenLogo.imageset/image@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/ios/expoappboilerplate/Images.xcassets/SplashScreenLogo.imageset/image@2x.png -------------------------------------------------------------------------------- /ios/expoappboilerplate/Images.xcassets/SplashScreenLogo.imageset/image@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/ios/expoappboilerplate/Images.xcassets/SplashScreenLogo.imageset/image@3x.png -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Android/IntelliJ 6 | # 7 | build/ 8 | .idea 9 | .gradle 10 | local.properties 11 | *.iml 12 | *.hprof 13 | .cxx/ 14 | 15 | # Bundle artifacts 16 | *.jsbundle 17 | -------------------------------------------------------------------------------- /ios/expoappboilerplate/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jackfriks/expo-app-boilerplate/HEAD/ios/expoappboilerplate/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/expoappboilerplate/expoappboilerplate.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | #ffffff 3 | #ffffff 4 | #023c69 5 | #ffffff 6 | -------------------------------------------------------------------------------- /ios/expoappboilerplate/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /types/superwall.ts: -------------------------------------------------------------------------------- 1 | export type SubscriptionStatus = 'active' | 'inactive' | 'unknown'; 2 | 3 | export interface SuperwallConfig { 4 | apiKey: string; 5 | debugMode?: boolean; 6 | options?: { 7 | logging?: boolean; 8 | paywallTimeout?: number; 9 | }; 10 | } -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ios/expoappboilerplate.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/expoappboilerplate/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "App-Icon-1024x1024@1x.png", 5 | "idiom": "universal", 6 | "platform": "ios", 7 | "size": "1024x1024" 8 | } 9 | ], 10 | "info": { 11 | "version": 1, 12 | "author": "expo" 13 | } 14 | } -------------------------------------------------------------------------------- /components/__tests__/ThemedText-test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | 4 | import { ThemedText } from '../ThemedText'; 5 | 6 | it(`renders correctly`, () => { 7 | const tree = renderer.create(Snapshot test!).toJSON(); 8 | 9 | expect(tree).toMatchSnapshot(); 10 | }); 11 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | # ===================================================================== 2 | # SUPERWALL PUBLIC API KEYS GO HERE 3 | # -- find these in your superwall project settings (iOS + Android) 4 | # ===================================================================== 5 | 6 | EXPO_PUBLIC_SUPERWALL_API_KEY_IOS=pk_20e80369f45370cb87384a63ee08add892019cf66cec499a 7 | EXPO_PUBLIC_SUPERWALL_API_KEY_ANDROID= -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # ===================================================================== 5 | # SUPERWALL PUBLIC API KEYS GO HERE 6 | # -- find these in your superwall project settings (iOS + Android) 7 | # ===================================================================== 8 | EXPO_PUBLIC_SUPERWALL_API_KEY_IOS=your_ios_api_key 9 | EXPO_PUBLIC_SUPERWALL_API_KEY_ANDROID=your_android_api_key -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | expo-app-boilerplate 3 | automatic 4 | contain 5 | false 6 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/expoappboilerplate/Supporting/Expo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | EXUpdatesCheckOnLaunch 6 | ALWAYS 7 | EXUpdatesEnabled 8 | 9 | EXUpdatesLaunchWaitMs 10 | 0 11 | 12 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | .xcode.env.local 25 | 26 | # Bundle artifacts 27 | *.jsbundle 28 | 29 | # CocoaPods 30 | /Pods/ 31 | -------------------------------------------------------------------------------- /components/__tests__/__snapshots__/ThemedText-test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`renders correctly 1`] = ` 4 | 22 | Snapshot test! 23 | 24 | `; 25 | -------------------------------------------------------------------------------- /ios/expoappboilerplate/Images.xcassets/SplashScreenBackground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors": [ 3 | { 4 | "color": { 5 | "components": { 6 | "alpha": "1.000", 7 | "blue": "1.00000000000000", 8 | "green": "1.00000000000000", 9 | "red": "1.00000000000000" 10 | }, 11 | "color-space": "srgb" 12 | }, 13 | "idiom": "universal" 14 | } 15 | ], 16 | "info": { 17 | "version": 1, 18 | "author": "expo" 19 | } 20 | } -------------------------------------------------------------------------------- /ios/expoappboilerplate/Images.xcassets/SplashScreenLogo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "idiom": "universal", 5 | "filename": "image.png", 6 | "scale": "1x" 7 | }, 8 | { 9 | "idiom": "universal", 10 | "filename": "image@2x.png", 11 | "scale": "2x" 12 | }, 13 | { 14 | "idiom": "universal", 15 | "filename": "image@3x.png", 16 | "scale": "3x" 17 | } 18 | ], 19 | "info": { 20 | "version": 1, 21 | "author": "expo" 22 | } 23 | } -------------------------------------------------------------------------------- /ios/.xcode.env: -------------------------------------------------------------------------------- 1 | # This `.xcode.env` file is versioned and is used to source the environment 2 | # used when running script phases inside Xcode. 3 | # To customize your local environment, you can create an `.xcode.env.local` 4 | # file that is not versioned. 5 | 6 | # NODE_BINARY variable contains the PATH to the node executable. 7 | # 8 | # Customize the NODE_BINARY variable here. 9 | # For example, to use nvm with brew, add the following line 10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use 11 | export NODE_BINARY=$(command -v node) 12 | -------------------------------------------------------------------------------- /app/onboarding/_layout.tsx: -------------------------------------------------------------------------------- 1 | import { Stack } from 'expo-router'; 2 | import { SafeAreaProvider } from 'react-native-safe-area-context'; 3 | 4 | export default function Layout() { 5 | return ( 6 | 7 | 17 | 18 | ); 19 | } -------------------------------------------------------------------------------- /components/ThemedView.tsx: -------------------------------------------------------------------------------- 1 | import { View, type ViewProps } from 'react-native'; 2 | 3 | import { useThemeColor } from '@/hooks/useThemeColor'; 4 | 5 | export type ThemedViewProps = ViewProps & { 6 | lightColor?: string; 7 | darkColor?: string; 8 | }; 9 | 10 | export function ThemedView({ style, lightColor, darkColor, ...otherProps }: ThemedViewProps) { 11 | const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background'); 12 | 13 | return ; 14 | } 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "expo/tsconfig.base", 3 | "compilerOptions": { 4 | "strict": true, 5 | "paths": { 6 | "@/*": ["./*"], 7 | "@components/*": ["./components/*"], 8 | "@utils/*": ["./utils/*"], 9 | "@constants/*": ["./constants/*"], 10 | "@types/*": ["./types/*"] 11 | }, 12 | "jsx": "react-jsx", 13 | "baseUrl": ".", 14 | "types": ["node"] 15 | }, 16 | "include": [ 17 | "**/*.ts", 18 | "**/*.tsx", 19 | ".expo/types/**/*.ts", 20 | "expo-env.d.ts" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files 2 | 3 | # dependencies 4 | node_modules/ 5 | 6 | # Expo 7 | .expo/ 8 | dist/ 9 | web-build/ 10 | expo-env.d.ts 11 | 12 | # Native 13 | *.orig.* 14 | *.jks 15 | *.p8 16 | *.p12 17 | *.key 18 | *.mobileprovision 19 | 20 | # Metro 21 | .metro-health-check* 22 | 23 | # debug 24 | npm-debug.* 25 | yarn-debug.* 26 | yarn-error.* 27 | 28 | # macOS 29 | .DS_Store 30 | *.pem 31 | 32 | # local env files 33 | .env*.local 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | 38 | app-example 39 | -------------------------------------------------------------------------------- /hooks/useColorScheme.web.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { useColorScheme as useRNColorScheme } from 'react-native'; 3 | 4 | /** 5 | * To support static rendering, this value needs to be re-calculated on the client side for web 6 | */ 7 | export function useColorScheme() { 8 | const [hasHydrated, setHasHydrated] = useState(false); 9 | 10 | useEffect(() => { 11 | setHasHydrated(true); 12 | }, []); 13 | 14 | const colorScheme = useRNColorScheme(); 15 | 16 | if (hasHydrated) { 17 | return colorScheme; 18 | } 19 | 20 | return 'light'; 21 | } 22 | -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # react-native-reanimated 11 | -keep class com.swmansion.reanimated.** { *; } 12 | -keep class com.facebook.react.turbomodule.** { *; } 13 | 14 | # Add any project specific keep options here: 15 | -------------------------------------------------------------------------------- /hooks/useThemeColor.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Learn more about light and dark modes: 3 | * https://docs.expo.dev/guides/color-schemes/ 4 | */ 5 | 6 | import { Colors } from '@/constants/Colors'; 7 | import { useColorScheme } from '@/hooks/useColorScheme'; 8 | 9 | export function useThemeColor( 10 | props: { light?: string; dark?: string }, 11 | colorName: keyof typeof Colors.light & keyof typeof Colors.dark 12 | ) { 13 | const theme = useColorScheme() ?? 'light'; 14 | const colorFromProps = props[theme]; 15 | 16 | if (colorFromProps) { 17 | return colorFromProps; 18 | } else { 19 | return Colors[theme][colorName]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /components/HapticTab.tsx: -------------------------------------------------------------------------------- 1 | import { BottomTabBarButtonProps } from '@react-navigation/bottom-tabs'; 2 | import { PlatformPressable } from '@react-navigation/elements'; 3 | import * as Haptics from 'expo-haptics'; 4 | 5 | export function HapticTab(props: BottomTabBarButtonProps) { 6 | return ( 7 | { 10 | if (process.env.EXPO_OS === 'ios') { 11 | // Add a soft haptic feedback when pressing down on the tabs. 12 | Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); 13 | } 14 | props.onPressIn?.(ev); 15 | }} 16 | /> 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /config/superwall.ts: -------------------------------------------------------------------------------- 1 | import { SuperwallOptions, LogLevel, LogScope } from '@superwall/react-native-superwall'; 2 | 3 | export const SUPERWALL_TRIGGERS = { 4 | ONBOARDING: 'campaign_trigger', 5 | FEATURE_UNLOCK: 'campaign_trigger', 6 | // Add more triggers as needed 7 | } as const; 8 | 9 | export const createSuperwallConfig = () => { 10 | const options = new SuperwallOptions(); 11 | 12 | // Enable debug logging in development 13 | if (__DEV__) { 14 | options.logging.level = LogLevel.Debug; 15 | options.logging.scopes = [ 16 | LogScope.PaywallPresentation, 17 | LogScope.PaywallTransactions, 18 | LogScope.Network, 19 | ]; 20 | } 21 | 22 | return options; 23 | }; -------------------------------------------------------------------------------- /components/ui/IconSymbol.ios.tsx: -------------------------------------------------------------------------------- 1 | import { SymbolView, SymbolViewProps, SymbolWeight } from 'expo-symbols'; 2 | import { StyleProp, ViewStyle } from 'react-native'; 3 | 4 | export function IconSymbol({ 5 | name, 6 | size = 24, 7 | color, 8 | style, 9 | weight = 'regular', 10 | }: { 11 | name: SymbolViewProps['name']; 12 | size?: number; 13 | color: string; 14 | style?: StyleProp; 15 | weight?: SymbolWeight; 16 | }) { 17 | return ( 18 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /components/ExternalLink.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from 'expo-router'; 2 | import { openBrowserAsync } from 'expo-web-browser'; 3 | import { type ComponentProps } from 'react'; 4 | import { Platform } from 'react-native'; 5 | 6 | type Props = Omit, 'href'> & { href: string }; 7 | 8 | export function ExternalLink({ href, ...rest }: Props) { 9 | return ( 10 | { 15 | if (Platform.OS !== 'web') { 16 | // Prevent the default behavior of linking to the default browser on native. 17 | event.preventDefault(); 18 | // Open the link in an in-app browser. 19 | await openBrowserAsync(href); 20 | } 21 | }} 22 | /> 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /components/ui/TabBarBackground.ios.tsx: -------------------------------------------------------------------------------- 1 | import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs'; 2 | import { BlurView } from 'expo-blur'; 3 | import { StyleSheet } from 'react-native'; 4 | import { useSafeAreaInsets } from 'react-native-safe-area-context'; 5 | 6 | export default function BlurTabBarBackground() { 7 | return ( 8 | 15 | ); 16 | } 17 | 18 | export function useBottomTabOverflow() { 19 | const tabHeight = useBottomTabBarHeight(); 20 | const { bottom } = useSafeAreaInsets(); 21 | return tabHeight - bottom; 22 | } 23 | -------------------------------------------------------------------------------- /constants/Colors.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Below are the colors that are used in the app. The colors are defined in the light and dark mode. 3 | * There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc. 4 | */ 5 | 6 | const tintColorLight = '#0a7ea4'; 7 | const tintColorDark = '#fff'; 8 | 9 | export const Colors = { 10 | light: { 11 | text: '#11181C', 12 | background: '#fff', 13 | tint: tintColorLight, 14 | icon: '#687076', 15 | tabIconDefault: '#687076', 16 | tabIconSelected: tintColorLight, 17 | }, 18 | dark: { 19 | text: '#ECEDEE', 20 | background: '#151718', 21 | tint: tintColorDark, 22 | icon: '#9BA1A6', 23 | tabIconDefault: '#9BA1A6', 24 | tabIconSelected: tintColorDark, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /components/PaywallButton.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet } from 'react-native'; 2 | import { TouchableOpacity } from 'react-native-gesture-handler'; 3 | import { ThemedText } from './ThemedText'; 4 | import { useSuperwall } from '@/hooks/useSuperwall'; 5 | import { SUPERWALL_TRIGGERS } from '@/config/superwall'; 6 | 7 | export function PaywallButton() { 8 | const { showPaywall } = useSuperwall(); 9 | 10 | const handlePress = () => { 11 | showPaywall(SUPERWALL_TRIGGERS.ONBOARDING); 12 | }; 13 | 14 | return ( 15 | 16 | Show Paywall 17 | 18 | ); 19 | } 20 | 21 | const styles = StyleSheet.create({ 22 | button: { 23 | padding: 16, 24 | borderRadius: 8, 25 | backgroundColor: '#0a7ea4', 26 | alignItems: 'center', 27 | }, 28 | }); -------------------------------------------------------------------------------- /app/+not-found.tsx: -------------------------------------------------------------------------------- 1 | import { Link, Stack } from 'expo-router'; 2 | import { StyleSheet } from 'react-native'; 3 | 4 | import { ThemedText } from '@/components/ThemedText'; 5 | import { ThemedView } from '@/components/ThemedView'; 6 | 7 | export default function NotFoundScreen() { 8 | return ( 9 | <> 10 | 11 | 12 | This screen doesn't exist. 13 | 14 | Go to home screen! 15 | 16 | 17 | 18 | ); 19 | } 20 | 21 | const styles = StyleSheet.create({ 22 | container: { 23 | flex: 1, 24 | alignItems: 'center', 25 | justifyContent: 'center', 26 | padding: 20, 27 | }, 28 | link: { 29 | marginTop: 15, 30 | paddingVertical: 15, 31 | }, 32 | }); 33 | -------------------------------------------------------------------------------- /eas.json: -------------------------------------------------------------------------------- 1 | { 2 | "cli": { 3 | "version": ">= 15.0.10", 4 | "appVersionSource": "remote" 5 | }, 6 | "build": { 7 | "development": { 8 | "developmentClient": true, 9 | "distribution": "internal", 10 | "ios": { 11 | "simulator": true 12 | }, 13 | "env": { 14 | "EXPO_PUBLIC_SUPERWALL_API_KEY_IOS": "your_ios_api_key", 15 | "EXPO_PUBLIC_SUPERWALL_API_KEY_ANDROID": "your_android_key_here" 16 | } 17 | }, 18 | "preview": { 19 | "distribution": "internal", 20 | "env": { 21 | "EXPO_PUBLIC_SUPERWALL_API_KEY_IOS": "your_ios_api_key", 22 | "EXPO_PUBLIC_SUPERWALL_API_KEY_ANDROID": "your_android_key_here" 23 | } 24 | }, 25 | "production": { 26 | "autoIncrement": true, 27 | "env": { 28 | "EXPO_PUBLIC_SUPERWALL_API_KEY_IOS": "your_ios_api_key", 29 | "EXPO_PUBLIC_SUPERWALL_API_KEY_ANDROID": "your_android_key_here" 30 | } 31 | } 32 | }, 33 | "submit": { 34 | "production": {} 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.cursorrules: -------------------------------------------------------------------------------- 1 | // React Native Expo .cursorrules 2 | 3 | // React Native Expo best practices 4 | 5 | const reactNativeExpoBestPractices = [ 6 | "Use functional components with hooks", 7 | "Utilize Expo SDK features and APIs", 8 | "Implement proper navigation using React Navigation", 9 | "Use Expo's asset system for images and fonts", 10 | "Implement proper error handling and crash reporting", 11 | "Utilize Expo's push notification system", 12 | ]; 13 | 14 | // Folder structure 15 | 16 | const folderStructure = ` 17 | assets/ 18 | src/ 19 | components/ 20 | screens/ 21 | navigation/ 22 | hooks/ 23 | utils/ 24 | App.js 25 | app.json 26 | `; 27 | 28 | // Additional instructions 29 | 30 | const additionalInstructions = ` 31 | 1. Use TypeScript for type safety 32 | 2. Implement proper styling using StyleSheet 33 | 3. Utilize Expo's vector icons 34 | 4. Use Expo's secure store for sensitive data 35 | 5. Implement proper offline support 36 | 6. Follow React Native best practices for performance 37 | 7. Use Expo's OTA updates for quick deployments 38 | `; 39 | -------------------------------------------------------------------------------- /components/HelloWave.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { StyleSheet } from 'react-native'; 3 | import Animated, { 4 | useSharedValue, 5 | useAnimatedStyle, 6 | withTiming, 7 | withRepeat, 8 | withSequence, 9 | } from 'react-native-reanimated'; 10 | 11 | import { ThemedText } from '@/components/ThemedText'; 12 | 13 | export function HelloWave() { 14 | const rotationAnimation = useSharedValue(0); 15 | 16 | useEffect(() => { 17 | rotationAnimation.value = withRepeat( 18 | withSequence(withTiming(25, { duration: 150 }), withTiming(0, { duration: 150 })), 19 | 4 // Run the animation 4 times 20 | ); 21 | }, []); 22 | 23 | const animatedStyle = useAnimatedStyle(() => ({ 24 | transform: [{ rotate: `${rotationAnimation.value}deg` }], 25 | })); 26 | 27 | return ( 28 | 29 | 👋 30 | 31 | ); 32 | } 33 | 34 | const styles = StyleSheet.create({ 35 | text: { 36 | fontSize: 28, 37 | lineHeight: 32, 38 | marginTop: -6, 39 | }, 40 | }); 41 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 14 | 19 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "expo-app-boilerplate", 4 | "slug": "expo-app-boilerplate", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/images/icon.png", 8 | "scheme": "myapp", 9 | "userInterfaceStyle": "automatic", 10 | "newArchEnabled": true, 11 | "ios": { 12 | "supportsTablet": true, 13 | "bundleIdentifier": "com.cqjack.expoappboilerplate", 14 | "infoPlist": { 15 | "ITSAppUsesNonExemptEncryption": false 16 | } 17 | }, 18 | "android": { 19 | "adaptiveIcon": { 20 | "foregroundImage": "./assets/images/adaptive-icon.png", 21 | "backgroundColor": "#ffffff" 22 | }, 23 | "package": "com.cqjack.expoappboilerplate" 24 | }, 25 | "web": { 26 | "bundler": "metro", 27 | "output": "static", 28 | "favicon": "./assets/images/favicon.png" 29 | }, 30 | "plugins": [ 31 | "expo-router", 32 | [ 33 | "expo-splash-screen", 34 | { 35 | "image": "./assets/images/splash-icon.png", 36 | "imageWidth": 200, 37 | "resizeMode": "contain", 38 | "backgroundColor": "#ffffff" 39 | } 40 | ] 41 | ], 42 | "experiments": { 43 | "typedRoutes": true 44 | }, 45 | "extra": { 46 | "router": { 47 | "origin": false 48 | }, 49 | "eas": { 50 | "projectId": "e55fcdea-13e2-4c3b-a4c3-59c174184eb5" 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ios/expoappboilerplate/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyAccessedAPITypes 6 | 7 | 8 | NSPrivacyAccessedAPIType 9 | NSPrivacyAccessedAPICategoryUserDefaults 10 | NSPrivacyAccessedAPITypeReasons 11 | 12 | CA92.1 13 | 14 | 15 | 16 | NSPrivacyAccessedAPIType 17 | NSPrivacyAccessedAPICategoryFileTimestamp 18 | NSPrivacyAccessedAPITypeReasons 19 | 20 | 0A2A.1 21 | 3B52.1 22 | C617.1 23 | 24 | 25 | 26 | NSPrivacyAccessedAPIType 27 | NSPrivacyAccessedAPICategoryDiskSpace 28 | NSPrivacyAccessedAPITypeReasons 29 | 30 | E174.1 31 | 85F4.1 32 | 33 | 34 | 35 | NSPrivacyAccessedAPIType 36 | NSPrivacyAccessedAPICategorySystemBootTime 37 | NSPrivacyAccessedAPITypeReasons 38 | 39 | 35F9.1 40 | 41 | 42 | 43 | NSPrivacyCollectedDataTypes 44 | 45 | NSPrivacyTracking 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /app/(tabs)/_layout.tsx: -------------------------------------------------------------------------------- 1 | import { Tabs } from 'expo-router'; 2 | import React from 'react'; 3 | import { Platform } from 'react-native'; 4 | 5 | import { HapticTab } from '@/components/HapticTab'; 6 | import { IconSymbol } from '@/components/ui/IconSymbol'; 7 | import TabBarBackground from '@/components/ui/TabBarBackground'; 8 | import { Colors } from '@/constants/Colors'; 9 | import { useColorScheme } from '@/hooks/useColorScheme'; 10 | 11 | export default function TabLayout() { 12 | const colorScheme = useColorScheme(); 13 | 14 | return ( 15 | 29 | , 34 | }} 35 | /> 36 | , 41 | }} 42 | /> 43 | 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /components/ui/IconSymbol.tsx: -------------------------------------------------------------------------------- 1 | // This file is a fallback for using MaterialIcons on Android and web. 2 | 3 | import MaterialIcons from '@expo/vector-icons/MaterialIcons'; 4 | import { SymbolWeight } from 'expo-symbols'; 5 | import React from 'react'; 6 | import { OpaqueColorValue, StyleProp, ViewStyle } from 'react-native'; 7 | 8 | // Add your SFSymbol to MaterialIcons mappings here. 9 | const MAPPING = { 10 | // See MaterialIcons here: https://icons.expo.fyi 11 | // See SF Symbols in the SF Symbols app on Mac. 12 | 'house.fill': 'home', 13 | 'paperplane.fill': 'send', 14 | 'chevron.left.forwardslash.chevron.right': 'code', 15 | 'chevron.right': 'chevron-right', 16 | } as Partial< 17 | Record< 18 | import('expo-symbols').SymbolViewProps['name'], 19 | React.ComponentProps['name'] 20 | > 21 | >; 22 | 23 | export type IconSymbolName = keyof typeof MAPPING; 24 | 25 | /** 26 | * An icon component that uses native SFSymbols on iOS, and MaterialIcons on Android and web. This ensures a consistent look across platforms, and optimal resource usage. 27 | * 28 | * Icon `name`s are based on SFSymbols and require manual mapping to MaterialIcons. 29 | */ 30 | export function IconSymbol({ 31 | name, 32 | size = 24, 33 | color, 34 | style, 35 | }: { 36 | name: IconSymbolName; 37 | size?: number; 38 | color: string | OpaqueColorValue; 39 | style?: StyleProp; 40 | weight?: SymbolWeight; 41 | }) { 42 | return ; 43 | } 44 | -------------------------------------------------------------------------------- /hooks/useSuperwall.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { Platform } from 'react-native'; 3 | import { SubscriptionStatus } from '@superwall/react-native-superwall'; 4 | import { superwallService } from '@/services/superwall'; 5 | 6 | export function useSuperwall() { 7 | const [isSubscribed, setIsSubscribed] = useState(false); 8 | const [isLoading, setIsLoading] = useState(true); 9 | 10 | useEffect(() => { 11 | if (Platform.OS === 'web') { 12 | setIsLoading(false); 13 | return; 14 | } 15 | 16 | superwallService.initialize(); 17 | checkSubscription(); 18 | }, []); 19 | 20 | const checkSubscription = async () => { 21 | try { 22 | const status = await superwallService.getSubscriptionStatus(); 23 | setIsSubscribed(status === SubscriptionStatus.ACTIVE); 24 | } catch (error) { 25 | console.error('[Superwall] Hook subscription check failed:', error); 26 | } finally { 27 | setIsLoading(false); 28 | } 29 | }; 30 | 31 | const showPaywall = async (triggerId: string) => { 32 | if (isLoading || Platform.OS === 'web') return; 33 | 34 | try { 35 | await superwallService.presentPaywall(triggerId); 36 | // Refresh subscription status after paywall interaction 37 | await checkSubscription(); 38 | } catch (error) { 39 | console.error('[Superwall] Hook failed to show paywall:', error); 40 | } 41 | }; 42 | 43 | return { 44 | isSubscribed, 45 | isLoading, 46 | showPaywall, 47 | checkSubscription, 48 | }; 49 | } -------------------------------------------------------------------------------- /components/Collapsible.tsx: -------------------------------------------------------------------------------- 1 | import { PropsWithChildren, useState } from 'react'; 2 | import { StyleSheet, TouchableOpacity } from 'react-native'; 3 | 4 | import { ThemedText } from '@/components/ThemedText'; 5 | import { ThemedView } from '@/components/ThemedView'; 6 | import { IconSymbol } from '@/components/ui/IconSymbol'; 7 | import { Colors } from '@/constants/Colors'; 8 | import { useColorScheme } from '@/hooks/useColorScheme'; 9 | 10 | export function Collapsible({ children, title }: PropsWithChildren & { title: string }) { 11 | const [isOpen, setIsOpen] = useState(false); 12 | const theme = useColorScheme() ?? 'light'; 13 | 14 | return ( 15 | 16 | setIsOpen((value) => !value)} 19 | activeOpacity={0.8}> 20 | 27 | 28 | {title} 29 | 30 | {isOpen && {children}} 31 | 32 | ); 33 | } 34 | 35 | const styles = StyleSheet.create({ 36 | heading: { 37 | flexDirection: 'row', 38 | alignItems: 'center', 39 | gap: 6, 40 | }, 41 | content: { 42 | marginTop: 6, 43 | marginLeft: 24, 44 | }, 45 | }); 46 | -------------------------------------------------------------------------------- /components/ThemedText.tsx: -------------------------------------------------------------------------------- 1 | import { Text, type TextProps, StyleSheet } from 'react-native'; 2 | 3 | import { useThemeColor } from '@/hooks/useThemeColor'; 4 | 5 | export type ThemedTextProps = TextProps & { 6 | lightColor?: string; 7 | darkColor?: string; 8 | type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link'; 9 | }; 10 | 11 | export function ThemedText({ 12 | style, 13 | lightColor, 14 | darkColor, 15 | type = 'default', 16 | ...rest 17 | }: ThemedTextProps) { 18 | const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text'); 19 | 20 | return ( 21 | 33 | ); 34 | } 35 | 36 | const styles = StyleSheet.create({ 37 | default: { 38 | fontSize: 16, 39 | lineHeight: 24, 40 | }, 41 | defaultSemiBold: { 42 | fontSize: 16, 43 | lineHeight: 24, 44 | fontWeight: '600', 45 | }, 46 | title: { 47 | fontSize: 32, 48 | fontWeight: 'bold', 49 | lineHeight: 32, 50 | }, 51 | subtitle: { 52 | fontSize: 20, 53 | fontWeight: 'bold', 54 | }, 55 | link: { 56 | lineHeight: 30, 57 | fontSize: 16, 58 | color: '#0a7ea4', 59 | }, 60 | }); 61 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | includeBuild(new File(["node", "--print", "require.resolve('@react-native/gradle-plugin/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().toString()) 3 | } 4 | plugins { id("com.facebook.react.settings") } 5 | 6 | extensions.configure(com.facebook.react.ReactSettingsExtension) { ex -> 7 | if (System.getenv('EXPO_USE_COMMUNITY_AUTOLINKING') == '1') { 8 | ex.autolinkLibrariesFromCommand() 9 | } else { 10 | def command = [ 11 | 'node', 12 | '--no-warnings', 13 | '--eval', 14 | 'require(require.resolve(\'expo-modules-autolinking\', { paths: [require.resolve(\'expo/package.json\')] }))(process.argv.slice(1))', 15 | 'react-native-config', 16 | '--json', 17 | '--platform', 18 | 'android' 19 | ].toList() 20 | ex.autolinkLibrariesFromCommand(command) 21 | } 22 | } 23 | 24 | rootProject.name = 'expo-app-boilerplate' 25 | 26 | dependencyResolutionManagement { 27 | versionCatalogs { 28 | reactAndroidLibs { 29 | from(files(new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../gradle/libs.versions.toml"))) 30 | } 31 | } 32 | } 33 | 34 | apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle"); 35 | useExpoModules() 36 | 37 | include ':app' 38 | includeBuild(new File(["node", "--print", "require.resolve('@react-native/gradle-plugin/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile()) 39 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = findProperty('android.buildToolsVersion') ?: '35.0.0' 6 | minSdkVersion = Integer.parseInt(findProperty('android.minSdkVersion') ?: '24') 7 | compileSdkVersion = Integer.parseInt(findProperty('android.compileSdkVersion') ?: '35') 8 | targetSdkVersion = Integer.parseInt(findProperty('android.targetSdkVersion') ?: '34') 9 | kotlinVersion = findProperty('android.kotlinVersion') ?: '1.9.25' 10 | 11 | ndkVersion = "26.1.10909125" 12 | } 13 | repositories { 14 | google() 15 | mavenCentral() 16 | } 17 | dependencies { 18 | classpath('com.android.tools.build:gradle') 19 | classpath('com.facebook.react:react-native-gradle-plugin') 20 | classpath('org.jetbrains.kotlin:kotlin-gradle-plugin') 21 | } 22 | } 23 | 24 | apply plugin: "com.facebook.react.rootproject" 25 | 26 | allprojects { 27 | repositories { 28 | maven { 29 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 30 | url(new File(['node', '--print', "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), '../android')) 31 | } 32 | maven { 33 | // Android JSC is installed from npm 34 | url(new File(['node', '--print', "require.resolve('jsc-android/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim(), '../dist')) 35 | } 36 | 37 | google() 38 | mavenCentral() 39 | maven { url 'https://www.jitpack.io' } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/_layout.tsx: -------------------------------------------------------------------------------- 1 | import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native'; 2 | import { useFonts } from 'expo-font'; 3 | import { Stack } from 'expo-router'; 4 | import * as SplashScreen from 'expo-splash-screen'; 5 | import { StatusBar } from 'expo-status-bar'; 6 | import { useEffect } from 'react'; 7 | import { Platform } from 'react-native'; 8 | import 'react-native-reanimated'; 9 | import { GestureHandlerRootView } from 'react-native-gesture-handler'; 10 | 11 | import { superwallService } from '@/services/superwall'; 12 | import { useColorScheme } from '@/hooks/useColorScheme'; 13 | import { OnboardingProvider } from '@/contexts/OnboardingContext'; 14 | 15 | // Prevent the splash screen from auto-hiding before asset loading is complete. 16 | SplashScreen.preventAutoHideAsync(); 17 | 18 | export default function RootLayout() { 19 | const colorScheme = useColorScheme(); 20 | const [loaded] = useFonts({ 21 | SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'), 22 | }); 23 | 24 | useEffect(() => { 25 | if (Platform.OS !== 'web') { 26 | superwallService.initialize(); 27 | } 28 | }, []); 29 | 30 | useEffect(() => { 31 | if (loaded) { 32 | SplashScreen.hideAsync(); 33 | } 34 | }, [loaded]); 35 | 36 | if (!loaded) return null; 37 | 38 | return ( 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | ); 52 | } 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Welcome to jack's Expo React Native free boilerplate 👋 2 | 3 | This is an [Expo](https://expo.dev) template project with Superwall libraries ready to use and a simple onboarding sequence for first time users. 4 | 5 | This free boilerplate is sponsored by [post bridge](https://post-bridge.com) - a super simple and affordable social media scheduling tool for small teams and founders. 6 | 7 | ## Get started 8 | 9 | 1. Clone this repository 10 | 11 | 2. Install dependencies 12 | 13 | ```bash 14 | npm install 15 | ``` 16 | Or 17 | 18 | ```bash 19 | npx expo install 20 | ``` 21 | 22 | 3. Start the app 23 | 24 | ```bash 25 | npx expo start 26 | ``` 27 | -- you will need to make a development build or run in development mode as Superwall does not work in Expo GO 28 | 29 | In the output, you'll find options to open the app in a 30 | 31 | - [development build](https://docs.expo.dev/develop/development-builds/introduction/) 32 | - [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/) 33 | - [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/) 34 | - [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo 35 | 36 | You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction). 37 | 38 | ## Need help? 39 | 40 | Join [the discord](https://discord.gg/XuT2V5GUkA) for app founders and @jackfriks for help. 41 | 42 | ## Learn more 43 | 44 | To learn more about developing your project with Expo, look at the following resources: 45 | 46 | - [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides). 47 | - [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. 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "expo-app-boilerplate", 3 | "main": "expo-router/entry", 4 | "version": "1.0.0", 5 | "scripts": { 6 | "start": "expo start", 7 | "reset-project": "node ./scripts/reset-project.js", 8 | "android": "expo run:android", 9 | "ios": "expo run:ios", 10 | "web": "expo start --web", 11 | "test": "jest --watchAll", 12 | "lint": "expo lint" 13 | }, 14 | "jest": { 15 | "preset": "jest-expo" 16 | }, 17 | "dependencies": { 18 | "@expo/vector-icons": "^14.0.2", 19 | "@react-navigation/bottom-tabs": "^7.2.0", 20 | "@react-navigation/native": "^7.0.14", 21 | "@superwall/react-native-superwall": "^1.4.7", 22 | "expo": "~52.0.35", 23 | "expo-blur": "~14.0.3", 24 | "expo-constants": "~17.0.6", 25 | "expo-dev-client": "~5.0.12", 26 | "expo-font": "~13.0.3", 27 | "expo-haptics": "~14.0.1", 28 | "expo-linking": "~7.0.5", 29 | "expo-router": "~4.0.17", 30 | "expo-splash-screen": "~0.29.22", 31 | "expo-status-bar": "~2.0.1", 32 | "expo-symbols": "~0.2.2", 33 | "expo-system-ui": "~4.0.8", 34 | "expo-web-browser": "~14.0.2", 35 | "react": "18.3.1", 36 | "react-dom": "18.3.1", 37 | "react-native": "0.76.7", 38 | "react-native-gesture-handler": "~2.20.2", 39 | "react-native-reanimated": "~3.16.1", 40 | "react-native-safe-area-context": "4.12.0", 41 | "react-native-screens": "~4.4.0", 42 | "react-native-web": "~0.19.13", 43 | "react-native-webview": "13.12.5", 44 | "@react-native-async-storage/async-storage": "1.23.1" 45 | }, 46 | "devDependencies": { 47 | "@babel/core": "^7.25.2", 48 | "@types/jest": "^29.5.12", 49 | "@types/react": "~18.3.12", 50 | "@types/react-native": "^0.72.8", 51 | "@types/react-test-renderer": "^18.3.0", 52 | "jest": "^29.2.1", 53 | "jest-expo": "~52.0.4", 54 | "react-test-renderer": "18.3.1", 55 | "typescript": "^5.3.3" 56 | }, 57 | "private": true 58 | } 59 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/rn_edit_text_material.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 22 | 23 | 24 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /services/superwall.ts: -------------------------------------------------------------------------------- 1 | import { Platform } from 'react-native'; 2 | import Superwall, { SubscriptionStatus } from '@superwall/react-native-superwall'; 3 | import { createSuperwallConfig } from '@/config/superwall'; 4 | 5 | class SuperwallService { 6 | private static instance: SuperwallService; 7 | private initialized = false; 8 | 9 | private constructor() {} 10 | 11 | static getInstance(): SuperwallService { 12 | if (!SuperwallService.instance) { 13 | SuperwallService.instance = new SuperwallService(); 14 | } 15 | return SuperwallService.instance; 16 | } 17 | 18 | initialize() { 19 | if (this.initialized) return; 20 | 21 | const apiKey = Platform.select({ 22 | ios: process.env.EXPO_PUBLIC_SUPERWALL_API_KEY_IOS, 23 | android: process.env.EXPO_PUBLIC_SUPERWALL_API_KEY_ANDROID, 24 | default: undefined, 25 | }); 26 | 27 | if (!apiKey) { 28 | console.warn('[Superwall] No API key found for platform:', Platform.OS); 29 | return; 30 | } 31 | 32 | try { 33 | const options = createSuperwallConfig(); 34 | Superwall.configure(apiKey, options); 35 | this.initialized = true; 36 | console.log('[Superwall] Initialized successfully'); 37 | } catch (error) { 38 | console.error('[Superwall] Initialization failed:', error); 39 | } 40 | } 41 | 42 | async presentPaywall(triggerId: string): Promise { 43 | try { 44 | console.log('[Superwall] Presenting paywall for trigger:', triggerId); 45 | await Superwall.shared.register(triggerId); 46 | } catch (error) { 47 | console.error('[Superwall] Failed to present paywall:', error); 48 | throw error; 49 | } 50 | } 51 | 52 | async getSubscriptionStatus(): Promise { 53 | try { 54 | const status = await Superwall.shared.getSubscriptionStatus(); 55 | console.log('[Superwall] Subscription status:', status); 56 | return status; 57 | } catch (error) { 58 | console.error('[Superwall] Failed to get subscription status:', error); 59 | throw error; 60 | } 61 | } 62 | } 63 | 64 | export const superwallService = SuperwallService.getInstance(); -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /contexts/OnboardingContext.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, useContext, useEffect, useState } from 'react'; 2 | import AsyncStorage from '@react-native-async-storage/async-storage'; 3 | import { useRouter, useSegments } from 'expo-router'; 4 | 5 | type OnboardingContextType = { 6 | isOnboarded: boolean; 7 | setIsOnboarded: (value: boolean) => void; 8 | }; 9 | 10 | const OnboardingContext = createContext(undefined); 11 | 12 | const STORAGE_KEY = 'hasCompletedOnboarding'; 13 | 14 | export function OnboardingProvider({ children }: { children: React.ReactNode }) { 15 | const [isOnboarded, setIsOnboarded] = useState(null); 16 | const segments = useSegments(); 17 | const router = useRouter(); 18 | 19 | useEffect(() => { 20 | checkOnboardingStatus(); 21 | }, []); 22 | 23 | useEffect(() => { 24 | if (isOnboarded === null) return; 25 | 26 | const inAuthGroup = segments[0] === 'onboarding'; 27 | 28 | if (!isOnboarded && !inAuthGroup) { 29 | router.replace('/onboarding'); 30 | } else if (isOnboarded && inAuthGroup) { 31 | router.replace('/'); 32 | } 33 | }, [isOnboarded, segments]); 34 | 35 | const checkOnboardingStatus = async () => { 36 | try { 37 | const value = await AsyncStorage.getItem(STORAGE_KEY); 38 | setIsOnboarded(value === 'true'); 39 | } catch (error) { 40 | console.error('Error checking onboarding status:', error); 41 | setIsOnboarded(false); 42 | } 43 | }; 44 | 45 | const handleSetIsOnboarded = async (value: boolean) => { 46 | try { 47 | await AsyncStorage.setItem(STORAGE_KEY, String(value)); 48 | setIsOnboarded(value); 49 | } catch (error) { 50 | console.error('Error setting onboarding status:', error); 51 | } 52 | }; 53 | 54 | return ( 55 | 60 | {children} 61 | 62 | ); 63 | } 64 | 65 | export function useOnboarding() { 66 | const context = useContext(OnboardingContext); 67 | if (context === undefined) { 68 | throw new Error('useOnboarding must be used within an OnboardingProvider'); 69 | } 70 | return context; 71 | } -------------------------------------------------------------------------------- /android/app/src/main/java/com/cqjack/expoappboilerplate/MainApplication.kt: -------------------------------------------------------------------------------- 1 | package com.cqjack.expoappboilerplate 2 | 3 | import android.app.Application 4 | import android.content.res.Configuration 5 | 6 | import com.facebook.react.PackageList 7 | import com.facebook.react.ReactApplication 8 | import com.facebook.react.ReactNativeHost 9 | import com.facebook.react.ReactPackage 10 | import com.facebook.react.ReactHost 11 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load 12 | import com.facebook.react.defaults.DefaultReactNativeHost 13 | import com.facebook.react.soloader.OpenSourceMergedSoMapping 14 | import com.facebook.soloader.SoLoader 15 | 16 | import expo.modules.ApplicationLifecycleDispatcher 17 | import expo.modules.ReactNativeHostWrapper 18 | 19 | class MainApplication : Application(), ReactApplication { 20 | 21 | override val reactNativeHost: ReactNativeHost = ReactNativeHostWrapper( 22 | this, 23 | object : DefaultReactNativeHost(this) { 24 | override fun getPackages(): List { 25 | val packages = PackageList(this).packages 26 | // Packages that cannot be autolinked yet can be added manually here, for example: 27 | // packages.add(new MyReactNativePackage()); 28 | return packages 29 | } 30 | 31 | override fun getJSMainModuleName(): String = ".expo/.virtual-metro-entry" 32 | 33 | override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG 34 | 35 | override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED 36 | override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED 37 | } 38 | ) 39 | 40 | override val reactHost: ReactHost 41 | get() = ReactNativeHostWrapper.createReactHost(applicationContext, reactNativeHost) 42 | 43 | override fun onCreate() { 44 | super.onCreate() 45 | SoLoader.init(this, OpenSourceMergedSoMapping) 46 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { 47 | // If you opted-in for the New Architecture, we load the native entry point for this app. 48 | load() 49 | } 50 | ApplicationLifecycleDispatcher.onApplicationCreate(this) 51 | } 52 | 53 | override fun onConfigurationChanged(newConfig: Configuration) { 54 | super.onConfigurationChanged(newConfig) 55 | ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /components/ParallaxScrollView.tsx: -------------------------------------------------------------------------------- 1 | import type { PropsWithChildren, ReactElement } from 'react'; 2 | import { StyleSheet } from 'react-native'; 3 | import Animated, { 4 | interpolate, 5 | useAnimatedRef, 6 | useAnimatedStyle, 7 | useScrollViewOffset, 8 | } from 'react-native-reanimated'; 9 | 10 | import { ThemedView } from '@/components/ThemedView'; 11 | import { useBottomTabOverflow } from '@/components/ui/TabBarBackground'; 12 | import { useColorScheme } from '@/hooks/useColorScheme'; 13 | 14 | const HEADER_HEIGHT = 250; 15 | 16 | type Props = PropsWithChildren<{ 17 | headerImage: ReactElement; 18 | headerBackgroundColor: { dark: string; light: string }; 19 | }>; 20 | 21 | export default function ParallaxScrollView({ 22 | children, 23 | headerImage, 24 | headerBackgroundColor, 25 | }: Props) { 26 | const colorScheme = useColorScheme() ?? 'light'; 27 | const scrollRef = useAnimatedRef(); 28 | const scrollOffset = useScrollViewOffset(scrollRef); 29 | const bottom = useBottomTabOverflow(); 30 | const headerAnimatedStyle = useAnimatedStyle(() => { 31 | return { 32 | transform: [ 33 | { 34 | translateY: interpolate( 35 | scrollOffset.value, 36 | [-HEADER_HEIGHT, 0, HEADER_HEIGHT], 37 | [-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75] 38 | ), 39 | }, 40 | { 41 | scale: interpolate(scrollOffset.value, [-HEADER_HEIGHT, 0, HEADER_HEIGHT], [2, 1, 1]), 42 | }, 43 | ], 44 | }; 45 | }); 46 | 47 | return ( 48 | 49 | 54 | 60 | {headerImage} 61 | 62 | {children} 63 | 64 | 65 | ); 66 | } 67 | 68 | const styles = StyleSheet.create({ 69 | container: { 70 | flex: 1, 71 | }, 72 | header: { 73 | height: HEADER_HEIGHT, 74 | overflow: 'hidden', 75 | }, 76 | content: { 77 | flex: 1, 78 | padding: 32, 79 | gap: 16, 80 | overflow: 'hidden', 81 | }, 82 | }); 83 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m 13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | # AndroidX package structure to make it clearer which packages are bundled with the 21 | # Android operating system, and which are packaged with your app's APK 22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 23 | android.useAndroidX=true 24 | 25 | # Enable AAPT2 PNG crunching 26 | android.enablePngCrunchInReleaseBuilds=true 27 | 28 | # Use this property to specify which architecture you want to build. 29 | # You can also override it from the CLI using 30 | # ./gradlew -PreactNativeArchitectures=x86_64 31 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 32 | 33 | # Use this property to enable support to the new architecture. 34 | # This will allow you to use TurboModules and the Fabric render in 35 | # your application. You should enable this flag either if you want 36 | # to write custom TurboModules/Fabric components OR use libraries that 37 | # are providing them. 38 | newArchEnabled=true 39 | 40 | # Use this property to enable or disable the Hermes JS engine. 41 | # If set to false, you will be using JSC instead. 42 | hermesEnabled=true 43 | 44 | # Enable GIF support in React Native images (~200 B increase) 45 | expo.gif.enabled=true 46 | # Enable webp support in React Native images (~85 KB increase) 47 | expo.webp.enabled=true 48 | # Enable animated webp support (~3.4 MB increase) 49 | # Disabled by default because iOS doesn't support animated webp 50 | expo.webp.animated=false 51 | 52 | # Enable network inspector 53 | EX_DEV_CLIENT_NETWORK_INSPECTOR=true 54 | 55 | # Use legacy packaging to compress native libraries in the resulting APK. 56 | expo.useLegacyPackaging=false 57 | -------------------------------------------------------------------------------- /app/onboarding/index.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, View } from 'react-native'; 2 | import { useRouter } from 'expo-router'; 3 | import { ThemedText } from '@/components/ThemedText'; 4 | import { ThemedView } from '@/components/ThemedView'; 5 | import { TouchableOpacity } from 'react-native-gesture-handler'; 6 | import { SafeAreaView } from 'react-native-safe-area-context'; 7 | import { StatusBar } from 'expo-status-bar'; 8 | import { MaterialCommunityIcons } from '@expo/vector-icons'; 9 | 10 | export default function WelcomeScreen() { 11 | const router = useRouter(); 12 | 13 | const handleNext = () => { 14 | router.push('/onboarding/problem'); 15 | }; 16 | 17 | return ( 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Your App Name 26 | 27 | 28 | 29 | A short, compelling tagline that captures your app's value 30 | 31 | 32 | 33 | 34 | 35 | 36 | Get Started 37 | 38 | 39 | 40 | 41 | 42 | ); 43 | } 44 | 45 | const styles = StyleSheet.create({ 46 | container: { 47 | flex: 1, 48 | }, 49 | safeArea: { 50 | flex: 1, 51 | }, 52 | content: { 53 | flex: 1, 54 | paddingHorizontal: 24, 55 | justifyContent: 'space-between', 56 | paddingVertical: 24, 57 | }, 58 | main: { 59 | flex: 1, 60 | alignItems: 'center', 61 | justifyContent: 'center', 62 | gap: 24, 63 | }, 64 | title: { 65 | fontSize: 36, 66 | textAlign: 'center', 67 | paddingHorizontal: 16, 68 | }, 69 | subtitleContainer: { 70 | paddingHorizontal: 32, 71 | }, 72 | subtitle: { 73 | fontSize: 18, 74 | opacity: 0.7, 75 | textAlign: 'center', 76 | lineHeight: 24, 77 | }, 78 | button: { 79 | backgroundColor: '#0A7EA4', 80 | padding: 20, 81 | borderRadius: 16, 82 | alignItems: 'center', 83 | }, 84 | buttonText: { 85 | color: 'white', 86 | fontSize: 18, 87 | }, 88 | }); -------------------------------------------------------------------------------- /android/app/src/main/java/com/cqjack/expoappboilerplate/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.cqjack.expoappboilerplate 2 | import expo.modules.splashscreen.SplashScreenManager 3 | 4 | import android.os.Build 5 | import android.os.Bundle 6 | 7 | import com.facebook.react.ReactActivity 8 | import com.facebook.react.ReactActivityDelegate 9 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled 10 | import com.facebook.react.defaults.DefaultReactActivityDelegate 11 | 12 | import expo.modules.ReactActivityDelegateWrapper 13 | 14 | class MainActivity : ReactActivity() { 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | // Set the theme to AppTheme BEFORE onCreate to support 17 | // coloring the background, status bar, and navigation bar. 18 | // This is required for expo-splash-screen. 19 | // setTheme(R.style.AppTheme); 20 | // @generated begin expo-splashscreen - expo prebuild (DO NOT MODIFY) sync-f3ff59a738c56c9a6119210cb55f0b613eb8b6af 21 | SplashScreenManager.registerOnActivity(this) 22 | // @generated end expo-splashscreen 23 | super.onCreate(null) 24 | } 25 | 26 | /** 27 | * Returns the name of the main component registered from JavaScript. This is used to schedule 28 | * rendering of the component. 29 | */ 30 | override fun getMainComponentName(): String = "main" 31 | 32 | /** 33 | * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] 34 | * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] 35 | */ 36 | override fun createReactActivityDelegate(): ReactActivityDelegate { 37 | return ReactActivityDelegateWrapper( 38 | this, 39 | BuildConfig.IS_NEW_ARCHITECTURE_ENABLED, 40 | object : DefaultReactActivityDelegate( 41 | this, 42 | mainComponentName, 43 | fabricEnabled 44 | ){}) 45 | } 46 | 47 | /** 48 | * Align the back button behavior with Android S 49 | * where moving root activities to background instead of finishing activities. 50 | * @see onBackPressed 51 | */ 52 | override fun invokeDefaultOnBackPressed() { 53 | if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) { 54 | if (!moveTaskToBack(false)) { 55 | // For non-root activities, use the default implementation to finish them. 56 | super.invokeDefaultOnBackPressed() 57 | } 58 | return 59 | } 60 | 61 | // Use the default back button implementation on Android S 62 | // because it's doing more than [Activity.moveTaskToBack] in fact. 63 | super.invokeDefaultOnBackPressed() 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking") 2 | require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods") 3 | 4 | require 'json' 5 | podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} 6 | 7 | ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' 8 | ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] 9 | 10 | platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' 11 | install! 'cocoapods', 12 | :deterministic_uuids => false 13 | 14 | prepare_react_native_project! 15 | 16 | target 'expoappboilerplate' do 17 | use_expo_modules! 18 | 19 | if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' 20 | config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; 21 | else 22 | config_command = [ 23 | 'node', 24 | '--no-warnings', 25 | '--eval', 26 | 'require(require.resolve(\'expo-modules-autolinking\', { paths: [require.resolve(\'expo/package.json\')] }))(process.argv.slice(1))', 27 | 'react-native-config', 28 | '--json', 29 | '--platform', 30 | 'ios' 31 | ] 32 | end 33 | 34 | config = use_native_modules!(config_command) 35 | 36 | use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] 37 | use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] 38 | 39 | use_react_native!( 40 | :path => config[:reactNativePath], 41 | :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', 42 | # An absolute path to your application root. 43 | :app_path => "#{Pod::Config.instance.installation_root}/..", 44 | :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', 45 | ) 46 | 47 | post_install do |installer| 48 | react_native_post_install( 49 | installer, 50 | config[:reactNativePath], 51 | :mac_catalyst_enabled => false, 52 | :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', 53 | ) 54 | 55 | # This is necessary for Xcode 14, because it signs resource bundles by default 56 | # when building for devices. 57 | installer.target_installation_results.pod_target_installation_results 58 | .each do |pod_name, target_installation_result| 59 | target_installation_result.resource_bundle_targets.each do |resource_bundle_target| 60 | resource_bundle_target.build_configurations.each do |config| 61 | config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' 62 | end 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /ios/expoappboilerplate/AppDelegate.mm: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | #import 5 | 6 | @implementation AppDelegate 7 | 8 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 9 | { 10 | self.moduleName = @"main"; 11 | 12 | // You can add your custom initial props in the dictionary below. 13 | // They will be passed down to the ViewController used by React Native. 14 | self.initialProps = @{}; 15 | 16 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 17 | } 18 | 19 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 20 | { 21 | return [self bundleURL]; 22 | } 23 | 24 | - (NSURL *)bundleURL 25 | { 26 | #if DEBUG 27 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@".expo/.virtual-metro-entry"]; 28 | #else 29 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 30 | #endif 31 | } 32 | 33 | // Linking API 34 | - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { 35 | return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options]; 36 | } 37 | 38 | // Universal Links 39 | - (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler { 40 | BOOL result = [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler]; 41 | return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler] || result; 42 | } 43 | 44 | // Explicitly define remote notification delegates to ensure compatibility with some third-party libraries 45 | - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken 46 | { 47 | return [super application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; 48 | } 49 | 50 | // Explicitly define remote notification delegates to ensure compatibility with some third-party libraries 51 | - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error 52 | { 53 | return [super application:application didFailToRegisterForRemoteNotificationsWithError:error]; 54 | } 55 | 56 | // Explicitly define remote notification delegates to ensure compatibility with some third-party libraries 57 | - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler 58 | { 59 | return [super application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /ios/expoappboilerplate/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CADisableMinimumFrameDurationOnPhone 6 | 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 9 | CFBundleDisplayName 10 | expo-app-boilerplate 11 | CFBundleExecutable 12 | $(EXECUTABLE_NAME) 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | $(PRODUCT_NAME) 19 | CFBundlePackageType 20 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 21 | CFBundleShortVersionString 22 | 1.0.0 23 | CFBundleSignature 24 | ???? 25 | CFBundleURLTypes 26 | 27 | 28 | CFBundleURLSchemes 29 | 30 | myapp 31 | com.cqjack.expoappboilerplate 32 | 33 | 34 | 35 | CFBundleURLSchemes 36 | 37 | exp+expo-app-boilerplate 38 | 39 | 40 | 41 | CFBundleVersion 42 | 1 43 | ITSAppUsesNonExemptEncryption 44 | 45 | LSMinimumSystemVersion 46 | 12.0 47 | LSRequiresIPhoneOS 48 | 49 | NSAppTransportSecurity 50 | 51 | NSAllowsArbitraryLoads 52 | 53 | NSAllowsLocalNetworking 54 | 55 | 56 | NSUserActivityTypes 57 | 58 | $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route 59 | 60 | UILaunchStoryboardName 61 | SplashScreen 62 | UIRequiredDeviceCapabilities 63 | 64 | arm64 65 | 66 | UIRequiresFullScreen 67 | 68 | UIStatusBarStyle 69 | UIStatusBarStyleDefault 70 | UISupportedInterfaceOrientations 71 | 72 | UIInterfaceOrientationPortrait 73 | UIInterfaceOrientationPortraitUpsideDown 74 | 75 | UISupportedInterfaceOrientations~ipad 76 | 77 | UIInterfaceOrientationPortrait 78 | UIInterfaceOrientationPortraitUpsideDown 79 | UIInterfaceOrientationLandscapeLeft 80 | UIInterfaceOrientationLandscapeRight 81 | 82 | UIUserInterfaceStyle 83 | Automatic 84 | UIViewControllerBasedStatusBarAppearance 85 | 86 | 87 | -------------------------------------------------------------------------------- /ios/expoappboilerplate/SplashScreen.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /ios/expoappboilerplate.xcodeproj/xcshareddata/xcschemes/expoappboilerplate.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /scripts/reset-project.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * This script is used to reset the project to a blank state. 5 | * It deletes or moves the /app, /components, /hooks, /scripts, and /constants directories to /app-example based on user input and creates a new /app directory with an index.tsx and _layout.tsx file. 6 | * You can remove the `reset-project` script from package.json and safely delete this file after running it. 7 | */ 8 | 9 | const fs = require("fs"); 10 | const path = require("path"); 11 | const readline = require("readline"); 12 | 13 | const root = process.cwd(); 14 | const oldDirs = ["app", "components", "hooks", "constants", "scripts"]; 15 | const exampleDir = "app-example"; 16 | const newAppDir = "app"; 17 | const exampleDirPath = path.join(root, exampleDir); 18 | 19 | const indexContent = `import { Text, View } from "react-native"; 20 | 21 | export default function Index() { 22 | return ( 23 | 30 | Edit app/index.tsx to edit this screen. 31 | 32 | ); 33 | } 34 | `; 35 | 36 | const layoutContent = `import { Stack } from "expo-router"; 37 | 38 | export default function RootLayout() { 39 | return ; 40 | } 41 | `; 42 | 43 | const rl = readline.createInterface({ 44 | input: process.stdin, 45 | output: process.stdout, 46 | }); 47 | 48 | const moveDirectories = async (userInput) => { 49 | try { 50 | if (userInput === "y") { 51 | // Create the app-example directory 52 | await fs.promises.mkdir(exampleDirPath, { recursive: true }); 53 | console.log(`📁 /${exampleDir} directory created.`); 54 | } 55 | 56 | // Move old directories to new app-example directory or delete them 57 | for (const dir of oldDirs) { 58 | const oldDirPath = path.join(root, dir); 59 | if (fs.existsSync(oldDirPath)) { 60 | if (userInput === "y") { 61 | const newDirPath = path.join(root, exampleDir, dir); 62 | await fs.promises.rename(oldDirPath, newDirPath); 63 | console.log(`➡️ /${dir} moved to /${exampleDir}/${dir}.`); 64 | } else { 65 | await fs.promises.rm(oldDirPath, { recursive: true, force: true }); 66 | console.log(`❌ /${dir} deleted.`); 67 | } 68 | } else { 69 | console.log(`➡️ /${dir} does not exist, skipping.`); 70 | } 71 | } 72 | 73 | // Create new /app directory 74 | const newAppDirPath = path.join(root, newAppDir); 75 | await fs.promises.mkdir(newAppDirPath, { recursive: true }); 76 | console.log("\n📁 New /app directory created."); 77 | 78 | // Create index.tsx 79 | const indexPath = path.join(newAppDirPath, "index.tsx"); 80 | await fs.promises.writeFile(indexPath, indexContent); 81 | console.log("📄 app/index.tsx created."); 82 | 83 | // Create _layout.tsx 84 | const layoutPath = path.join(newAppDirPath, "_layout.tsx"); 85 | await fs.promises.writeFile(layoutPath, layoutContent); 86 | console.log("📄 app/_layout.tsx created."); 87 | 88 | console.log("\n✅ Project reset complete. Next steps:"); 89 | console.log( 90 | `1. Run \`npx expo start\` to start a development server.\n2. Edit app/index.tsx to edit the main screen.${ 91 | userInput === "y" 92 | ? `\n3. Delete the /${exampleDir} directory when you're done referencing it.` 93 | : "" 94 | }` 95 | ); 96 | } catch (error) { 97 | console.error(`❌ Error during script execution: ${error.message}`); 98 | } 99 | }; 100 | 101 | rl.question( 102 | "Do you want to move existing files to /app-example instead of deleting them? (Y/n): ", 103 | (answer) => { 104 | const userInput = answer.trim().toLowerCase() || "y"; 105 | if (userInput === "y" || userInput === "n") { 106 | moveDirectories(userInput).finally(() => rl.close()); 107 | } else { 108 | console.log("❌ Invalid input. Please enter 'Y' or 'N'."); 109 | rl.close(); 110 | } 111 | } 112 | ); 113 | -------------------------------------------------------------------------------- /app/onboarding/final.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, View, ScrollView } from 'react-native'; 2 | import { ThemedText } from '@/components/ThemedText'; 3 | import { ThemedView } from '@/components/ThemedView'; 4 | import { TouchableOpacity } from 'react-native-gesture-handler'; 5 | import { useSuperwall } from '@/hooks/useSuperwall'; 6 | import { useOnboarding } from '@/contexts/OnboardingContext'; 7 | import { SUPERWALL_TRIGGERS } from '@/config/superwall'; 8 | import { SafeAreaView } from 'react-native-safe-area-context'; 9 | import { MaterialCommunityIcons } from '@expo/vector-icons'; 10 | import type { MaterialCommunityIcons as IconType } from '@expo/vector-icons'; 11 | 12 | export default function FinalScreen() { 13 | const { showPaywall } = useSuperwall(); 14 | const { setIsOnboarded } = useOnboarding(); 15 | 16 | const handleGetStarted = async () => { 17 | try { 18 | await showPaywall(SUPERWALL_TRIGGERS.ONBOARDING); 19 | setIsOnboarded(true); 20 | } catch (error) { 21 | console.error('Failed to show paywall:', error); 22 | } 23 | }; 24 | 25 | return ( 26 | 27 | 28 | 29 | 30 | 31 | 32 | Start Building Today 33 | 34 | 35 | You're all set to create your next great app. Get started now and save weeks of development time! 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | Get Started Now 49 | 50 | 51 | 52 | 53 | ); 54 | } 55 | 56 | function Benefit({ icon, text }: { icon: keyof typeof IconType.glyphMap; text: string }) { 57 | return ( 58 | 59 | 60 | 61 | 62 | {text} 63 | 64 | ); 65 | } 66 | 67 | const styles = StyleSheet.create({ 68 | container: { 69 | flex: 1, 70 | }, 71 | safeArea: { 72 | flex: 1, 73 | }, 74 | scroll: { 75 | flex: 1, 76 | }, 77 | scrollContent: { 78 | paddingHorizontal: 24, 79 | paddingTop: 24, 80 | gap: 32, 81 | }, 82 | header: { 83 | alignItems: 'center', 84 | gap: 16, 85 | }, 86 | title: { 87 | fontSize: 32, 88 | textAlign: 'center', 89 | }, 90 | description: { 91 | textAlign: 'center', 92 | fontSize: 18, 93 | lineHeight: 28, 94 | opacity: 0.7, 95 | }, 96 | benefits: { 97 | gap: 16, 98 | paddingBottom: 24, 99 | }, 100 | benefitContainer: { 101 | flexDirection: 'row', 102 | alignItems: 'center', 103 | gap: 16, 104 | backgroundColor: '#0A7EA410', 105 | padding: 16, 106 | borderRadius: 12, 107 | }, 108 | iconContainer: { 109 | width: 40, 110 | height: 40, 111 | borderRadius: 20, 112 | backgroundColor: '#0A7EA420', 113 | alignItems: 'center', 114 | justifyContent: 'center', 115 | }, 116 | benefitText: { 117 | fontSize: 17, 118 | }, 119 | button: { 120 | backgroundColor: '#0A7EA4', 121 | padding: 16, 122 | borderRadius: 12, 123 | alignItems: 'center', 124 | marginHorizontal: 24, 125 | marginBottom: 16, 126 | }, 127 | buttonText: { 128 | color: 'white', 129 | fontSize: 18, 130 | }, 131 | }); -------------------------------------------------------------------------------- /app/(tabs)/index.tsx: -------------------------------------------------------------------------------- 1 | import { Image, StyleSheet, Platform, View } from 'react-native'; 2 | import { TouchableOpacity } from 'react-native-gesture-handler'; 3 | 4 | import { HelloWave } from '@/components/HelloWave'; 5 | import ParallaxScrollView from '@/components/ParallaxScrollView'; 6 | import { ThemedText } from '@/components/ThemedText'; 7 | import { ThemedView } from '@/components/ThemedView'; 8 | import { useSuperwall } from '@/hooks/useSuperwall'; 9 | import { useOnboarding } from '@/contexts/OnboardingContext'; 10 | import { useRouter } from 'expo-router'; 11 | import { SUPERWALL_TRIGGERS } from '@/config/superwall'; 12 | 13 | export default function HomeScreen() { 14 | const { setIsOnboarded } = useOnboarding(); 15 | const { showPaywall } = useSuperwall(); 16 | const router = useRouter(); 17 | 18 | const handleRestartOnboarding = async () => { 19 | await setIsOnboarded(false); 20 | router.push('/onboarding'); 21 | }; 22 | 23 | const handleShowPaywall = () => { 24 | showPaywall(SUPERWALL_TRIGGERS.ONBOARDING); 25 | }; 26 | 27 | return ( 28 | 35 | }> 36 | 37 | Welcome! 38 | 39 | 40 | 41 | Step 1: Try it 42 | 43 | Edit app/(tabs)/index.tsx to see changes. 44 | Press{' '} 45 | 46 | {Platform.select({ 47 | ios: 'cmd + d', 48 | android: 'cmd + m', 49 | web: 'F12' 50 | })} 51 | {' '} 52 | to open developer tools. 53 | 54 | 55 | 56 | Step 2: Explore 57 | 58 | Tap the Explore tab to learn more about what's included in this starter app. 59 | 60 | 61 | 62 | Step 3: Get a fresh start 63 | 64 | When you're ready, run{' '} 65 | npm run reset-project to get a fresh{' '} 66 | app directory. This will move the current{' '} 67 | app to{' '} 68 | app-example. 69 | 70 | 71 | 72 | 73 | 74 | 75 | Show Paywall 76 | 77 | 78 | 79 | 80 | 81 | Restart Onboarding 82 | 83 | 84 | 85 | 86 | ); 87 | } 88 | 89 | const styles = StyleSheet.create({ 90 | titleContainer: { 91 | flexDirection: 'row', 92 | alignItems: 'center', 93 | gap: 8, 94 | }, 95 | stepContainer: { 96 | gap: 8, 97 | marginBottom: 8, 98 | }, 99 | reactLogo: { 100 | height: 178, 101 | width: 290, 102 | bottom: 0, 103 | left: 0, 104 | position: 'absolute', 105 | }, 106 | buttonContainer: { 107 | marginTop: 24, 108 | gap: 12, 109 | }, 110 | button: { 111 | padding: 16, 112 | borderRadius: 12, 113 | alignItems: 'center', 114 | backgroundColor: '#0A7EA4', 115 | }, 116 | buttonText: { 117 | color: 'white', 118 | }, 119 | secondaryButton: { 120 | backgroundColor: '#0A7EA420', 121 | }, 122 | secondaryButtonText: { 123 | color: '#0A7EA4', 124 | }, 125 | }); 126 | -------------------------------------------------------------------------------- /app/onboarding/solution.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, View, ScrollView } from 'react-native'; 2 | import { useRouter } from 'expo-router'; 3 | import { ThemedText } from '@/components/ThemedText'; 4 | import { ThemedView } from '@/components/ThemedView'; 5 | import { TouchableOpacity } from 'react-native-gesture-handler'; 6 | import { SafeAreaView } from 'react-native-safe-area-context'; 7 | import { MaterialCommunityIcons } from '@expo/vector-icons'; 8 | 9 | export default function SolutionScreen() { 10 | const router = useRouter(); 11 | 12 | const handleNext = () => { 13 | router.push('/onboarding/features'); 14 | }; 15 | 16 | return ( 17 | 18 | 19 | 24 | 25 | 26 | 27 | Introducing a Better Way 28 | 29 | 30 | 31 | 32 | 33 | Your App's Core Value 34 | 35 | 36 | One clear, powerful sentence that explains exactly how you solve the user's problem. 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | Key benefit or feature that solves their pain 45 | 46 | 47 | 48 | 49 | 50 | Another important advantage of your solution 51 | 52 | 53 | 54 | 55 | 56 | A third compelling reason to use your app 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | Show Me How 66 | 67 | 68 | 69 | 70 | 71 | ); 72 | } 73 | 74 | const styles = StyleSheet.create({ 75 | container: { 76 | flex: 1, 77 | }, 78 | safeArea: { 79 | flex: 1, 80 | }, 81 | scroll: { 82 | flex: 1, 83 | }, 84 | scrollContent: { 85 | paddingHorizontal: 24, 86 | paddingTop: 24, 87 | paddingBottom: 16, 88 | gap: 24, 89 | }, 90 | header: { 91 | alignItems: 'center', 92 | gap: 16, 93 | }, 94 | title: { 95 | fontSize: 32, 96 | textAlign: 'center', 97 | }, 98 | mainFeature: { 99 | backgroundColor: '#0A7EA410', 100 | padding: 24, 101 | borderRadius: 20, 102 | gap: 8, 103 | }, 104 | mainTitle: { 105 | fontSize: 22, 106 | textAlign: 'center', 107 | }, 108 | mainDescription: { 109 | fontSize: 16, 110 | opacity: 0.7, 111 | textAlign: 'center', 112 | lineHeight: 22, 113 | }, 114 | benefits: { 115 | gap: 12, 116 | }, 117 | benefit: { 118 | flexDirection: 'row', 119 | alignItems: 'center', 120 | gap: 12, 121 | backgroundColor: '#0A7EA408', 122 | padding: 16, 123 | borderRadius: 12, 124 | }, 125 | benefitText: { 126 | flex: 1, 127 | fontSize: 16, 128 | lineHeight: 22, 129 | }, 130 | buttonContainer: { 131 | paddingHorizontal: 24, 132 | paddingVertical: 16, 133 | }, 134 | button: { 135 | backgroundColor: '#0A7EA4', 136 | padding: 20, 137 | borderRadius: 16, 138 | alignItems: 'center', 139 | }, 140 | buttonText: { 141 | color: 'white', 142 | fontSize: 18, 143 | }, 144 | }); -------------------------------------------------------------------------------- /app/onboarding/features.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, View, ScrollView } from 'react-native'; 2 | import { useRouter } from 'expo-router'; 3 | import { ThemedText } from '@/components/ThemedText'; 4 | import { ThemedView } from '@/components/ThemedView'; 5 | import { TouchableOpacity } from 'react-native-gesture-handler'; 6 | import { SafeAreaView } from 'react-native-safe-area-context'; 7 | import { MaterialCommunityIcons } from '@expo/vector-icons'; 8 | import type { MaterialCommunityIcons as IconType } from '@expo/vector-icons'; 9 | 10 | export default function FeaturesScreen() { 11 | const router = useRouter(); 12 | 13 | const handleNext = () => { 14 | router.push('/onboarding/final'); 15 | }; 16 | 17 | return ( 18 | 19 | 20 | 25 | 26 | 27 | 28 | Ready to Use 29 | 30 | 31 | 32 | 33 | 38 | 43 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | Almost There 55 | 56 | 57 | 58 | 59 | 60 | ); 61 | } 62 | 63 | function Feature({ icon, title, description }: { 64 | icon: keyof typeof IconType.glyphMap; 65 | title: string; 66 | description: string; 67 | }) { 68 | return ( 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | {title} 77 | 78 | {description} 79 | 80 | 81 | 82 | ); 83 | } 84 | 85 | const styles = StyleSheet.create({ 86 | container: { 87 | flex: 1, 88 | }, 89 | safeArea: { 90 | flex: 1, 91 | }, 92 | scroll: { 93 | flex: 1, 94 | }, 95 | scrollContent: { 96 | paddingHorizontal: 24, 97 | paddingTop: 24, 98 | paddingBottom: 16, 99 | gap: 24, 100 | }, 101 | header: { 102 | alignItems: 'center', 103 | gap: 16, 104 | }, 105 | title: { 106 | fontSize: 32, 107 | textAlign: 'center', 108 | }, 109 | features: { 110 | gap: 16, 111 | }, 112 | feature: { 113 | backgroundColor: '#0A7EA410', 114 | padding: 16, 115 | borderRadius: 12, 116 | }, 117 | featureHeader: { 118 | flexDirection: 'row', 119 | gap: 16, 120 | alignItems: 'flex-start', 121 | }, 122 | iconContainer: { 123 | width: 40, 124 | height: 40, 125 | borderRadius: 20, 126 | backgroundColor: '#0A7EA420', 127 | alignItems: 'center', 128 | justifyContent: 'center', 129 | }, 130 | featureText: { 131 | flex: 1, 132 | gap: 4, 133 | }, 134 | featureTitle: { 135 | fontSize: 17, 136 | }, 137 | featureDescription: { 138 | fontSize: 15, 139 | opacity: 0.7, 140 | lineHeight: 20, 141 | }, 142 | buttonContainer: { 143 | paddingHorizontal: 24, 144 | paddingVertical: 16, 145 | }, 146 | button: { 147 | backgroundColor: '#0A7EA4', 148 | padding: 20, 149 | borderRadius: 16, 150 | alignItems: 'center', 151 | }, 152 | buttonText: { 153 | color: 'white', 154 | fontSize: 18, 155 | }, 156 | }); -------------------------------------------------------------------------------- /app/onboarding/problem.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, View, ScrollView } from 'react-native'; 2 | import { useRouter } from 'expo-router'; 3 | import { ThemedText } from '@/components/ThemedText'; 4 | import { ThemedView } from '@/components/ThemedView'; 5 | import { TouchableOpacity } from 'react-native-gesture-handler'; 6 | import { SafeAreaView } from 'react-native-safe-area-context'; 7 | import { MaterialCommunityIcons } from '@expo/vector-icons'; 8 | 9 | export default function ProblemScreen() { 10 | const router = useRouter(); 11 | 12 | const handleNext = () => { 13 | router.push('/onboarding/solution'); 14 | }; 15 | 16 | return ( 17 | 18 | 19 | 24 | 25 | 26 | The Problem 27 | 28 | 29 | Describe the main challenge or pain point your users face. Make it relatable and specific. 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | "I struggle with X every day, and it costs me Y hours per week..." 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | Current solutions are expensive and complex 46 | 47 | 48 | 49 | 50 | 51 | Users waste time on manual workarounds 52 | 53 | 54 | 55 | 56 | 57 | Existing tools lack key features 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | See the Solution 68 | 69 | 70 | 71 | 72 | 73 | ); 74 | } 75 | 76 | const styles = StyleSheet.create({ 77 | container: { 78 | flex: 1, 79 | }, 80 | safeArea: { 81 | flex: 1, 82 | }, 83 | scroll: { 84 | flex: 1, 85 | }, 86 | scrollContent: { 87 | flexGrow: 1, 88 | paddingHorizontal: 24, 89 | paddingTop: 24, 90 | }, 91 | header: { 92 | gap: 8, 93 | marginBottom: 32, 94 | }, 95 | title: { 96 | fontSize: 36, 97 | }, 98 | description: { 99 | fontSize: 16, 100 | opacity: 0.7, 101 | lineHeight: 22, 102 | }, 103 | content: { 104 | gap: 24, 105 | }, 106 | example: { 107 | backgroundColor: '#0A7EA410', 108 | padding: 16, 109 | borderRadius: 16, 110 | flexDirection: 'row', 111 | gap: 12, 112 | alignItems: 'center', 113 | }, 114 | exampleText: { 115 | flex: 1, 116 | fontSize: 16, 117 | fontStyle: 'italic', 118 | lineHeight: 22, 119 | }, 120 | points: { 121 | gap: 12, 122 | }, 123 | point: { 124 | flexDirection: 'row', 125 | alignItems: 'center', 126 | gap: 12, 127 | backgroundColor: '#E11D4810', 128 | padding: 14, 129 | borderRadius: 12, 130 | }, 131 | pointText: { 132 | flex: 1, 133 | fontSize: 15, 134 | lineHeight: 20, 135 | }, 136 | buttonContainer: { 137 | paddingHorizontal: 24, 138 | paddingVertical: 12, 139 | }, 140 | button: { 141 | backgroundColor: '#0A7EA4', 142 | padding: 16, 143 | borderRadius: 16, 144 | alignItems: 'center', 145 | }, 146 | buttonText: { 147 | color: 'white', 148 | fontSize: 17, 149 | }, 150 | }); -------------------------------------------------------------------------------- /app/(tabs)/explore.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, Image, Platform, TouchableOpacity } from 'react-native'; 2 | 3 | import { Collapsible } from '@/components/Collapsible'; 4 | import { ExternalLink } from '@/components/ExternalLink'; 5 | import ParallaxScrollView from '@/components/ParallaxScrollView'; 6 | import { ThemedText } from '@/components/ThemedText'; 7 | import { ThemedView } from '@/components/ThemedView'; 8 | import { IconSymbol } from '@/components/ui/IconSymbol'; 9 | import { useSuperwall } from '@/hooks/useSuperwall'; 10 | import { SUPERWALL_TRIGGERS } from '@/config/superwall'; 11 | import { Colors } from '@/constants/Colors'; 12 | 13 | export default function TabTwoScreen() { 14 | const { isSubscribed, isLoading, showPaywall } = useSuperwall(); 15 | 16 | const handlePremiumFeature = () => { 17 | if (!isSubscribed && !isLoading) { 18 | showPaywall(SUPERWALL_TRIGGERS.FEATURE_UNLOCK); 19 | } 20 | }; 21 | 22 | return ( 23 | 32 | }> 33 | 34 | Explore 35 | 36 | This app includes example code to help you get started. 37 | 38 | 39 | This app has two screens:{' '} 40 | app/(tabs)/index.tsx and{' '} 41 | app/(tabs)/explore.tsx 42 | 43 | 44 | The layout file in app/(tabs)/_layout.tsx{' '} 45 | sets up the tab navigator. 46 | 47 | 48 | Learn more 49 | 50 | 51 | 52 | 53 | You can open this project on Android, iOS, and the web. To open the web version, press{' '} 54 | w in the terminal running this project. 55 | 56 | 57 | 58 | 59 | For static images, you can use the @2x and{' '} 60 | @3x suffixes to provide files for 61 | different screen densities 62 | 63 | 64 | 65 | Learn more 66 | 67 | 68 | 69 | 70 | Open app/_layout.tsx to see how to load{' '} 71 | 72 | custom fonts such as this one. 73 | 74 | 75 | 76 | Learn more 77 | 78 | 79 | 80 | 81 | This template has light and dark mode support. The{' '} 82 | useColorScheme() hook lets you inspect 83 | what the user's current color scheme is, and so you can adjust UI colors accordingly. 84 | 85 | 86 | Learn more 87 | 88 | 89 | 90 | 91 | This template includes an example of an animated component. The{' '} 92 | components/HelloWave.tsx component uses 93 | the powerful react-native-reanimated{' '} 94 | library to create a waving hand animation. 95 | 96 | {Platform.select({ 97 | ios: ( 98 | 99 | The components/ParallaxScrollView.tsx{' '} 100 | component provides a parallax effect for the header image. 101 | 102 | ), 103 | })} 104 | 105 | 106 | 107 | Tap to explore premium features {isSubscribed ? '(Subscribed)' : '(Upgrade Required)'} 108 | 109 | {!isSubscribed && ( 110 | 111 | Upgrade Now 112 | 113 | )} 114 | 115 | 116 | ); 117 | } 118 | 119 | const styles = StyleSheet.create({ 120 | headerImage: { 121 | color: '#808080', 122 | bottom: -90, 123 | left: -35, 124 | position: 'absolute', 125 | }, 126 | titleContainer: { 127 | flexDirection: 'row', 128 | gap: 8, 129 | }, 130 | upgradeButton: { 131 | marginTop: 12, 132 | padding: 8, 133 | backgroundColor: Colors.light.tint, 134 | borderRadius: 8, 135 | alignItems: 'center', 136 | }, 137 | }); 138 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | apply plugin: "org.jetbrains.kotlin.android" 3 | apply plugin: "com.facebook.react" 4 | 5 | def projectRoot = rootDir.getAbsoluteFile().getParentFile().getAbsolutePath() 6 | 7 | /** 8 | * This is the configuration block to customize your React Native Android app. 9 | * By default you don't need to apply any configuration, just uncomment the lines you need. 10 | */ 11 | react { 12 | entryFile = file(["node", "-e", "require('expo/scripts/resolveAppEntry')", projectRoot, "android", "absolute"].execute(null, rootDir).text.trim()) 13 | reactNativeDir = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile() 14 | hermesCommand = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/sdks/hermesc/%OS-BIN%/hermesc" 15 | codegenDir = new File(["node", "--print", "require.resolve('@react-native/codegen/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile() 16 | 17 | // Use Expo CLI to bundle the app, this ensures the Metro config 18 | // works correctly with Expo projects. 19 | cliFile = new File(["node", "--print", "require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })"].execute(null, rootDir).text.trim()) 20 | bundleCommand = "export:embed" 21 | 22 | /* Folders */ 23 | // The root of your project, i.e. where "package.json" lives. Default is '../..' 24 | // root = file("../../") 25 | // The folder where the react-native NPM package is. Default is ../../node_modules/react-native 26 | // reactNativeDir = file("../../node_modules/react-native") 27 | // The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen 28 | // codegenDir = file("../../node_modules/@react-native/codegen") 29 | 30 | /* Variants */ 31 | // The list of variants to that are debuggable. For those we're going to 32 | // skip the bundling of the JS bundle and the assets. By default is just 'debug'. 33 | // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. 34 | // debuggableVariants = ["liteDebug", "prodDebug"] 35 | 36 | /* Bundling */ 37 | // A list containing the node command and its flags. Default is just 'node'. 38 | // nodeExecutableAndArgs = ["node"] 39 | 40 | // 41 | // The path to the CLI configuration file. Default is empty. 42 | // bundleConfig = file(../rn-cli.config.js) 43 | // 44 | // The name of the generated asset file containing your JS bundle 45 | // bundleAssetName = "MyApplication.android.bundle" 46 | // 47 | // The entry file for bundle generation. Default is 'index.android.js' or 'index.js' 48 | // entryFile = file("../js/MyApplication.android.js") 49 | // 50 | // A list of extra flags to pass to the 'bundle' commands. 51 | // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle 52 | // extraPackagerArgs = [] 53 | 54 | /* Hermes Commands */ 55 | // The hermes compiler command to run. By default it is 'hermesc' 56 | // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc" 57 | // 58 | // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" 59 | // hermesFlags = ["-O", "-output-source-map"] 60 | 61 | /* Autolinking */ 62 | autolinkLibrariesWithApp() 63 | } 64 | 65 | /** 66 | * Set this to true to Run Proguard on Release builds to minify the Java bytecode. 67 | */ 68 | def enableProguardInReleaseBuilds = (findProperty('android.enableProguardInReleaseBuilds') ?: false).toBoolean() 69 | 70 | /** 71 | * The preferred build flavor of JavaScriptCore (JSC) 72 | * 73 | * For example, to use the international variant, you can use: 74 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` 75 | * 76 | * The international variant includes ICU i18n library and necessary data 77 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that 78 | * give correct results when using with locales other than en-US. Note that 79 | * this variant is about 6MiB larger per architecture than default. 80 | */ 81 | def jscFlavor = 'org.webkit:android-jsc:+' 82 | 83 | android { 84 | ndkVersion rootProject.ext.ndkVersion 85 | 86 | buildToolsVersion rootProject.ext.buildToolsVersion 87 | compileSdk rootProject.ext.compileSdkVersion 88 | 89 | namespace 'com.cqjack.expoappboilerplate' 90 | defaultConfig { 91 | applicationId 'com.cqjack.expoappboilerplate' 92 | minSdkVersion rootProject.ext.minSdkVersion 93 | targetSdkVersion rootProject.ext.targetSdkVersion 94 | versionCode 1 95 | versionName "1.0.0" 96 | } 97 | signingConfigs { 98 | debug { 99 | storeFile file('debug.keystore') 100 | storePassword 'android' 101 | keyAlias 'androiddebugkey' 102 | keyPassword 'android' 103 | } 104 | } 105 | buildTypes { 106 | debug { 107 | signingConfig signingConfigs.debug 108 | } 109 | release { 110 | // Caution! In production, you need to generate your own keystore file. 111 | // see https://reactnative.dev/docs/signed-apk-android. 112 | signingConfig signingConfigs.debug 113 | shrinkResources (findProperty('android.enableShrinkResourcesInReleaseBuilds')?.toBoolean() ?: false) 114 | minifyEnabled enableProguardInReleaseBuilds 115 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 116 | crunchPngs (findProperty('android.enablePngCrunchInReleaseBuilds')?.toBoolean() ?: true) 117 | } 118 | } 119 | packagingOptions { 120 | jniLibs { 121 | useLegacyPackaging (findProperty('expo.useLegacyPackaging')?.toBoolean() ?: false) 122 | } 123 | } 124 | androidResources { 125 | ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~' 126 | } 127 | } 128 | 129 | // Apply static values from `gradle.properties` to the `android.packagingOptions` 130 | // Accepts values in comma delimited lists, example: 131 | // android.packagingOptions.pickFirsts=/LICENSE,**/picasa.ini 132 | ["pickFirsts", "excludes", "merges", "doNotStrip"].each { prop -> 133 | // Split option: 'foo,bar' -> ['foo', 'bar'] 134 | def options = (findProperty("android.packagingOptions.$prop") ?: "").split(","); 135 | // Trim all elements in place. 136 | for (i in 0.. 0) { 141 | println "android.packagingOptions.$prop += $options ($options.length)" 142 | // Ex: android.packagingOptions.pickFirsts += '**/SCCS/**' 143 | options.each { 144 | android.packagingOptions[prop] += it 145 | } 146 | } 147 | } 148 | 149 | dependencies { 150 | // The version of react-native is set by the React Native Gradle Plugin 151 | implementation("com.facebook.react:react-android") 152 | 153 | def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true"; 154 | def isWebpEnabled = (findProperty('expo.webp.enabled') ?: "") == "true"; 155 | def isWebpAnimatedEnabled = (findProperty('expo.webp.animated') ?: "") == "true"; 156 | 157 | if (isGifEnabled) { 158 | // For animated gif support 159 | implementation("com.facebook.fresco:animated-gif:${reactAndroidLibs.versions.fresco.get()}") 160 | } 161 | 162 | if (isWebpEnabled) { 163 | // For webp support 164 | implementation("com.facebook.fresco:webpsupport:${reactAndroidLibs.versions.fresco.get()}") 165 | if (isWebpAnimatedEnabled) { 166 | // Animated webp support 167 | implementation("com.facebook.fresco:animated-webp:${reactAndroidLibs.versions.fresco.get()}") 168 | } 169 | } 170 | 171 | if (hermesEnabled.toBoolean()) { 172 | implementation("com.facebook.react:hermes-android") 173 | } else { 174 | implementation jscFlavor 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s 90 | ' "$PWD" ) || exit 91 | 92 | # Use the maximum available, or set MAX_FD != -1 to use that value. 93 | MAX_FD=maximum 94 | 95 | warn () { 96 | echo "$*" 97 | } >&2 98 | 99 | die () { 100 | echo 101 | echo "$*" 102 | echo 103 | exit 1 104 | } >&2 105 | 106 | # OS specific support (must be 'true' or 'false'). 107 | cygwin=false 108 | msys=false 109 | darwin=false 110 | nonstop=false 111 | case "$( uname )" in #( 112 | CYGWIN* ) cygwin=true ;; #( 113 | Darwin* ) darwin=true ;; #( 114 | MSYS* | MINGW* ) msys=true ;; #( 115 | NONSTOP* ) nonstop=true ;; 116 | esac 117 | 118 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 119 | 120 | 121 | # Determine the Java command to use to start the JVM. 122 | if [ -n "$JAVA_HOME" ] ; then 123 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 124 | # IBM's JDK on AIX uses strange locations for the executables 125 | JAVACMD=$JAVA_HOME/jre/sh/java 126 | else 127 | JAVACMD=$JAVA_HOME/bin/java 128 | fi 129 | if [ ! -x "$JAVACMD" ] ; then 130 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 131 | 132 | Please set the JAVA_HOME variable in your environment to match the 133 | location of your Java installation." 134 | fi 135 | else 136 | JAVACMD=java 137 | if ! command -v java >/dev/null 2>&1 138 | then 139 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 140 | 141 | Please set the JAVA_HOME variable in your environment to match the 142 | location of your Java installation." 143 | fi 144 | fi 145 | 146 | # Increase the maximum file descriptors if we can. 147 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 148 | case $MAX_FD in #( 149 | max*) 150 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 151 | # shellcheck disable=SC2039,SC3045 152 | MAX_FD=$( ulimit -H -n ) || 153 | warn "Could not query maximum file descriptor limit" 154 | esac 155 | case $MAX_FD in #( 156 | '' | soft) :;; #( 157 | *) 158 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 159 | # shellcheck disable=SC2039,SC3045 160 | ulimit -n "$MAX_FD" || 161 | warn "Could not set maximum file descriptor limit to $MAX_FD" 162 | esac 163 | fi 164 | 165 | # Collect all arguments for the java command, stacking in reverse order: 166 | # * args from the command line 167 | # * the main class name 168 | # * -classpath 169 | # * -D...appname settings 170 | # * --module-path (only if needed) 171 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 172 | 173 | # For Cygwin or MSYS, switch paths to Windows format before running java 174 | if "$cygwin" || "$msys" ; then 175 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 176 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 177 | 178 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 179 | 180 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 181 | for arg do 182 | if 183 | case $arg in #( 184 | -*) false ;; # don't mess with options #( 185 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 186 | [ -e "$t" ] ;; #( 187 | *) false ;; 188 | esac 189 | then 190 | arg=$( cygpath --path --ignore --mixed "$arg" ) 191 | fi 192 | # Roll the args list around exactly as many times as the number of 193 | # args, so each arg winds up back in the position where it started, but 194 | # possibly modified. 195 | # 196 | # NB: a `for` loop captures its iteration list before it begins, so 197 | # changing the positional parameters here affects neither the number of 198 | # iterations, nor the values presented in `arg`. 199 | shift # remove old arg 200 | set -- "$@" "$arg" # push replacement arg 201 | done 202 | fi 203 | 204 | 205 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 206 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 207 | 208 | # Collect all arguments for the java command: 209 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 210 | # and any embedded shellness will be escaped. 211 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 212 | # treated as '${Hostname}' itself on the command line. 213 | 214 | set -- \ 215 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 216 | -classpath "$CLASSPATH" \ 217 | org.gradle.wrapper.GradleWrapperMain \ 218 | "$@" 219 | 220 | # Stop when "xargs" is not available. 221 | if ! command -v xargs >/dev/null 2>&1 222 | then 223 | die "xargs is not available" 224 | fi 225 | 226 | # Use "xargs" to parse quoted args. 227 | # 228 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 229 | # 230 | # In Bash we could simply go: 231 | # 232 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 233 | # set -- "${ARGS[@]}" "$@" 234 | # 235 | # but POSIX shell has neither arrays nor command substitution, so instead we 236 | # post-process each arg (as a line of input to sed) to backslash-escape any 237 | # character that might be a shell metacharacter, then use eval to reverse 238 | # that process (while maintaining the separation between arguments), and wrap 239 | # the whole thing up as a single "set" statement. 240 | # 241 | # This will of course break if any of these variables contains a newline or 242 | # an unmatched quote. 243 | # 244 | 245 | eval "set -- $( 246 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 247 | xargs -n1 | 248 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 249 | tr '\n' ' ' 250 | )" '"$@"' 251 | 252 | exec "$JAVACMD" "$@" 253 | -------------------------------------------------------------------------------- /ios/expoappboilerplate.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 11 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 12 | 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 13 | 190D622815B7446E85DE663F /* noop-file.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A4ECB565DE44967A4A9E63A /* noop-file.swift */; }; 14 | 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; }; 15 | 64A27CBA0C15D3668487CE31 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 25D8759A3F37E8B85A1B85A4 /* PrivacyInfo.xcprivacy */; }; 16 | 96905EF65AED1B983A6B3ABC /* libPods-expoappboilerplate.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-expoappboilerplate.a */; }; 17 | B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; }; 18 | BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXFileReference section */ 22 | 13B07F961A680F5B00A75B9A /* expoappboilerplate.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = expoappboilerplate.app; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = expoappboilerplate/AppDelegate.h; sourceTree = ""; }; 24 | 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = expoappboilerplate/AppDelegate.mm; sourceTree = ""; }; 25 | 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = expoappboilerplate/Images.xcassets; sourceTree = ""; }; 26 | 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = expoappboilerplate/Info.plist; sourceTree = ""; }; 27 | 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = expoappboilerplate/main.m; sourceTree = ""; }; 28 | 25D8759A3F37E8B85A1B85A4 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = expoappboilerplate/PrivacyInfo.xcprivacy; sourceTree = ""; }; 29 | 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-expoappboilerplate.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-expoappboilerplate.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | 5A4ECB565DE44967A4A9E63A /* noop-file.swift */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = "noop-file.swift"; path = "expoappboilerplate/noop-file.swift"; sourceTree = ""; }; 31 | 6C2E3173556A471DD304B334 /* Pods-expoappboilerplate.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-expoappboilerplate.debug.xcconfig"; path = "Target Support Files/Pods-expoappboilerplate/Pods-expoappboilerplate.debug.xcconfig"; sourceTree = ""; }; 32 | 7A4D352CD337FB3A3BF06240 /* Pods-expoappboilerplate.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-expoappboilerplate.release.xcconfig"; path = "Target Support Files/Pods-expoappboilerplate/Pods-expoappboilerplate.release.xcconfig"; sourceTree = ""; }; 33 | 98CC00954968407EA3D35E84 /* expoappboilerplate-Bridging-Header.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = "expoappboilerplate-Bridging-Header.h"; path = "expoappboilerplate/expoappboilerplate-Bridging-Header.h"; sourceTree = ""; }; 34 | AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = expoappboilerplate/SplashScreen.storyboard; sourceTree = ""; }; 35 | BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = ""; }; 36 | ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; 37 | FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-expoappboilerplate/ExpoModulesProvider.swift"; sourceTree = ""; }; 38 | /* End PBXFileReference section */ 39 | 40 | /* Begin PBXFrameworksBuildPhase section */ 41 | 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { 42 | isa = PBXFrameworksBuildPhase; 43 | buildActionMask = 2147483647; 44 | files = ( 45 | 96905EF65AED1B983A6B3ABC /* libPods-expoappboilerplate.a in Frameworks */, 46 | ); 47 | runOnlyForDeploymentPostprocessing = 0; 48 | }; 49 | /* End PBXFrameworksBuildPhase section */ 50 | 51 | /* Begin PBXGroup section */ 52 | 13B07FAE1A68108700A75B9A /* expoappboilerplate */ = { 53 | isa = PBXGroup; 54 | children = ( 55 | BB2F792B24A3F905000567C9 /* Supporting */, 56 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */, 57 | 13B07FB01A68108700A75B9A /* AppDelegate.mm */, 58 | 13B07FB51A68108700A75B9A /* Images.xcassets */, 59 | 13B07FB61A68108700A75B9A /* Info.plist */, 60 | 13B07FB71A68108700A75B9A /* main.m */, 61 | AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */, 62 | 5A4ECB565DE44967A4A9E63A /* noop-file.swift */, 63 | 98CC00954968407EA3D35E84 /* expoappboilerplate-Bridging-Header.h */, 64 | 25D8759A3F37E8B85A1B85A4 /* PrivacyInfo.xcprivacy */, 65 | ); 66 | name = expoappboilerplate; 67 | sourceTree = ""; 68 | }; 69 | 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | ED297162215061F000B7C4FE /* JavaScriptCore.framework */, 73 | 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-expoappboilerplate.a */, 74 | ); 75 | name = Frameworks; 76 | sourceTree = ""; 77 | }; 78 | 832341AE1AAA6A7D00B99B32 /* Libraries */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | ); 82 | name = Libraries; 83 | sourceTree = ""; 84 | }; 85 | 83CBB9F61A601CBA00E9B192 = { 86 | isa = PBXGroup; 87 | children = ( 88 | 13B07FAE1A68108700A75B9A /* expoappboilerplate */, 89 | 832341AE1AAA6A7D00B99B32 /* Libraries */, 90 | 83CBBA001A601CBA00E9B192 /* Products */, 91 | 2D16E6871FA4F8E400B85C8A /* Frameworks */, 92 | D65327D7A22EEC0BE12398D9 /* Pods */, 93 | D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */, 94 | ); 95 | indentWidth = 2; 96 | sourceTree = ""; 97 | tabWidth = 2; 98 | usesTabs = 0; 99 | }; 100 | 83CBBA001A601CBA00E9B192 /* Products */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 13B07F961A680F5B00A75B9A /* expoappboilerplate.app */, 104 | ); 105 | name = Products; 106 | sourceTree = ""; 107 | }; 108 | 92DBD88DE9BF7D494EA9DA96 /* expoappboilerplate */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */, 112 | ); 113 | name = expoappboilerplate; 114 | sourceTree = ""; 115 | }; 116 | BB2F792B24A3F905000567C9 /* Supporting */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | BB2F792C24A3F905000567C9 /* Expo.plist */, 120 | ); 121 | name = Supporting; 122 | path = expoappboilerplate/Supporting; 123 | sourceTree = ""; 124 | }; 125 | D65327D7A22EEC0BE12398D9 /* Pods */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 6C2E3173556A471DD304B334 /* Pods-expoappboilerplate.debug.xcconfig */, 129 | 7A4D352CD337FB3A3BF06240 /* Pods-expoappboilerplate.release.xcconfig */, 130 | ); 131 | path = Pods; 132 | sourceTree = ""; 133 | }; 134 | D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | 92DBD88DE9BF7D494EA9DA96 /* expoappboilerplate */, 138 | ); 139 | name = ExpoModulesProviders; 140 | sourceTree = ""; 141 | }; 142 | /* End PBXGroup section */ 143 | 144 | /* Begin PBXNativeTarget section */ 145 | 13B07F861A680F5B00A75B9A /* expoappboilerplate */ = { 146 | isa = PBXNativeTarget; 147 | buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "expoappboilerplate" */; 148 | buildPhases = ( 149 | 08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */, 150 | 96E2F22856A27C2ACE5CE685 /* [Expo] Configure project */, 151 | 13B07F871A680F5B00A75B9A /* Sources */, 152 | 13B07F8C1A680F5B00A75B9A /* Frameworks */, 153 | 13B07F8E1A680F5B00A75B9A /* Resources */, 154 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, 155 | 800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */, 156 | 68ACA832FEA019FF7556258A /* [CP] Embed Pods Frameworks */, 157 | ); 158 | buildRules = ( 159 | ); 160 | dependencies = ( 161 | ); 162 | name = expoappboilerplate; 163 | productName = expoappboilerplate; 164 | productReference = 13B07F961A680F5B00A75B9A /* expoappboilerplate.app */; 165 | productType = "com.apple.product-type.application"; 166 | }; 167 | /* End PBXNativeTarget section */ 168 | 169 | /* Begin PBXProject section */ 170 | 83CBB9F71A601CBA00E9B192 /* Project object */ = { 171 | isa = PBXProject; 172 | attributes = { 173 | LastUpgradeCheck = 1130; 174 | TargetAttributes = { 175 | 13B07F861A680F5B00A75B9A = { 176 | LastSwiftMigration = 1250; 177 | }; 178 | }; 179 | }; 180 | buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "expoappboilerplate" */; 181 | compatibilityVersion = "Xcode 3.2"; 182 | developmentRegion = en; 183 | hasScannedForEncodings = 0; 184 | knownRegions = ( 185 | en, 186 | Base, 187 | ); 188 | mainGroup = 83CBB9F61A601CBA00E9B192; 189 | productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; 190 | projectDirPath = ""; 191 | projectRoot = ""; 192 | targets = ( 193 | 13B07F861A680F5B00A75B9A /* expoappboilerplate */, 194 | ); 195 | }; 196 | /* End PBXProject section */ 197 | 198 | /* Begin PBXResourcesBuildPhase section */ 199 | 13B07F8E1A680F5B00A75B9A /* Resources */ = { 200 | isa = PBXResourcesBuildPhase; 201 | buildActionMask = 2147483647; 202 | files = ( 203 | BB2F792D24A3F905000567C9 /* Expo.plist in Resources */, 204 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 205 | 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */, 206 | 64A27CBA0C15D3668487CE31 /* PrivacyInfo.xcprivacy in Resources */, 207 | ); 208 | runOnlyForDeploymentPostprocessing = 0; 209 | }; 210 | /* End PBXResourcesBuildPhase section */ 211 | 212 | /* Begin PBXShellScriptBuildPhase section */ 213 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { 214 | isa = PBXShellScriptBuildPhase; 215 | alwaysOutOfDate = 1; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | ); 219 | inputPaths = ( 220 | ); 221 | name = "Bundle React Native code and images"; 222 | outputPaths = ( 223 | ); 224 | runOnlyForDeploymentPostprocessing = 0; 225 | shellPath = /bin/sh; 226 | shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios absolute | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n# Source .xcode.env.updates if it exists to allow\n# SKIP_BUNDLING to be unset if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.updates\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.updates\"\nfi\n# Source local changes to allow overrides\n# if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n`\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n\n"; 227 | }; 228 | 08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */ = { 229 | isa = PBXShellScriptBuildPhase; 230 | buildActionMask = 2147483647; 231 | files = ( 232 | ); 233 | inputFileListPaths = ( 234 | ); 235 | inputPaths = ( 236 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 237 | "${PODS_ROOT}/Manifest.lock", 238 | ); 239 | name = "[CP] Check Pods Manifest.lock"; 240 | outputFileListPaths = ( 241 | ); 242 | outputPaths = ( 243 | "$(DERIVED_FILE_DIR)/Pods-expoappboilerplate-checkManifestLockResult.txt", 244 | ); 245 | runOnlyForDeploymentPostprocessing = 0; 246 | shellPath = /bin/sh; 247 | 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"; 248 | showEnvVarsInLog = 0; 249 | }; 250 | 68ACA832FEA019FF7556258A /* [CP] Embed Pods Frameworks */ = { 251 | isa = PBXShellScriptBuildPhase; 252 | buildActionMask = 2147483647; 253 | files = ( 254 | ); 255 | inputPaths = ( 256 | "${PODS_ROOT}/Target Support Files/Pods-expoappboilerplate/Pods-expoappboilerplate-frameworks.sh", 257 | "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", 258 | ); 259 | name = "[CP] Embed Pods Frameworks"; 260 | outputPaths = ( 261 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework", 262 | ); 263 | runOnlyForDeploymentPostprocessing = 0; 264 | shellPath = /bin/sh; 265 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-expoappboilerplate/Pods-expoappboilerplate-frameworks.sh\"\n"; 266 | showEnvVarsInLog = 0; 267 | }; 268 | 800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */ = { 269 | isa = PBXShellScriptBuildPhase; 270 | buildActionMask = 2147483647; 271 | files = ( 272 | ); 273 | inputPaths = ( 274 | "${PODS_ROOT}/Target Support Files/Pods-expoappboilerplate/Pods-expoappboilerplate-resources.sh", 275 | "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle", 276 | "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle", 277 | "${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle", 278 | "${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle", 279 | "${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly/RCT-Folly_privacy.bundle", 280 | "${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle", 281 | "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle", 282 | "${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle", 283 | "${PODS_CONFIGURATION_BUILD_DIR}/SuperwallKit/SuperwallKit.bundle", 284 | "${PODS_CONFIGURATION_BUILD_DIR}/boost/boost_privacy.bundle", 285 | "${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-launcher/EXDevLauncher.bundle", 286 | "${PODS_CONFIGURATION_BUILD_DIR}/expo-dev-menu/EXDevMenu.bundle", 287 | "${PODS_CONFIGURATION_BUILD_DIR}/glog/glog_privacy.bundle", 288 | ); 289 | name = "[CP] Copy Pods Resources"; 290 | outputPaths = ( 291 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle", 292 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle", 293 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle", 294 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle", 295 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle", 296 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle", 297 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle", 298 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_privacy.bundle", 299 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/SuperwallKit.bundle", 300 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/boost_privacy.bundle", 301 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevLauncher.bundle", 302 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXDevMenu.bundle", 303 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/glog_privacy.bundle", 304 | ); 305 | runOnlyForDeploymentPostprocessing = 0; 306 | shellPath = /bin/sh; 307 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-expoappboilerplate/Pods-expoappboilerplate-resources.sh\"\n"; 308 | showEnvVarsInLog = 0; 309 | }; 310 | 96E2F22856A27C2ACE5CE685 /* [Expo] Configure project */ = { 311 | isa = PBXShellScriptBuildPhase; 312 | alwaysOutOfDate = 1; 313 | buildActionMask = 2147483647; 314 | files = ( 315 | ); 316 | inputFileListPaths = ( 317 | ); 318 | inputPaths = ( 319 | ); 320 | name = "[Expo] Configure project"; 321 | outputFileListPaths = ( 322 | ); 323 | outputPaths = ( 324 | ); 325 | runOnlyForDeploymentPostprocessing = 0; 326 | shellPath = /bin/sh; 327 | shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-expoappboilerplate/expo-configure-project.sh\"\n"; 328 | }; 329 | /* End PBXShellScriptBuildPhase section */ 330 | 331 | /* Begin PBXSourcesBuildPhase section */ 332 | 13B07F871A680F5B00A75B9A /* Sources */ = { 333 | isa = PBXSourcesBuildPhase; 334 | buildActionMask = 2147483647; 335 | files = ( 336 | 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */, 337 | 13B07FC11A68108700A75B9A /* main.m in Sources */, 338 | B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */, 339 | 190D622815B7446E85DE663F /* noop-file.swift in Sources */, 340 | ); 341 | runOnlyForDeploymentPostprocessing = 0; 342 | }; 343 | /* End PBXSourcesBuildPhase section */ 344 | 345 | /* Begin XCBuildConfiguration section */ 346 | 13B07F941A680F5B00A75B9A /* Debug */ = { 347 | isa = XCBuildConfiguration; 348 | baseConfigurationReference = 6C2E3173556A471DD304B334 /* Pods-expoappboilerplate.debug.xcconfig */; 349 | buildSettings = { 350 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 351 | CLANG_ENABLE_MODULES = YES; 352 | CODE_SIGN_ENTITLEMENTS = expoappboilerplate/expoappboilerplate.entitlements; 353 | CURRENT_PROJECT_VERSION = 1; 354 | ENABLE_BITCODE = NO; 355 | GCC_PREPROCESSOR_DEFINITIONS = ( 356 | "$(inherited)", 357 | "FB_SONARKIT_ENABLED=1", 358 | ); 359 | INFOPLIST_FILE = expoappboilerplate/Info.plist; 360 | IPHONEOS_DEPLOYMENT_TARGET = 15.1; 361 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 362 | MARKETING_VERSION = 1.0; 363 | OTHER_LDFLAGS = ( 364 | "$(inherited)", 365 | "-ObjC", 366 | "-lc++", 367 | ); 368 | OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; 369 | PRODUCT_BUNDLE_IDENTIFIER = com.cqjack.expoappboilerplate; 370 | PRODUCT_NAME = expoappboilerplate; 371 | SWIFT_OBJC_BRIDGING_HEADER = "expoappboilerplate/expoappboilerplate-Bridging-Header.h"; 372 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 373 | SWIFT_VERSION = 5.0; 374 | TARGETED_DEVICE_FAMILY = "1,2"; 375 | VERSIONING_SYSTEM = "apple-generic"; 376 | }; 377 | name = Debug; 378 | }; 379 | 13B07F951A680F5B00A75B9A /* Release */ = { 380 | isa = XCBuildConfiguration; 381 | baseConfigurationReference = 7A4D352CD337FB3A3BF06240 /* Pods-expoappboilerplate.release.xcconfig */; 382 | buildSettings = { 383 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 384 | CLANG_ENABLE_MODULES = YES; 385 | CODE_SIGN_ENTITLEMENTS = expoappboilerplate/expoappboilerplate.entitlements; 386 | CURRENT_PROJECT_VERSION = 1; 387 | INFOPLIST_FILE = expoappboilerplate/Info.plist; 388 | IPHONEOS_DEPLOYMENT_TARGET = 15.1; 389 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 390 | MARKETING_VERSION = 1.0; 391 | OTHER_LDFLAGS = ( 392 | "$(inherited)", 393 | "-ObjC", 394 | "-lc++", 395 | ); 396 | OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; 397 | PRODUCT_BUNDLE_IDENTIFIER = com.cqjack.expoappboilerplate; 398 | PRODUCT_NAME = expoappboilerplate; 399 | SWIFT_OBJC_BRIDGING_HEADER = "expoappboilerplate/expoappboilerplate-Bridging-Header.h"; 400 | SWIFT_VERSION = 5.0; 401 | TARGETED_DEVICE_FAMILY = "1,2"; 402 | VERSIONING_SYSTEM = "apple-generic"; 403 | }; 404 | name = Release; 405 | }; 406 | 83CBBA201A601CBA00E9B192 /* Debug */ = { 407 | isa = XCBuildConfiguration; 408 | buildSettings = { 409 | ALWAYS_SEARCH_USER_PATHS = NO; 410 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 411 | CLANG_CXX_LANGUAGE_STANDARD = "c++20"; 412 | CLANG_CXX_LIBRARY = "libc++"; 413 | CLANG_ENABLE_MODULES = YES; 414 | CLANG_ENABLE_OBJC_ARC = YES; 415 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 416 | CLANG_WARN_BOOL_CONVERSION = YES; 417 | CLANG_WARN_COMMA = YES; 418 | CLANG_WARN_CONSTANT_CONVERSION = YES; 419 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 420 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 421 | CLANG_WARN_EMPTY_BODY = YES; 422 | CLANG_WARN_ENUM_CONVERSION = YES; 423 | CLANG_WARN_INFINITE_RECURSION = YES; 424 | CLANG_WARN_INT_CONVERSION = YES; 425 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 426 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 427 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 428 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 429 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 430 | CLANG_WARN_STRICT_PROTOTYPES = YES; 431 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 432 | CLANG_WARN_UNREACHABLE_CODE = YES; 433 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 434 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 435 | COPY_PHASE_STRIP = NO; 436 | ENABLE_STRICT_OBJC_MSGSEND = YES; 437 | ENABLE_TESTABILITY = YES; 438 | GCC_C_LANGUAGE_STANDARD = gnu99; 439 | GCC_DYNAMIC_NO_PIC = NO; 440 | GCC_NO_COMMON_BLOCKS = YES; 441 | GCC_OPTIMIZATION_LEVEL = 0; 442 | GCC_PREPROCESSOR_DEFINITIONS = ( 443 | "DEBUG=1", 444 | "$(inherited)", 445 | ); 446 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 447 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 448 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 449 | GCC_WARN_UNDECLARED_SELECTOR = YES; 450 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 451 | GCC_WARN_UNUSED_FUNCTION = YES; 452 | GCC_WARN_UNUSED_VARIABLE = YES; 453 | IPHONEOS_DEPLOYMENT_TARGET = 15.1; 454 | LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; 455 | LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\""; 456 | MTL_ENABLE_DEBUG_INFO = YES; 457 | ONLY_ACTIVE_ARCH = YES; 458 | OTHER_LDFLAGS = ( 459 | "$(inherited)", 460 | " ", 461 | ); 462 | REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; 463 | SDKROOT = iphoneos; 464 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; 465 | USE_HERMES = true; 466 | }; 467 | name = Debug; 468 | }; 469 | 83CBBA211A601CBA00E9B192 /* Release */ = { 470 | isa = XCBuildConfiguration; 471 | buildSettings = { 472 | ALWAYS_SEARCH_USER_PATHS = NO; 473 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 474 | CLANG_CXX_LANGUAGE_STANDARD = "c++20"; 475 | CLANG_CXX_LIBRARY = "libc++"; 476 | CLANG_ENABLE_MODULES = YES; 477 | CLANG_ENABLE_OBJC_ARC = YES; 478 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 479 | CLANG_WARN_BOOL_CONVERSION = YES; 480 | CLANG_WARN_COMMA = YES; 481 | CLANG_WARN_CONSTANT_CONVERSION = YES; 482 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 483 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 484 | CLANG_WARN_EMPTY_BODY = YES; 485 | CLANG_WARN_ENUM_CONVERSION = YES; 486 | CLANG_WARN_INFINITE_RECURSION = YES; 487 | CLANG_WARN_INT_CONVERSION = YES; 488 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 489 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 490 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 491 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 492 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 493 | CLANG_WARN_STRICT_PROTOTYPES = YES; 494 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 495 | CLANG_WARN_UNREACHABLE_CODE = YES; 496 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 497 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 498 | COPY_PHASE_STRIP = YES; 499 | ENABLE_NS_ASSERTIONS = NO; 500 | ENABLE_STRICT_OBJC_MSGSEND = YES; 501 | GCC_C_LANGUAGE_STANDARD = gnu99; 502 | GCC_NO_COMMON_BLOCKS = YES; 503 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 504 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 505 | GCC_WARN_UNDECLARED_SELECTOR = YES; 506 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 507 | GCC_WARN_UNUSED_FUNCTION = YES; 508 | GCC_WARN_UNUSED_VARIABLE = YES; 509 | IPHONEOS_DEPLOYMENT_TARGET = 15.1; 510 | LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; 511 | LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\""; 512 | MTL_ENABLE_DEBUG_INFO = NO; 513 | OTHER_LDFLAGS = ( 514 | "$(inherited)", 515 | " ", 516 | ); 517 | REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; 518 | SDKROOT = iphoneos; 519 | USE_HERMES = true; 520 | VALIDATE_PRODUCT = YES; 521 | }; 522 | name = Release; 523 | }; 524 | /* End XCBuildConfiguration section */ 525 | 526 | /* Begin XCConfigurationList section */ 527 | 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "expoappboilerplate" */ = { 528 | isa = XCConfigurationList; 529 | buildConfigurations = ( 530 | 13B07F941A680F5B00A75B9A /* Debug */, 531 | 13B07F951A680F5B00A75B9A /* Release */, 532 | ); 533 | defaultConfigurationIsVisible = 0; 534 | defaultConfigurationName = Release; 535 | }; 536 | 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "expoappboilerplate" */ = { 537 | isa = XCConfigurationList; 538 | buildConfigurations = ( 539 | 83CBBA201A601CBA00E9B192 /* Debug */, 540 | 83CBBA211A601CBA00E9B192 /* Release */, 541 | ); 542 | defaultConfigurationIsVisible = 0; 543 | defaultConfigurationName = Release; 544 | }; 545 | /* End XCConfigurationList section */ 546 | }; 547 | rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; 548 | } 549 | --------------------------------------------------------------------------------