0Pricing
React Native Academy · Lezione

Rifinitura, test e pubblicazione sui due store

Scriva test unitari per gli hook principali, aggiunga un flusso E2E in Maestro per il percorso critico, ottimizzi il bundle, generi le risorse per gli store e invii l’app sia all’App Store sia a Google Play.

Rifinitura, test e pubblicazione sui due store è una lezione React Native Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento React Native Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso React Native Academy include 4 lezioni in totale.

Parti di questa lezione non sono ancora state tradotte e vengono mostrate in inglese.

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.

Domande Frequenti

La lezione «Rifinitura, test e pubblicazione sui due store» è gratuita?

Sì — il testo completo di «Rifinitura, test e pubblicazione sui due store» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso React Native Academy, passa a CoddyKit PRO. Il corso React Native Academy include 4 lezioni in totale.

Cosa imparerò in «Rifinitura, test e pubblicazione sui due store»?

Scriva test unitari per gli hook principali, aggiunga un flusso E2E in Maestro per il percorso critico, ottimizzi il bundle, generi le risorse per gli store e invii l’app sia all’App Store sia a Goog… Eserciti React Native Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare React Native Academy?

Non è richiesta alcuna esperienza precedente. React Native Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.

Quanto tempo richiede la lezione «Rifinitura, test e pubblicazione sui due store»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione React Native Academy?

Sì. Ogni lezione React Native Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Pianificare l'architettura e lo stack tecnologico
  2. Flusso di autenticazione e route protette
  3. Funzionalità principale: feed di dati con supporto offline
  4. Rifinitura, test e pubblicazione sui due store
← Torna a React Native Academy