Skip to main content

Custom Update UI

Build your own update experience using SwiftPatch hooks. Below are four ready-to-use patterns -- pick the one that fits your app.

Pattern 1: Update Banner

A subtle banner at the top of the screen. Great for optional updates.

import React from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import { useSwiftPatch, UpdateStatus } from '@swiftpatch/react-native';

function UpdateBanner() {
const { status, availableUpdate, downloadProgress, downloadUpdate, restart } =
useSwiftPatch();

if (status === UpdateStatus.UP_TO_DATE || status === UpdateStatus.CHECKING) {
return null;
}

return (
<View style={styles.banner}>
{status === UpdateStatus.UPDATE_AVAILABLE && (
<TouchableOpacity onPress={downloadUpdate} style={styles.row}>
<Text style={styles.text}>
Version {availableUpdate?.version} available
</Text>
<Text style={styles.action}>Update</Text>
</TouchableOpacity>
)}

{status === UpdateStatus.DOWNLOADING && (
<View style={styles.row}>
<Text style={styles.text}>
Downloading... {downloadProgress?.percentage}%
</Text>
<View style={[styles.progressBar, { width: `${downloadProgress?.percentage}%` }]} />
</View>
)}

{status === UpdateStatus.RESTART_REQUIRED && (
<TouchableOpacity onPress={restart} style={styles.row}>
<Text style={styles.text}>Update ready</Text>
<Text style={styles.action}>Restart</Text>
</TouchableOpacity>
)}
</View>
);
}

const styles = StyleSheet.create({
banner: { backgroundColor: '#6366f1', paddingHorizontal: 16, paddingVertical: 12 },
row: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' },
text: { color: 'white', fontSize: 14 },
action: { color: 'white', fontWeight: 'bold', fontSize: 14 },
progressBar: { position: 'absolute', bottom: 0, left: 0, height: 2, backgroundColor: 'white' },
});

Pattern 2: Full-Screen Modal

A more prominent prompt. Good when you want to encourage users to update.

import React from 'react';
import { Modal, View, Text, Button, ActivityIndicator, StyleSheet } from 'react-native';
import { useSwiftPatch, UpdateStatus } from '@swiftpatch/react-native';

function UpdateModal() {
const { status, availableUpdate, downloadProgress, downloadUpdate, restart } = useSwiftPatch();

const showModal =
status === UpdateStatus.UPDATE_AVAILABLE ||
status === UpdateStatus.DOWNLOADING ||
status === UpdateStatus.RESTART_REQUIRED;

if (!showModal) return null;

return (
<Modal visible animationType="slide" transparent>
<View style={styles.overlay}>
<View style={styles.modal}>
<Text style={styles.title}>Update Available</Text>
<Text style={styles.version}>Version {availableUpdate?.version}</Text>
{availableUpdate?.releaseNote && (
<Text style={styles.description}>{availableUpdate.releaseNote}</Text>
)}

{status === UpdateStatus.UPDATE_AVAILABLE && (
<Button title="Download Update" onPress={downloadUpdate} />
)}
{status === UpdateStatus.DOWNLOADING && (
<View style={styles.progress}>
<ActivityIndicator size="large" color="#6366f1" />
<Text style={styles.progressText}>{downloadProgress?.percentage}%</Text>
</View>
)}
{status === UpdateStatus.RESTART_REQUIRED && (
<Button title="Restart Now" onPress={restart} />
)}
</View>
</View>
</Modal>
);
}

const styles = StyleSheet.create({
overlay: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: 'rgba(0,0,0,0.5)' },
modal: { backgroundColor: 'white', borderRadius: 16, padding: 24, width: '80%', alignItems: 'center' },
title: { fontSize: 20, fontWeight: 'bold', marginBottom: 8 },
version: { fontSize: 16, color: '#6366f1', marginBottom: 12 },
description: { fontSize: 14, color: '#666', textAlign: 'center', marginBottom: 20 },
progress: { alignItems: 'center', gap: 12 },
progressText: { fontSize: 18, fontWeight: 'bold' },
});

Pattern 3: Silent Background Updates

No UI at all. The update downloads and installs silently, then applies on the next restart.

import { useEffect } from 'react';
import { useSwiftPatch, UpdateStatus } from '@swiftpatch/react-native';

function SilentUpdater() {
const { status, downloadUpdate, installUpdate } = useSwiftPatch();

useEffect(() => {
if (status === UpdateStatus.UPDATE_AVAILABLE) {
downloadUpdate()
.then(() => installUpdate())
.catch(console.error);
}
}, [status]);

return null;
}

The update applies on next restart based on installMode.

tip

This is the simplest approach. Users never see an update prompt -- changes just appear next time they open the app.

Pattern 4: Mandatory Update Gate

Block the entire app until a mandatory update is applied:

import { useSwiftPatch, UpdateStatus } from '@swiftpatch/react-native';

function MandatoryUpdateGate({ children }) {
const { availableUpdate, isRestartRequired, restart } = useSwiftPatch();

if (availableUpdate?.isMandatory && isRestartRequired) {
return (
<View style={styles.fullScreen}>
<Text style={styles.title}>Update Required</Text>
<Text>A required update has been downloaded.</Text>
<Button title="Restart Now" onPress={restart} />
</View>
);
}

return children;
}
warning

Only use this pattern for mandatory updates. Blocking optional updates frustrates users.