Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Cross-Platform Auto-Update Implementation for Electron Applications

Tech May 16 1

Auto-Update Implementation

The following code demonstrates how to implement automatic updates for Electron applications that work seamlessly on both Windows and macOS platforms.

Main Process Update Handler

import { ipcMain } from 'electron'
import log from './logger'
import { autoUpdater } from 'electron-updater'
const fs = require('fs-extra')
const path = require('path')

// Function to handle application updates
export function updateHandler({ mainWindow }) {
  // Clear previous update download files to ensure proper updating
  // The updaterCacheDirName value should match the one in app-update.yml
  const updaterCacheDirName = 'workbench-updater'
  const updatePendingPath = path.join(autoUpdater.app.baseCachePath, updaterCacheDirName, 'pending')
  fs.emptyDir(updatePendingPath)
  
  const updateMessages = {
    error: 'Update check failed',
    checking: 'Checking for updates...',
    updateAvailable: 'New version detected. Downloading...',
    updateNotAvailable: 'You are using the latest version'
  }
  
  // Configure update server URL
  autoUpdater.setFeedURL({
    provider: 'generic',
    url: 'your-update-server-url'
  })
  
  // Configure update download settings
  autoUpdater.disableWebInstaller = false
  autoUpdater.autoDownload = false
  autoUpdater.logger = log
  
  // Event handlers for update process
  autoUpdater.on('error', () => {
    sendUpdateMessage(updateMessages.error, mainWindow)
    console.log(updateMessages.error)
  })
  
  autoUpdater.on('checking-for-update', () => {
    if (process.env.NODE_ENV !== 'development') {
      sendUpdateMessage(updateMessages.checking, mainWindow)
      console.log(updateMessages.checking)
    }
  })
  
  autoUpdater.on('update-available', () => {
    sendUpdateMessage(updateMessages.updateAvailable, mainWindow)
    console.log(updateMessages.updateAvailable)
  })
  
  autoUpdater.on('update-not-available', () => {
    sendUpdateMessage(updateMessages.updateNotAvailable, mainWindow)
    console.log(updateMessages.updateNotAvailable)
  })
  
  autoUpdater.on('download-progress', (progressObj) => {
    mainWindow.webContents.send('downloadProgress', progressObj)
  })
  
  autoUpdater.on('update-downloaded', () => {
    log.warn('Starting update process')
    autoUpdater.quitAndInstall()
  })
  
  ipcMain.on('checkForUpdate', () => {
    log.warn('Checking for updates, window destroyed:', mainWindow.isDestroyed())
    if (mainWindow && !mainWindow.isDestroyed()) {
      autoUpdater.checkForUpdates()
    }
  })
  
  ipcMain.on('downloadUpdate', () => {
    log.warn('Downloading update')
    autoUpdater.downloadUpdate()
  })
}

// Helper function to send messages to renderer process
function sendUpdateMessage(text, mainWindow) {
  mainWindow.webContents.send('message', text)
}

Logging Configuration

const log = require('electron-log')
log.transports.file.fileName = 'main.log';
log.transports.file.level = 'false';
// When reaching max size, backup file as main.old.log (only one backup)
log.transports.file.maxSize = 1048576;
log.transports.console.level = 'silly';

export default log

Renderer Process Update UI

<template>
  <el-dialog
    v-model="isProgressVisible"
    :show-close="false" 
    :close-on-click-modal="false"
    :close-on-press-escape="false"
    fullscreen
    title="Downloading new version, please wait..."
  >
    <div style="text-align:center">
      <el-progress type="circle" :percentage="percentage"></el-progress>
    </div>
  </el-dialog>
</template>

<script>
import { ipcRenderer } from 'electron'
import { ref, nextTick } from 'vue'

export default {
  setup() {
    // Update progress visibility
    const isProgressVisible = ref(false)
    // Download progress percentage
    const percentage = ref(0)
    
    // Function to check for updates
    const checkForUpdates = () => {
      // Trigger update check
      ipcRenderer.send('checkForUpdate')
      
      // Listen for update events
      ipcRenderer.on('message', (event, text) => {
        if (text === 'New version detected. Downloading...') {
          nextTick(() => {
            isProgressVisible.value = true
            ipcRenderer.send('downloadUpdate')
            
            // Listen for download progress
            ipcRenderer.on('downloadProgress', (event, progressObj) => {
              percentage.value = parseInt(progressObj.percent || 0)
              console.log('Download progress:', percentage.value)
            })
          })
        }
      })
    }
    
    return {
      isProgressVisible,
      percentage,
      checkForUpdates
    }
  }
}
</script>

Required Update Files

For Windows, the update process requires the installer package and the yml configuration file.

For macOS, in addition to the installer package and yml file, you also need a zip archive of the installer, making a total of three required files.

These files are automatically generated during the packaging process.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.