Phase 1 & 2: Authentication and Children Management
Completed Features:
- Full JWT authentication system with refresh tokens
- User registration and login with device fingerprinting
- Child profile CRUD operations with permission-based access
- Family management with roles and permissions
- Database migrations for core auth and family structure
- Comprehensive test coverage (37 unit + E2E tests)
Tech Stack:
- NestJS backend with TypeORM
- PostgreSQL database
- JWT authentication with Passport
- bcrypt password hashing
- Docker Compose for infrastructure
🤖 Generated with Claude Code
This commit is contained in:
590
docs/maternal-app-mobile-deployment.md
Normal file
590
docs/maternal-app-mobile-deployment.md
Normal file
@@ -0,0 +1,590 @@
|
||||
# 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
|
||||
<!-- ios/MaternalApp/Info.plist -->
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.maternalapp.ios</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Maternal</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
```
|
||||
|
||||
### App Capabilities
|
||||
```xml
|
||||
<!-- ios/MaternalApp/MaternalApp.entitlements -->
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>aps-environment</key>
|
||||
<string>production</string>
|
||||
<key>com.apple.developer.healthkit</key>
|
||||
<true/>
|
||||
<key>com.apple.developer.healthkit.background-delivery</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.com.maternalapp.shared</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
### Permissions
|
||||
```xml
|
||||
<!-- ios/MaternalApp/Info.plist -->
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Take photos of your child for memories and milestone tracking</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Enable voice input for hands-free activity logging</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Select photos for your child's profile and milestones</string>
|
||||
<key>NSSpeechRecognitionUsageDescription</key>
|
||||
<string>Convert your voice to text for quick logging</string>
|
||||
<key>NSHealthShareUsageDescription</key>
|
||||
<string>Read health data to track your child's growth</string>
|
||||
<key>NSHealthUpdateUsageDescription</key>
|
||||
<string>Save growth measurements to Health app</string>
|
||||
```
|
||||
|
||||
### 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
|
||||
<!-- android/app/src/main/AndroidManifest.xml -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
<!-- Google Play Services -->
|
||||
<uses-permission android:name="com.google.android.gms.permission.AD_ID" tools:node="remove" />
|
||||
```
|
||||
|
||||
### 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
|
||||
Reference in New Issue
Block a user