updateService.ts
1 import * as Updates from 'expo-updates'; 2 import { Platform } from 'react-native'; 3 4 export interface UpdateStatus { 5 isChecking: boolean; 6 isUpdateAvailable: boolean; 7 isDownloading: boolean; 8 isReady: boolean; 9 error: string | null; 10 } 11 12 export interface UpdateResult { 13 success: boolean; 14 message: string; 15 } 16 17 export interface UpdateOptions { 18 silent?: boolean; 19 forceReload?: boolean; 20 } 21 22 /** 23 * Checks if an update is available from Expo's update server 24 */ 25 export const checkForUpdate = async (): Promise<UpdateResult> => { 26 try { 27 if (__DEV__) { 28 return { 29 success: false, 30 message: 'Updates are not available in development mode', 31 }; 32 } 33 34 console.log('Checking for updates...'); 35 const update = await Updates.checkForUpdateAsync(); 36 37 if (update.isAvailable) { 38 return { 39 success: true, 40 message: 'Update available', 41 }; 42 } else { 43 return { 44 success: false, 45 message: 'App is up to date', 46 }; 47 } 48 } catch (error) { 49 console.error('Error checking for update:', error); 50 return { 51 success: false, 52 message: `Error checking for updates: ${error instanceof Error ? error.message : 'Unknown error'}`, 53 }; 54 } 55 }; 56 57 /** 58 * Downloads the latest update from Expo's update server 59 */ 60 export const downloadUpdate = async (): Promise<UpdateResult> => { 61 try { 62 if (__DEV__) { 63 return { 64 success: false, 65 message: 'Cannot download updates in development mode', 66 }; 67 } 68 69 console.log('Downloading update...'); 70 await Updates.fetchUpdateAsync(); 71 return { 72 success: true, 73 message: 'Update downloaded successfully', 74 }; 75 } catch (error) { 76 console.error('Error downloading update:', error); 77 return { 78 success: false, 79 message: `Error downloading update: ${error instanceof Error ? error.message : 'Unknown error'}`, 80 }; 81 } 82 }; 83 84 /** 85 * Applies the update and restarts the app 86 */ 87 export const applyUpdate = async (): Promise<UpdateResult> => { 88 try { 89 if (__DEV__ || Platform.OS === 'web') { 90 return { 91 success: false, 92 message: 'Cannot apply updates in development or web mode', 93 }; 94 } 95 96 console.log('Reloading app with update...'); 97 await Updates.reloadAsync(); 98 99 return { 100 success: true, 101 message: 'Update applied successfully', 102 }; 103 } catch (error) { 104 console.error('Error applying update:', error); 105 return { 106 success: false, 107 message: `Error applying update: ${error instanceof Error ? error.message : 'Unknown error'}`, 108 }; 109 } 110 }; 111 112 /** 113 * Performs the complete update flow: check, download, and apply 114 * Can run silently in the background with optional immediate reload 115 */ 116 export const performFullUpdateFlow = async ( 117 options: UpdateOptions = {}, 118 onStatusChange?: (status: UpdateStatus) => void 119 ): Promise<UpdateResult> => { 120 const { silent = false, forceReload = false } = options; 121 122 const updateStatus: UpdateStatus = { 123 isChecking: false, 124 isUpdateAvailable: false, 125 isDownloading: false, 126 isReady: false, 127 error: null, 128 }; 129 130 // Helper to update status 131 const updateState = (updates: Partial<UpdateStatus>) => { 132 Object.assign(updateStatus, updates); 133 if (onStatusChange) { 134 onStatusChange({ ...updateStatus }); 135 } 136 }; 137 138 try { 139 // Check for update 140 updateState({ isChecking: true }); 141 const checkResult = await checkForUpdate(); 142 143 if (!checkResult.success) { 144 updateState({ 145 isChecking: false, 146 error: silent ? null : checkResult.message, 147 }); 148 return checkResult; 149 } 150 151 updateState({ 152 isChecking: false, 153 isUpdateAvailable: true, 154 }); 155 156 // Download update 157 updateState({ isDownloading: true }); 158 const downloadResult = await downloadUpdate(); 159 160 if (!downloadResult.success) { 161 updateState({ 162 isDownloading: false, 163 error: silent ? null : downloadResult.message, 164 }); 165 return downloadResult; 166 } 167 168 updateState({ 169 isDownloading: false, 170 isReady: true, 171 }); 172 173 // Apply update immediately if forceReload is true 174 if (forceReload) { 175 return await applyUpdate(); 176 } 177 178 return { 179 success: true, 180 message: 'Update is ready to be applied', 181 }; 182 } catch (error) { 183 const errorMessage = 184 error instanceof Error ? error.message : 'Unknown error'; 185 updateState({ 186 isChecking: false, 187 isDownloading: false, 188 isReady: false, 189 error: silent ? null : errorMessage, 190 }); 191 192 return { 193 success: false, 194 message: `Update failed: ${errorMessage}`, 195 }; 196 } 197 };