# Mobile Build & Deployment Guide - Maternal Organization App
## Build Environment Setup
### Prerequisites
```bash
# Required tools
node >= 18.0.0
npm >= 9.0.0
react-native-cli >= 2.0.1
expo-cli >= 6.0.0
cocoapods >= 1.12.0 (iOS)
java 11 (Android)
android-studio (Android)
xcode >= 14.0 (iOS)
```
### Project Initialization
```bash
# Create project with Expo
npx create-expo-app maternal-app --template
# Install core dependencies
cd maternal-app
npm install react-native-reanimated react-native-gesture-handler
npm install react-native-safe-area-context react-native-screens
npm install @react-navigation/native @react-navigation/bottom-tabs
```
---
## iOS Configuration
### Bundle Identifier & Provisioning
```xml
CFBundleIdentifier
com.maternalapp.ios
CFBundleDisplayName
Maternal
CFBundleShortVersionString
1.0.0
CFBundleVersion
1
```
### App Capabilities
```xml
aps-environment
production
com.apple.developer.healthkit
com.apple.developer.healthkit.background-delivery
com.apple.security.application-groups
group.com.maternalapp.shared
```
### Permissions
```xml
NSCameraUsageDescription
Take photos of your child for memories and milestone tracking
NSMicrophoneUsageDescription
Enable voice input for hands-free activity logging
NSPhotoLibraryUsageDescription
Select photos for your child's profile and milestones
NSSpeechRecognitionUsageDescription
Convert your voice to text for quick logging
NSHealthShareUsageDescription
Read health data to track your child's growth
NSHealthUpdateUsageDescription
Save growth measurements to Health app
```
### Code Signing Configuration
```ruby
# ios/fastlane/Fastfile
platform :ios do
desc "Build and deploy to TestFlight"
lane :beta do
increment_build_number
match(
type: "appstore",
app_identifier: "com.maternalapp.ios",
git_url: "git@github.com:maternal-app/certificates.git"
)
gym(
scheme: "MaternalApp",
configuration: "Release",
export_method: "app-store",
output_directory: "./build",
output_name: "MaternalApp.ipa"
)
pilot(
ipa: "./build/MaternalApp.ipa",
skip_waiting_for_build_processing: true,
changelog: "Bug fixes and performance improvements"
)
end
end
```
---
## Android Configuration
### Package Name & Versioning
```gradle
// android/app/build.gradle
android {
compileSdkVersion 34
buildToolsVersion "34.0.0"
defaultConfig {
applicationId "com.maternalapp.android"
minSdkVersion 23 // Android 6.0
targetSdkVersion 34
versionCode 1
versionName "1.0.0"
multiDexEnabled true
}
signingConfigs {
release {
storeFile file(MATERNAL_RELEASE_STORE_FILE)
storePassword MATERNAL_RELEASE_STORE_PASSWORD
keyAlias MATERNAL_RELEASE_KEY_ALIAS
keyPassword MATERNAL_RELEASE_KEY_PASSWORD
}
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
```
### Permissions
```xml
```
### ProGuard Rules
```pro
# android/app/proguard-rules.pro
-keep class com.maternalapp.** { *; }
-keep class com.facebook.react.** { *; }
-keep class com.swmansion.** { *; }
# React Native
-keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip
-keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters
# Firebase
-keep class com.google.firebase.** { *; }
-keep class com.google.android.gms.** { *; }
```
---
## Environment-Specific Builds
### Build Configurations
```javascript
// app.config.js
export default ({ config }) => {
const buildType = process.env.BUILD_TYPE || 'development';
const configs = {
development: {
name: 'Maternal (Dev)',
bundleIdentifier: 'com.maternalapp.dev',
apiUrl: 'https://dev-api.maternalapp.com',
icon: './assets/icon-dev.png',
},
staging: {
name: 'Maternal (Staging)',
bundleIdentifier: 'com.maternalapp.staging',
apiUrl: 'https://staging-api.maternalapp.com',
icon: './assets/icon-staging.png',
},
production: {
name: 'Maternal',
bundleIdentifier: 'com.maternalapp.ios',
apiUrl: 'https://api.maternalapp.com',
icon: './assets/icon.png',
},
};
return {
...config,
...configs[buildType],
ios: {
...config.ios,
bundleIdentifier: configs[buildType].bundleIdentifier,
buildNumber: '1',
},
android: {
...config.android,
package: configs[buildType].bundleIdentifier.replace('ios', 'android'),
versionCode: 1,
},
};
};
```
### Environment Variables
```bash
# .env.production
API_URL=https://api.maternalapp.com
SENTRY_DSN=https://prod-sentry.maternalapp.com
ANALYTICS_ENABLED=true
CRASH_REPORTING=true
# .env.staging
API_URL=https://staging-api.maternalapp.com
SENTRY_DSN=https://staging-sentry.maternalapp.com
ANALYTICS_ENABLED=true
CRASH_REPORTING=true
# .env.development
API_URL=http://localhost:3000
SENTRY_DSN=
ANALYTICS_ENABLED=false
CRASH_REPORTING=false
```
---
## CI/CD Pipeline
### GitHub Actions Workflow
```yaml
# .github/workflows/mobile-deploy.yml
name: Mobile Build & Deploy
on:
push:
branches: [main, staging]
tags: ['v*']
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- run: npm ci
- run: npm test
- run: npm run lint
build-ios:
needs: test
runs-on: macos-latest
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: |
npm ci
cd ios && pod install
- name: Setup certificates
env:
CERTIFICATE_BASE64: ${{ secrets.IOS_CERTIFICATE_BASE64 }}
PROVISION_PROFILE_BASE64: ${{ secrets.IOS_PROVISION_PROFILE_BASE64 }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
# Create keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
# Import certificate
echo "$CERTIFICATE_BASE64" | base64 --decode > certificate.p12
security import certificate.p12 -k build.keychain -P "${{ secrets.CERTIFICATE_PASSWORD }}" -T /usr/bin/codesign
# Import provisioning profile
echo "$PROVISION_PROFILE_BASE64" | base64 --decode > profile.mobileprovision
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp profile.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/
- name: Build IPA
run: |
cd ios
xcodebuild -workspace MaternalApp.xcworkspace \
-scheme MaternalApp \
-configuration Release \
-archivePath $PWD/build/MaternalApp.xcarchive \
archive
xcodebuild -exportArchive \
-archivePath $PWD/build/MaternalApp.xcarchive \
-exportPath $PWD/build \
-exportOptionsPlist ExportOptions.plist
- name: Upload to TestFlight
env:
APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
run: |
xcrun altool --upload-app \
--type ios \
--file ios/build/MaternalApp.ipa \
--apiKey "${{ secrets.API_KEY_ID }}" \
--apiIssuer "${{ secrets.API_ISSUER_ID }}"
build-android:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '11'
- name: Install dependencies
run: npm ci
- name: Setup keystore
env:
KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
run: |
echo "$KEYSTORE_BASE64" | base64 --decode > android/app/release.keystore
echo "MATERNAL_RELEASE_STORE_FILE=release.keystore" >> android/gradle.properties
echo "MATERNAL_RELEASE_KEY_ALIAS=${{ secrets.ANDROID_KEY_ALIAS }}" >> android/gradle.properties
echo "MATERNAL_RELEASE_STORE_PASSWORD=${{ secrets.ANDROID_STORE_PASSWORD }}" >> android/gradle.properties
echo "MATERNAL_RELEASE_KEY_PASSWORD=${{ secrets.ANDROID_KEY_PASSWORD }}" >> android/gradle.properties
- name: Build APK
run: |
cd android
./gradlew assembleRelease
- name: Build AAB
run: |
cd android
./gradlew bundleRelease
- name: Upload to Play Store
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
packageName: com.maternalapp.android
releaseFiles: android/app/build/outputs/bundle/release/app-release.aab
track: internal
status: draft
```
---
## TestFlight Configuration
### App Store Connect Setup
```javascript
// ios/fastlane/metadata/en-US/description.txt
Maternal is your AI-powered parenting companion, designed to reduce mental load and bring confidence to your parenting journey.
Key Features:
• Smart activity tracking with voice input
• AI assistant available 24/7 for parenting questions
• Real-time family synchronization
• Sleep predictions based on your baby's patterns
• Growth tracking with WHO percentiles
// ios/fastlane/metadata/en-US/keywords.txt
parenting,baby tracker,sleep tracking,feeding log,AI assistant,family app,childcare
```
### Beta Testing Groups
```yaml
# TestFlight Groups
internal_testing:
name: "Internal Team"
members: 10
builds: all
beta_families:
name: "Beta Families"
members: 50
builds: stable
feedback: enabled
early_access:
name: "Early Access"
members: 500
builds: release_candidate
```
---
## Google Play Console Configuration
### Store Listing
```yaml
# Play Console Setup
app_details:
title: "Maternal - AI Parenting Assistant"
short_description: "Smart parenting companion with AI support"
full_description: |
Complete description...
category: "Parenting"
content_rating: "Everyone"
graphics:
icon: 512x512px
feature_graphic: 1024x500px
screenshots:
phone: [6 images minimum]
tablet: [optional]
```
### Release Tracks
```yaml
internal_testing:
testers: "internal-testers@maternalapp.com"
release_frequency: "daily"
closed_testing:
testers: 100
release_frequency: "weekly"
open_testing:
countries: ["US", "CA", "GB", "AU"]
release_frequency: "bi-weekly"
production:
rollout_percentage: 10 # Start with 10%
staged_rollout: true
```
---
## Over-the-Air Updates
### CodePush Setup
```bash
# Install CodePush
npm install react-native-code-push
# iOS setup
cd ios && pod install
# Register app with CodePush
code-push app add Maternal-iOS ios react-native
code-push app add Maternal-Android android react-native
```
### Update Configuration
```javascript
// App.js
import CodePush from 'react-native-code-push';
const codePushOptions = {
checkFrequency: CodePush.CheckFrequency.ON_APP_RESUME,
installMode: CodePush.InstallMode.ON_NEXT_RESTART,
mandatoryInstallMode: CodePush.InstallMode.IMMEDIATE,
updateDialog: {
title: 'Update Available',
mandatoryUpdateMessage: 'An important update is available.',
optionalUpdateMessage: 'An update is available. Would you like to install it?',
},
};
export default CodePush(codePushOptions)(App);
```
### Deployment Commands
```bash
# Deploy update to staging
code-push release-react Maternal-iOS ios -d Staging
code-push release-react Maternal-Android android -d Staging
# Promote to production
code-push promote Maternal-iOS Staging Production -r 10%
code-push promote Maternal-Android Staging Production -r 10%
```
---
## Performance Monitoring
### Build Size Optimization
```javascript
// metro.config.js
module.exports = {
transformer: {
minifierConfig: {
keep_fnames: true,
mangle: {
keep_fnames: true,
},
},
},
};
```
### Bundle Analysis
```bash
# Analyze bundle size
npx react-native-bundle-visualizer
# iOS specific
npx react-native bundle --platform ios --dev false --entry-file index.js --bundle-output ios/main.jsbundle --assets-dest ios
# Android specific
cd android && ./gradlew bundleRelease --scan
```
---
## Pre-Launch Checklist
### iOS Submission
- [ ] TestFlight build approved
- [ ] App Store screenshots (6.5", 5.5")
- [ ] App preview video (optional)
- [ ] Privacy policy URL
- [ ] Support URL
- [ ] Marketing URL
- [ ] Age rating questionnaire
- [ ] Export compliance
- [ ] App Review notes
### Android Submission
- [ ] Signed AAB uploaded
- [ ] Store listing complete
- [ ] Content rating questionnaire
- [ ] Target audience declaration
- [ ] Data safety form
- [ ] Privacy policy URL
- [ ] App category selected
- [ ] Closed testing feedback addressed
### General Requirements
- [ ] COPPA compliance verified
- [ ] GDPR compliance documented
- [ ] Terms of service updated
- [ ] Support system ready
- [ ] Analytics tracking verified
- [ ] Crash reporting active
- [ ] Performance benchmarks met
- [ ] Accessibility tested