0Pricing
React Native Academy · 课时

完善、测试并发布到两大应用商店

为核心钩子编写单元测试,为关键路径添加 Maestro 端到端流程,优化应用包,生成商店素材,并提交到 App Store 和 Google Play。

完善、测试并发布到两大应用商店 是 CoddyKit 上的免费 React Native Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 React Native Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 React Native Academy 课程共包含 4 节课。

本课时的部分内容尚未翻译,以英文显示。

The Final Stretch: Polish, Test, Ship

The last phase of any app project is the most discipline-intensive: polishing the rough edges users will actually notice, writing tests that catch regressions before they reach production, and executing the submission process for both stores. Each of these feels like it should be quick but takes as long as the feature work — budget time accordingly. A well-polished app with proper test coverage ships with confidence.

UI Polish: Consistent Spacing and Typography

Polish starts with design tokens — a single source of truth for spacing, font sizes, font weights, border radii, and colors. Define these in a theme.ts file and import them everywhere instead of hard-coding padding: 16 scattered across screens. Consistency is what makes an app feel premium — when all buttons have the same height, all headings use the same font, and all cards have the same shadow, the UI feels intentional.

// src/theme.ts
export const theme = {
  colors: {
    primary: '#007AFF',
    background: '#F2F2F7',
    surface: '#FFFFFF',
    text: '#000000',
    textSecondary: '#8E8E93',
    error: '#FF3B30',
    success: '#34C759',
  },
  spacing: {
    xs: 4, sm: 8, md: 16, lg: 24, xl: 32
  },
  typography: {
    largeTitle: { fontSize: 34, fontWeight: '700' },
    title: { fontSize: 22, fontWeight: '600' },
    body: { fontSize: 17, fontWeight: '400' },
    caption: { fontSize: 12, fontWeight: '400' },
  },
  borderRadius: { sm: 8, md: 12, lg: 16, full: 999 },
};

Accessibility Audit

Accessibility is both a legal requirement and a quality marker. Audit your app for: accessible labels on all icon buttons (accessibilityLabel), minimum touch target sizes of 44×44pt on iOS and 48×48dp on Android, correct accessibility roles (accessibilityRole='button' for tappable items), and color contrast (at least 4.5:1 for body text). Use the iOS Accessibility Inspector or Android TalkBack to test screen reader behavior.

// Accessible icon button
<TouchableOpacity
  onPress={handleDelete}
  accessibilityLabel='Delete habit'
  accessibilityRole='button'
  accessibilityHint='Permanently removes this habit and its history'
  style={{ padding: 12 }}  // Ensures 44pt touch target with 20pt icon
>
  <Ionicons name='trash-outline' size={20} color='#FF3B30' />
</TouchableOpacity>

// Screen reader text for images
<Image
  source={{ uri: habit.icon }}
  accessibilityLabel={`${habit.name} habit icon`}
  accessible={true}
/>

Writing Unit Tests for Core Hooks

Unit-test the business logic that is independent of the UI — calculations, hooks with mocked queries, and utility functions. The streak calculation is a perfect candidate: test edge cases like an empty array, a single day, a gap in the middle, and today not completed. Use @testing-library/react-native with renderHook to test custom hooks that use React state and effects.

// src/utils/__tests__/streaks.test.ts
import { calculateStreak } from '../streaks';

describe('calculateStreak', () => {
  it('returns 0 for empty completions', () => {
    expect(calculateStreak([])).toBe(0);
  });

  it('returns 1 when only today is completed', () => {
    const today = new Date().toISOString().split('T')[0];
    expect(calculateStreak([today])).toBe(1);
  });

  it('returns correct streak for consecutive days', () => {
    const dates = ['2026-06-21', '2026-06-20', '2026-06-19'];
    // Mock today as 2026-06-21 in tests
    expect(calculateStreak(dates)).toBe(3);
  });

  it('stops at a gap', () => {
    // Today=21, gap at 20, then 19
    const dates = ['2026-06-21', '2026-06-19'];
    expect(calculateStreak(dates)).toBe(1);
  });
});

Component Tests with React Native Testing Library

Write component tests for the most-used UI elements: the HabitCard rendering habit name and streak, the CheckInButton toggling state on press, and the OfflineBanner appearing only when offline. Mock dependencies (Supabase, React Query) at the test boundary so components are tested in isolation without real network calls.

// src/components/__tests__/HabitCard.test.tsx
import { render, fireEvent } from '@testing-library/react-native';
import { HabitCard } from '../HabitCard';

const mockHabit = {
  id: '1', name: 'Morning Run', icon: 'run',
  color: '#007AFF', streak: 5
};

it('renders habit name and streak', () => {
  const { getByText } = render(<HabitCard habit={mockHabit} onPress={() => {}} />);
  expect(getByText('Morning Run')).toBeTruthy();
  expect(getByText('5 days')).toBeTruthy();
});

it('calls onPress when tapped', () => {
  const onPress = jest.fn();
  const { getByTestId } = render(
    <HabitCard habit={mockHabit} onPress={onPress} testID='habit-card' />
  );
  fireEvent.press(getByTestId('habit-card'));
  expect(onPress).toHaveBeenCalledTimes(1);
});

Maestro E2E: Critical Path Test

Write a Maestro flow for the critical user path: sign in, create a habit, check it in, verify the streak updates, and sign out. This catches integration bugs that unit tests miss — navigation state, network calls, and UI state all working together. Run it against a real simulator or device on every CI build before promoting to the next test track.

# maestro/critical-path.yaml
---
appId: com.yourcompany.habittracker
---
- launchApp:
    clearState: true
- tapOn: 'Sign In'
- inputText: 'test@example.com'
- tapOn: 'Password'
- inputText: 'TestPass123!'
- tapOn: 'Sign In'
- waitForAnimationToEnd
- assertVisible: 'My Habits'
- tapOn: 'Add Habit'
- inputText: 'Morning Run'
- tapOn: 'Save'
- waitForAnimationToEnd
- assertVisible: 'Morning Run'
- tapOn:
    id: 'check-in-btn-1'
- waitForAnimationToEnd
- assertVisible: '1 day'  # Streak shows 1 day
- tapOn: 'Settings'
- tapOn: 'Sign Out'
- assertVisible: 'Sign In'

Bundle Size Optimization

Before submitting, analyze and minimize the JS bundle. Use react-native-bundle-visualizer to identify large dependencies. Common savings: replace moment.js (280KB) with date-fns (tree-shakeable, ~5KB per import), remove unused icon packs (only import icons you use), and apply lazy loading (React.lazy + Suspense) for heavy screens like settings or onboarding that users rarely visit after the first launch.

# Analyze bundle
npx react-native-bundle-visualizer

# Opens a visual treemap in browser showing:
# - node_modules breakdown by size
# - Your source code size
# - Duplicate modules

# Common large packages to replace:
# moment (280KB) -> date-fns tree-shaking
# lodash (full, 70KB) -> lodash-es specific imports
# FontAwesome all icons -> import only used icons

# Result: each 100KB reduction = ~50KB gzip reduction
# = faster first install + lower Play/App Store size

Generating Store Assets

Create the required store assets before submission: app icon (1024×1024 for iOS, adaptive icon for Android), splash screen (all size variants via Expo's splash config), screenshots (at least 3–6 per required device size), and a feature graphic (1024×500 for Android). Use a design tool like Figma with device frame plugins to create professional-looking screenshots without needing a marketing designer.

// app.json — icon and splash configuration
{
  'expo': {
    'icon': './assets/icon.png',        // 1024x1024 PNG
    'splash': {
      'image': './assets/splash.png',
      'resizeMode': 'contain',
      'backgroundColor': '#007AFF'
    },
    'ios': {
      'icon': './assets/icon.png'
    },
    'android': {
      'icon': './assets/icon.png',
      'adaptiveIcon': {
        'foregroundImage': './assets/adaptive-icon.png',
        'backgroundColor': '#007AFF'
      }
    }
  }
}

Final Build for Production

Trigger the production EAS builds for both platforms. Verify the version number and version code are correct before building — changing them after requires a new build. Run both builds in parallel to save time. Once both are complete, upload to TestFlight and Play Console internal testing for one final smoke test on the exact production binary before submitting for review.

# Verify versions before building
cat app.json | grep '"version"'
# -> "version": "1.0.0"
# -> "versionCode": 1
# -> "buildNumber": "1"

# Build both platforms in parallel
eas build --platform ios --profile production &
eas build --platform android --profile production &
wait

# Or use the combined command:
eas build --platform all --profile production

# After builds complete, check status:
eas build:list --status finished --limit 2

Smoke Test the Production Build

Install the exact production binary on a real device before submitting for review. Download the iOS .ipa to TestFlight and the Android .aab to Play Console internal testing. Test the sign-up flow, core feature, push notifications, and purchase flow (if applicable) from scratch — never assume the development version behavior matches the production build. Production builds have different signing, minification, and ProGuard settings that can expose bugs not seen in development.

// Pre-submission smoke test checklist:

// Install production build on physical device
// (not simulator — push notifications require real device)

// Test:
// [ ] Fresh install and sign up flow
// [ ] Sign out and sign in with existing account
// [ ] Core feature: create, complete, view streak
// [ ] Push notification: schedule and receive
// [ ] Background behavior: close and reopen app
// [ ] Deep links from browser/email
// [ ] Offline: airplane mode then restore network
// [ ] Landscape orientation (if not locked)
// [ ] All tabs and major screens load without crash
// [ ] Performance: no jank on FlatList scroll

Submitting to Both Stores

Submit to both stores simultaneously so review timelines align and you can launch on both platforms on the same day. Use eas submit for both. App Store review typically takes 1–3 days; Google Play takes 1–7 days for a first app (faster for updates). Have marketing assets ready (app website, social posts, press kit) so you can announce launch the moment both stores approve. Monitor both stores' review dashboards daily during the review period.

# Submit to App Store (TestFlight first, then App Review)
eas submit --platform ios --latest

# Submit to Play Console (internal -> production)
eas submit --platform android --latest

# Or submit both at once:
eas submit --platform all --latest

# After submission:
# iOS: App Store Connect > App > 1.0 Prepare for Submission
#      > Add for Review > Submit to App Review
# Android: Play Console > Production > Create release
#           > Promote internal build > Review > Start rollout

Quick Check

Test your understanding of React Native Mobile Development concepts from this lesson.

Lesson Recap

In this lesson you learned: how to audit and polish your app with design tokens, accessibility labels, and minimum touch targets, how to write unit tests for streak logic and component tests with RNTL, and how to write a Maestro E2E flow for the critical user path and submit to both the App Store and Play Store. Congratulations — you have completed the React Native Mobile Development track! You now have the skills to build, test, and ship production-quality React Native apps to both platforms.

常见问题解答

「完善、测试并发布到两大应用商店」课时是免费的吗?

是的 — 「完善、测试并发布到两大应用商店」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 React Native Academy 课程的其余内容,请升级到 CoddyKit PRO。 React Native Academy 课程共包含 4 节课。

「完善、测试并发布到两大应用商店」这节课中我会学到什么?

为核心钩子编写单元测试,为关键路径添加 Maestro 端到端流程,优化应用包,生成商店素材,并提交到 App Store 和 Google Play。 你通过在浏览器中直接运行的动手代码来练习 React Native Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 React Native Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 React Native Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「完善、测试并发布到两大应用商店」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 React Native Academy 课中编写并运行代码吗?

能。每节 React Native Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 规划架构与技术栈
  2. 身份验证流程与受保护路由
  3. 核心功能:支持离线的数据流
  4. 完善、测试并发布到两大应用商店
← 返回 React Native Academy